diff -u --recursive --new-file v2.2.3/linux/CREDITS linux/CREDITS
--- v2.2.3/linux/CREDITS	Wed Mar 10 15:29:44 1999
+++ linux/CREDITS	Wed Mar 10 21:49:10 1999
@@ -779,6 +779,15 @@
 S: Mountain View, California 94043
 S: USA
 
+N: Benjamin Herrenschmidt
+E: bh40@calva.net
+E: benh@mipsys.com
+D: PowerMac booter (BootX)
+D: Additional PowerBook support
+S: 22, rue des Marguettes
+S: 75012 Paris
+S: France
+
 N: Sebastian Hetze
 E: she@lunetix.de
 D: German Linux Documentation,
diff -u --recursive --new-file v2.2.3/linux/Documentation/Changes linux/Documentation/Changes
--- v2.2.3/linux/Documentation/Changes	Wed Jan 20 23:14:03 1999
+++ linux/Documentation/Changes	Wed Mar 17 09:15:05 1999
@@ -579,7 +579,7 @@
 ==========
 
 The 2.9 release:
-ftp://ftp.win.tue.nl/pub/linux/util/util-linux-2.9g.tar.gz
+ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9g.tar.gz
 
 Autofs
 ======
diff -u --recursive --new-file v2.2.3/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.2.3/linux/Documentation/Configure.help	Wed Mar 10 15:29:44 1999
+++ linux/Documentation/Configure.help	Sun Mar 21 07:22:00 1999
@@ -1399,6 +1399,10 @@
   PC boards and vice versa.
   See Documentation/sgi-visws.txt for more.
 
+SGI Visual Workstation framebuffer support
+CONFIG_FB_SGIVW
+  SGI Visual Workstation support for framebuffer graphics.
+
 System V IPC
 CONFIG_SYSVIPC
   Inter Process Communication is a suite of library functions and
@@ -2247,6 +2251,12 @@
   you say Y here, you will be able to specify different routes for
   packets with different TOS values.
 
+IP: use FWMARK value as routing key
+CONFIG_IP_ROUTE_FWMARK
+  If you say Y here, you will be able to specify different routes for
+  packets with different FWMARK ("firewalling mark") values
+  (see ipchains(8), "-m" argument).
+
 IP: verbose route monitoring
 CONFIG_IP_ROUTE_VERBOSE
   If you say Y here, which is recommended, then the kernel will print
@@ -5099,7 +5109,7 @@
     *** networking options: especially CONFIG*FIREWALL.      ***
 
   However, it will work with all options in CONFIG_IP_ADVANCED_ROUTER
-  section (except for CONFIG_IP_ROUTE_TOS). At the moment, few devices
+  section (except for CONFIG_IP_ROUTE_TOS&FWMARK). At the moment, few devices
   support fast switching (tulip is one of them, modified 8390 can be
   found at ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). 
 
diff -u --recursive --new-file v2.2.3/linux/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex
--- v2.2.3/linux/Documentation/cdrom/cdrom-standard.tex	Tue Jan 19 11:32:50 1999
+++ linux/Documentation/cdrom/cdrom-standard.tex	Sun Mar 21 18:37:56 1999
@@ -25,7 +25,7 @@
 \author{David van Leeuwen\\{\normalsize\tt david@ElseWare.cistron.nl}
 \\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}
 \\{\footnotesize updated by Jens Axboe {\tt(axboe@image.dk)}}}
-\date{11 January 1999}
+\date{12 March 1999}
 
 \maketitle
 
@@ -549,7 +549,9 @@
   CDROMREADAUDIO, CDROMREADRAW, CDROMREADCOOKED, CDROMSEEK,
   CDROMPLAY\-BLK and CDROM\-READALL}.
 
+
 \subsection{\cdrom\ capabilities}
+\label{capability}
 
 Instead of just implementing some $ioctl$ calls, the interface in
 \cdromc\ supplies the possibility to indicate the {\em capabilities\/}
@@ -944,6 +946,13 @@
 \item[CDROM_CHANGER_NSLOTS] Returns the number of slots in a
   juke-box. 
 \item[CDROMRESET] Reset the drive. 
+\item[CDROM_GET_CAPABILITY] Returns the $capability$ flags for the
+  drive. Refer to section \ref{capability} for more information on
+  these flags.
+\item[CDROM_LOCKDOOR] Locks the door of the drive. $arg == \rm0$
+  unlocks the door, any other value locks it.
+\item[CDROM_DEBUG] Turns on debugging info. Only root is allowed
+  to do this. Same semantics as CDROM_LOCKDOOR.
 \end{description}
 
 \subsubsection{Device dependent $ioctl$s}
diff -u --recursive --new-file v2.2.3/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt
--- v2.2.3/linux/Documentation/filesystems/affs.txt	Wed Jun 24 22:54:01 1998
+++ linux/Documentation/filesystems/affs.txt	Sun Mar 21 07:11:36 1999
@@ -151,6 +151,28 @@
 /etc/fstab entry:
     /dev/sdb5	/amiga/Workbench    affs    noauto,user,exec,verbose 0 0
 
+IMPORTANT NOTE
+==============
+
+If you boot Windows 95 (don't know about 3.x, 98 and NT) while you
+have an Amiga harddisk connected to your PC, it will overwrite
+the bytes 0x00dc..0x00df of block 0 with garbage, thus invalidating
+the Rigid Disk Block. Sheer luck has it that this is an unused
+area of the RDB, so only the checksum doesn's match anymore.
+Linux will ignore this garbage and recognize the RDB anyway, but
+before you connect that drive to your Amiga again, you must
+restore or repair your RDB. So please do make a backup copy of it
+before booting Windows!
+
+If the damage is already done, the following should fix the RDB
+(where <disk> is the device name).
+DO AT YOUR OWN RISK:
+
+  dd if=/dev/<disk> of=rdb.tmp count=1
+  cp rdb.tmp rdb.fixed
+  dd if=/dev/zero of=rdb.fixed bs=1 seek=220 count=4
+  dd if=rdb.fixed of=/dev/<disk>
+
 Bugs, Restrictions, Caveats
 ===========================
 
@@ -185,9 +207,8 @@
 no way to fix a garbled filesystem without an Amiga (disk validator)
 or manually (who would do this?). Maybe later.
 
-A fsck.affs and mkfs.affs will probably be available in the future.
-If you mount them on system startup, you may want to tell fsck
-that the fs should not be checked (place a '0' in the sixth field
+If you mount affs partitions on system startup, you may want to tell
+fsck that the fs should not be checked (place a '0' in the sixth field
 of /etc/fstab).
 
 It's not possible to read floppy disks with a normal PC or workstation
diff -u --recursive --new-file v2.2.3/linux/Documentation/powerpc/ppc_htab.txt linux/Documentation/powerpc/ppc_htab.txt
--- v2.2.3/linux/Documentation/powerpc/ppc_htab.txt	Thu Apr 23 20:21:27 1998
+++ linux/Documentation/powerpc/ppc_htab.txt	Wed Mar 10 21:49:10 1999
@@ -114,10 +114,5 @@
 
 3. Bugs
 
-  Doing a 'less' or 'more' on ppc_htab results in a segmentation violation.
-  I'm not sure of the cause but in the mean time 'cat' works adequately for
-  reading the file.
-
   The PMC1 and PMC2 counters can overflow and give no indication of that
   in /proc/ppc_htab.
-
diff -u --recursive --new-file v2.2.3/linux/MAINTAINERS linux/MAINTAINERS
--- v2.2.3/linux/MAINTAINERS	Wed Mar 10 15:29:44 1999
+++ linux/MAINTAINERS	Wed Mar 10 18:11:11 1999
@@ -685,7 +685,7 @@
 
 SPARC:
 P:	David S. Miller
-M:	davem@dm.cobaltmicro.com
+M:	davem@redhat.com
 P:	Eddie C. Dost
 M:	ecd@skynet.be
 P:	Jakub Jelinek
diff -u --recursive --new-file v2.2.3/linux/Makefile linux/Makefile
--- v2.2.3/linux/Makefile	Wed Mar 10 15:29:44 1999
+++ linux/Makefile	Fri Mar 12 16:22:27 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 3
+SUBLEVEL = 4
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
diff -u --recursive --new-file v2.2.3/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
--- v2.2.3/linux/arch/alpha/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/alpha/kernel/time.c	Thu Mar 11 23:24:50 1999
@@ -357,7 +357,6 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	sti();
diff -u --recursive --new-file v2.2.3/linux/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c
--- v2.2.3/linux/arch/arm/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/arm/kernel/time.c	Thu Mar 11 23:24:55 1999
@@ -127,7 +127,6 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	sti ();
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v2.2.3/linux/arch/i386/kernel/bios32.c	Wed Mar 10 15:29:45 1999
+++ linux/arch/i386/kernel/bios32.c	Sun Mar 21 07:24:54 1999
@@ -997,15 +997,15 @@
 			    l != 0x0000 && l != 0xffff) {
 #ifdef CONFIG_PCI_BIOS
 				if (pci_bios_present) {
-					int succ, idx = 0;
+					int err, idx = 0;
 					u8 bios_bus, bios_dfn;
 					u16 d;
 					pcibios_read_config_word(n, i, PCI_DEVICE_ID, &d);
 					DBG("BIOS test for %02x:%02x (%04x:%04x)\n", n, i, l, d);
-					while ((succ = pci_bios_find_device(l, d, idx, &bios_bus, &bios_dfn)) &&
+					while (!(err = pci_bios_find_device(l, d, idx, &bios_bus, &bios_dfn)) &&
 					       (bios_bus != n || bios_dfn != i))
 						idx++;
-					if (!succ)
+					if (err)
 						break;
 				}
 #endif
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.2.3/linux/arch/i386/kernel/i386_ksyms.c	Tue Feb 23 15:21:32 1999
+++ linux/arch/i386/kernel/i386_ksyms.c	Wed Mar 10 15:07:19 1999
@@ -75,8 +75,11 @@
 EXPORT_SYMBOL(cpu_data);
 EXPORT_SYMBOL(kernel_flag);
 EXPORT_SYMBOL(smp_invalidate_needed);
+EXPORT_SYMBOL(cpu_number_map);
 EXPORT_SYMBOL(__cpu_logical_map);
 EXPORT_SYMBOL(smp_num_cpus);
+EXPORT_SYMBOL(cpu_present_map);
+EXPORT_SYMBOL(cpu_online_map);
 
 /* Global SMP irq stuff */
 EXPORT_SYMBOL(synchronize_irq);
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.2.3/linux/arch/i386/kernel/process.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/i386/kernel/process.c	Wed Mar 10 16:52:44 1999
@@ -316,7 +316,7 @@
 	/* Make sure the first page is mapped to the start of physical memory.
 	   It is normally not mapped, to trap kernel NULL pointer dereferences. */
 
-	pg0[0] = 7;
+	pg0[0] = _PAGE_RW | _PAGE_PRESENT;
 
 	/*
 	 * Use `swapper_pg_dir' as our page directory.  We bother with
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.2.3/linux/arch/i386/kernel/ptrace.c	Wed Jan 13 15:00:41 1999
+++ linux/arch/i386/kernel/ptrace.c	Wed Mar 10 16:51:34 1999
@@ -354,6 +354,7 @@
 {
 	struct task_struct *child;
 	struct user * dummy = NULL;
+	unsigned long flags;
 	int i, ret;
 
 	lock_kernel();
@@ -385,21 +386,22 @@
 		    (current->uid != child->uid) ||
 	 	    (current->gid != child->egid) ||
 	 	    (current->gid != child->sgid) ||
+	 	    (cap_issubset(child->cap_permitted, current->cap_permitted)) ||
 	 	    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
 			goto out;
 		/* the same process cannot be attached many times */
 		if (child->flags & PF_PTRACED)
 			goto out;
 		child->flags |= PF_PTRACED;
-		if (child->p_pptr != current) {
-			unsigned long flags;
 
-			write_lock_irqsave(&tasklist_lock, flags);
+		write_lock_irqsave(&tasklist_lock, flags);
+		if (child->p_pptr != current) {
 			REMOVE_LINKS(child);
 			child->p_pptr = current;
 			SET_LINKS(child);
-			write_unlock_irqrestore(&tasklist_lock, flags);
 		}
+		write_unlock_irqrestore(&tasklist_lock, flags);
+
 		send_sig(SIGSTOP, child, 1);
 		ret = 0;
 		goto out;
@@ -559,7 +561,6 @@
 		}
 
 		case PTRACE_DETACH: { /* detach a process that was attached. */
-			unsigned long flags;
 			long tmp;
 
 			ret = -EIO;
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.2.3/linux/arch/i386/kernel/setup.c	Tue Feb 23 15:21:32 1999
+++ linux/arch/i386/kernel/setup.c	Wed Mar 10 16:51:34 1999
@@ -381,7 +381,17 @@
 
 }
 
-__initfunc(static int amd_model(struct cpuinfo_x86 *c))
+#define rdmsr(msr,val1,val2) \
+       __asm__ __volatile__("rdmsr" \
+			    : "=a" (val1), "=d" (val2) \
+			    : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+     __asm__ __volatile__("wrmsr" \
+			  : /* no outputs */ \
+			  : "c" (msr), "a" (val1), "d" (val2))
+
+__initfunc(static int get_model_name(struct cpuinfo_x86 *c))
 {
 	unsigned int n, dummy, *v;
 
@@ -401,6 +411,77 @@
 	return 1;
 }
 
+__initfunc(static int amd_model(struct cpuinfo_x86 *c))
+{
+	u32 l, h;
+	unsigned long flags;
+	int mbytes = max_mapnr >> (20-PAGE_SHIFT);
+	
+	int r=get_model_name(c);
+	
+	/*
+	 *	Now do the cache operations. 
+	 */
+	 
+	switch(c->x86)
+	{
+		case 5:
+			if( c->x86_model < 6 )
+			{
+				/* Anyone with a K5 want to fill this in */				
+				break;
+			}
+			
+			/* K6 with old style WHCR */
+			if( c->x86_model < 8 ||
+				(c->x86_model== 8 && c->x86_mask < 8))
+			{
+				/* We can only write allocate on the low 508Mb */
+				if(mbytes>508)
+					mbytes=508;
+					
+				rdmsr(0xC0000082, l, h);
+				if((l&0x0000FFFF)==0)
+				{		
+					l=(1<<0)|(mbytes/4);
+					save_flags(flags);
+					__cli();
+					__asm__ __volatile__ ("wbinvd": : :"memory");
+					wrmsr(0xC0000082, l, h);
+					restore_flags(flags);
+					printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n",
+						mbytes);
+					
+				}
+				break;
+			}
+			if (c->x86_model == 8 || c->x86_model == 9)
+			{
+				/* The more serious chips .. */
+				
+				if(mbytes>4092)
+					mbytes=4092;
+				rdmsr(0xC0000082, l, h);
+				if((l&0xFFFF0000)==0)
+				{
+					mbytes>>=2;
+					l=(mbytes<<22)|(1<<16);
+					save_flags(flags);
+					__cli();
+					__asm__ __volatile__ ("wbinvd": : :"memory");
+					wrmsr(0xC0000082, l, h);
+					restore_flags(flags);
+					printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n",
+						mbytes);
+				}
+				break;
+			}
+			break;
+	}
+	return r;
+}
+			
+
 /*
  * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU
  */
@@ -517,7 +598,7 @@
 		
 		/* GXm supports extended cpuid levels 'ala' AMD */
 		if (c->cpuid_level == 2) {
-			amd_model(c);  /* get CPU marketing name */
+			get_model_name(c);  /* get CPU marketing name */
 			c->x86_capability&=~X86_FEATURE_TSC;
 			return;
 		}
@@ -642,6 +723,20 @@
 
 	if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c))
 		return;
+		
+	if (c->cpuid_level > 0 && c->x86_vendor == X86_VENDOR_INTEL)
+	{
+		if(c->x86_capability&(1<<18))
+		{
+			/* Disable processor serial number on Intel Pentium III 
+			   from code by Phil Karn */
+			unsigned long lo,hi;
+			rdmsr(0x119,lo,hi);
+			lo |= 0x200000;
+			wrmsr(0x119,lo,hi);
+			printk(KERN_INFO "Pentium-III serial number disabled.\n");
+		}
+	}
 
 	for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
 		if (c->cpuid_level > 1) {
@@ -726,15 +821,6 @@
 }
 	
 	
-#define rdmsr(msr,val1,val2) \
-       __asm__ __volatile__("rdmsr" \
-			    : "=a" (val1), "=d" (val2) \
-			    : "c" (msr))
-
-#define wrmsr(msr,val1,val2) \
-     __asm__ __volatile__("wrmsr" \
-			  : /* no outputs */ \
-			  : "c" (msr), "a" (val1), "d" (val2))
 
 static char *cpu_vendor_names[] __initdata = {
 	"Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" };
@@ -785,8 +871,8 @@
 	static char *x86_cap_flags[] = {
 	        "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce",
 	        "cx8", "9", "10", "sep", "12", "pge", "14", "cmov",
-	        "16", "17", "18", "19", "20", "21", "22", "mmx",
-	        "24", "25", "26", "27", "28", "29", "30", "31"
+	        "16", "17", "psn", "19", "20", "21", "22", "mmx",
+	        "24", "kni", "26", "27", "28", "29", "30", "31"
 	};
 	struct cpuinfo_x86 *c = cpu_data;
 	int i, n;
@@ -836,6 +922,7 @@
 			x86_cap_flags[14] = "mca";
 			x86_cap_flags[16] = "pat";
 			x86_cap_flags[17] = "pse36";
+			x86_cap_flags[18] = "psn";
 			x86_cap_flags[24] = "osfxsr";
 		}
 
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.2.3/linux/arch/i386/kernel/smp.c	Tue Feb 23 15:21:32 1999
+++ linux/arch/i386/kernel/smp.c	Mon Mar 15 11:09:47 1999
@@ -474,7 +474,7 @@
 					 */
 			
 					cfg=pg0[0];
-					pg0[0] = (mp_lapic_addr | 7);
+					pg0[0] = (mp_lapic_addr | _PAGE_RW | _PAGE_PRESENT);
 					local_flush_tlb();
 
 					boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID));
@@ -1329,7 +1329,7 @@
 		 *	Install writable page 0 entry.
 		 */
 		cfg = pg0[0];
-		pg0[0] = 3;	/* writeable, present, addr 0 */
+		pg0[0] = _PAGE_RW | _PAGE_PRESENT;	/* writeable, present, addr 0 */
 		local_flush_tlb();
 	
 		/*
diff -u --recursive --new-file v2.2.3/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.2.3/linux/arch/i386/kernel/time.c	Wed Mar 10 15:29:45 1999
+++ linux/arch/i386/kernel/time.c	Thu Mar 11 23:25:05 1999
@@ -279,7 +279,6 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	write_unlock_irq(&xtime_lock);
diff -u --recursive --new-file v2.2.3/linux/arch/m68k/kernel/time.c linux/arch/m68k/kernel/time.c
--- v2.2.3/linux/arch/m68k/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/m68k/kernel/time.c	Thu Mar 11 23:25:10 1999
@@ -198,7 +198,6 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	sti();
diff -u --recursive --new-file v2.2.3/linux/arch/mips/kernel/sysirix.c linux/arch/mips/kernel/sysirix.c
--- v2.2.3/linux/arch/mips/kernel/sysirix.c	Sun Nov  8 14:02:43 1998
+++ linux/arch/mips/kernel/sysirix.c	Thu Mar 11 23:25:14 1999
@@ -627,7 +627,6 @@
 	cli();
 	xtime.tv_sec = value;
 	xtime.tv_usec = 0;
-	time_state = TIME_ERROR;
 	time_maxerror = MAXPHASE;
 	time_esterror = MAXPHASE;
 	sti();
diff -u --recursive --new-file v2.2.3/linux/arch/mips/kernel/time.c linux/arch/mips/kernel/time.c
--- v2.2.3/linux/arch/mips/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/mips/kernel/time.c	Thu Mar 11 23:25:19 1999
@@ -260,7 +260,6 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	sti();
diff -u --recursive --new-file v2.2.3/linux/arch/mips/sgi/kernel/indy_timer.c linux/arch/mips/sgi/kernel/indy_timer.c
--- v2.2.3/linux/arch/mips/sgi/kernel/indy_timer.c	Fri Oct 23 22:01:20 1998
+++ linux/arch/mips/sgi/kernel/indy_timer.c	Thu Mar 11 23:25:24 1999
@@ -104,9 +104,10 @@
 	 * absolutely sure we do this update within 500ms before the
 	 * next second starts, thus the following code.
 	 */
-	if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
-	    xtime.tv_usec > 500000 - (tick >> 1) &&
-	    xtime.tv_usec < 500000 + (tick >> 1))
+	if ((time_status & STA_UNSYNC) == 0 &&
+	    xtime.tv_sec > last_rtc_update + 660 &&
+	    xtime.tv_usec >= 500000 - (tick >> 1) &&
+	    xtime.tv_usec <= 500000 + (tick >> 1))
 	  if (set_rtc_mmss(xtime.tv_sec) == 0)
 	    last_rtc_update = xtime.tv_sec;
 	  else
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/8xx_io/commproc.c linux/arch/ppc/8xx_io/commproc.c
--- v2.2.3/linux/arch/ppc/8xx_io/commproc.c	Mon Oct  5 13:13:35 1998
+++ linux/arch/ppc/8xx_io/commproc.c	Fri Mar 19 10:50:03 1999
@@ -111,7 +111,12 @@
 	*/
 	((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr =
 	    (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
-		((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK;
+		(((5)/2) << 13) | CICR_HP_MASK;
+	/* I hard coded the CPM interrupt to 5 above
+	 * since the CPM_INTERRUPT define is relative to
+	 * the linux irq structure not what the hardware
+	 * belives. -- Cort
+	 */
 	((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr = 0;
 	/* Set our interrupt handler with the core CPU.
 	*/
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/8xx_io/enet.c linux/arch/ppc/8xx_io/enet.c
--- v2.2.3/linux/arch/ppc/8xx_io/enet.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/8xx_io/enet.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,4 @@
 /*
- * $Id: enet.c,v 1.8 1998/11/15 19:58:07 cort Exp $
  * Ethernet driver for Motorola MPC8xx.
  * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
  *
@@ -39,7 +38,7 @@
 
 #include <asm/8xx_immap.h>
 #include <asm/pgtable.h>
-#include <asm/mbx.h>
+#include <asm/fads.h>
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
 #include "commproc.h"
@@ -49,7 +48,7 @@
  *
  * The MPC8xx CPM performs the Ethernet processing on SCC1.  It can use
  * an aribtrary number of buffers on byte boundaries, but must have at
- * least two receive buffers to prevent constand overrun conditions.
+ * least two receive buffers to prevent constant overrun conditions.
  *
  * The buffer descriptors are allocated from the CPM dual port memory
  * with the data buffers allocated from host memory, just like all other
@@ -94,6 +93,17 @@
  *	Port C, 15 - SCC1 Ethernet Tx Enable
  *	Port C, 11 - SCC1 Ethernet Collision
  *	Port C, 10 - SCC1 Ethernet Rx Enable
+ *
+ * The RPX-Lite (that I had :-), was the MPC850SAR.  It has a control
+ * register to enable Ethernet functions in the 68160, and the Ethernet
+ * was controlled by SCC2.  So, the pin I/O was like this:
+ *	Port A, 13 - SCC2 Ethernet Rx
+ *	Port A, 12 - SCC2 Ethernet Tx
+ *	Port A,  6 (CLK2) - Ethernet Tx Clk
+ *	Port A,  4 (CLK4) - Ethernet Rx Clk
+ *	Port B, 18 (RTS2) - Ethernet Tx Enable
+ *	Port C,  8 (CD2) - Ethernet Rx Enable
+ *	Port C,  9 (CTS2) - SCC Ethernet Collision
  */
 
 /* The number of Tx and Rx buffers.  These are allocated from the page
@@ -149,10 +159,26 @@
 static struct net_device_stats *cpm_enet_get_stats(struct device *dev);
 static void set_multicast_list(struct device *dev);
 
-/* GET THIS FROM THE VPD!!!!
+/* Get this from various configuration locations (depends on board).
 */
 /*static	ushort	my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*/
 
+/* Right now, only the boards with an 860 use SCC1 for the Ethernet.
+ * All others use SCC2.  We may need to make this board specific someday.
+ */
+#ifndef CONFIG_MPC860
+/*static	ushort	my_enet_addr[] = { 0x2700, 0x00ec, 0x1000 };*/
+#define CPM_CR_ENET	CPM_CR_CH_SCC2
+#define PROFF_ENET	PROFF_SCC2
+#define SCC_ENET	1		/* Index, not number! */
+#define CPMVEC_ENET	CPMVEC_SCC2
+#else
+#define CPM_CR_ENET CPM_CR_CH_SCC1
+#define PROFF_ENET	PROFF_SCC1
+#define SCC_ENET	0
+#define CPMVEC_ENET	CPMVEC_SCC1
+#endif
+
 static int
 cpm_enet_open(struct device *dev)
 {
@@ -178,7 +204,7 @@
 	/* Transmitter timeout, serious problems. */
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 20)
+		if (tickssofar < 200)
 			return 1;
 		printk("%s: transmit timed out.\n", dev->name);
 		cep->stats.tx_errors++;
@@ -186,17 +212,17 @@
 		{
 			int	i;
 			cbd_t	*bdp;
-			printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n",
+			printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n",
 				   cep->cur_tx, cep->tx_full ? " (full)" : "",
 				   cep->cur_rx);
 			bdp = cep->tx_bd_base;
-			for (i = 0 ; i < TX_RING_SIZE; i++)
+			for (i = 0 ; i < TX_RING_SIZE; i++, bdp++)
 				printk("%04x %04x %08x\n",
 					bdp->cbd_sc,
 					bdp->cbd_datlen,
 					bdp->cbd_bufaddr);
 			bdp = cep->rx_bd_base;
-			for (i = 0 ; i < RX_RING_SIZE; i++)
+			for (i = 0 ; i < RX_RING_SIZE; i++, bdp++)
 				printk("%04x %04x %08x\n",
 					bdp->cbd_sc,
 					bdp->cbd_datlen,
@@ -263,7 +289,7 @@
 	/* Push the data cache so the CPM does not get stale memory
 	 * data.
 	 */
-	/*flush_dcache_range(skb->data, skb->data + skb->len);*/
+	flush_dcache_range(skb->data, skb->data + skb->len);
 
 	/* Send it on its way.  Tell CPM its ready, interrupt when done,
 	 * its the last BD of the frame, and to put the CRC on the end.
@@ -300,7 +326,7 @@
 cpm_enet_interrupt(void *dev_id)
 {
 	struct	device *dev = dev_id;
-	struct	cpm_enet_private *cep;
+	volatile struct	cpm_enet_private *cep;
 	volatile cbd_t	*bdp;
 	ushort	int_events;
 	int	must_restart;
@@ -314,6 +340,7 @@
 	/* Get the interrupt events that caused us to be here.
 	*/
 	int_events = cep->sccp->scc_scce;
+	cep->sccp->scc_scce = int_events;
 	must_restart = 0;
 
 	/* Handle receive event in its own function.
@@ -329,6 +356,7 @@
 	 * I don't know if "normally" implies TXB is set when the buffer
 	 * descriptor is closed.....trial and error :-).
 	 */
+#if 0
 	if (int_events & SCCE_ENET_TXE) {
 
 		/* Transmission errors.
@@ -359,16 +387,46 @@
 		    (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN))
 			must_restart = 1;
 	}
+#endif
 
 	/* Transmit OK, or non-fatal error.  Update the buffer descriptors.
 	*/
 	if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) {
+#if 1
+	    bdp = cep->dirty_tx;
+	    while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) {
+		if ((bdp==cep->cur_tx) && (cep->tx_full == 0))
+		    break;
+
+		if (bdp->cbd_sc & BD_ENET_TX_HB)	/* No heartbeat */
+			cep->stats.tx_heartbeat_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_LC)	/* Late collision */
+			cep->stats.tx_window_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_RL)	/* Retrans limit */
+			cep->stats.tx_aborted_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_UN)	/* Underrun */
+			cep->stats.tx_fifo_errors++;
+		if (bdp->cbd_sc & BD_ENET_TX_CSL)	/* Carrier lost */
+			cep->stats.tx_carrier_errors++;
+
+
+		/* No heartbeat or Lost carrier are not really bad errors.
+		 * The others require a restart transmit command.
+		 */
+		if (bdp->cbd_sc &
+		    (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) {
+			must_restart = 1;
+			cep->stats.tx_errors++;
+		}
+
 		cep->stats.tx_packets++;
+#else
 		bdp = cep->dirty_tx;
-#ifndef final_version
+#if 1
 		if (bdp->cbd_sc & BD_ENET_TX_READY)
 			printk("HEY! Enet xmit interrupt and TX_READY.\n");
 #endif
+#endif
 		/* Deferred means some collisions occurred during transmit,
 		 * but we eventually sent the packet OK.
 		 */
@@ -406,9 +464,9 @@
 		}
 
 		cep->dirty_tx = (cbd_t *)bdp;
-	}
+	    }
 
-	if (must_restart) {
+	    if (must_restart) {
 		volatile cpm8xx_t *cp;
 
 		/* Some transmit errors cause the transmitter to shut
@@ -419,8 +477,9 @@
 		 */
 		cp = cpmp;
 		cp->cp_cpcr =
-		    mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG;
+		    mk_cr_cmd(CPM_CR_ENET, CPM_CR_RESTART_TX) | CPM_CR_FLG;
 		while (cp->cp_cpcr & CPM_CR_FLG);
+	    }
 	}
 
 	/* Check for receive busy, i.e. packets coming but no place to
@@ -432,11 +491,6 @@
 		printk("CPM ENET: BSY can't happen.\n");
 	}
 
-	/* Write the SCC event register with the events we have handled
-	 * to clear them.  Maybe we should do this sooner?
-	 */
-	cep->sccp->scc_scce = int_events;
-
 	dev->interrupt = 0;
 
 	return;
@@ -576,7 +630,7 @@
 	int	i, j;
 	cep = (struct cpm_enet_private *)dev->priv;
 
-	/* Get pointer to SCC1 area in parameter RAM.
+	/* Get pointer to SCC area in parameter RAM.
 	*/
 	ep = (scc_enet_t *)dev->base_addr;
 
@@ -627,7 +681,7 @@
 				/* Ask CPM to run CRC and set bit in
 				 * filter mask.
 				 */
-				cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG;
+				cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_SET_GADDR) | CPM_CR_FLG;
 				/* this delay is necessary here -- Cort */
 				udelay(10);
 				while (cpmp->cp_cpcr & CPM_CR_FLG);
@@ -636,13 +690,15 @@
 	}
 }
 
-/* Initialize the CPM Ethernet on SCC1.  If EPPC-Bug loaded us, or performed
+/* Initialize the CPM Ethernet on SCC.  If EPPC-Bug loaded us, or performed
  * some other network I/O, a whole bunch of this has already been set up.
  * It is no big deal if we do it again, we just have to disable the
  * transmit and receive to make sure we don't catch the CPM with some
  * inconsistent control information.
  */
-__initfunc(int cpm_enet_init(void))
+/* until this gets cleared up -- Cort */
+int __init cpm_enet_init() { m8xx_enet_init(); }
+int __init m8xx_enet_init(void)
 {
 	struct device *dev;
 	struct cpm_enet_private *cep;
@@ -650,6 +706,7 @@
 	unsigned char	*eap;
 	unsigned long	mem_addr;
 	pte_t		*pte;
+	bd_t		*bd;
 	volatile	cbd_t		*bdp;
 	volatile	cpm8xx_t	*cp;
 	volatile	scc_t		*sccp;
@@ -660,6 +717,8 @@
 
 	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
 
+	bd = (bd_t *)res;
+
 	/* Allocate some private information.
 	*/
 	cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL);
@@ -670,13 +729,13 @@
 	*/
 	dev = init_etherdev(0, 0);
 
-	/* Get pointer to SCC1 area in parameter RAM.
+	/* Get pointer to SCC area in parameter RAM.
 	*/
-	ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]);
+	ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_ENET]);
 
 	/* And another to the SCC register area.
 	*/
-	sccp = (volatile scc_t *)(&cp->cp_scc[0]);
+	sccp = (volatile scc_t *)(&cp->cp_scc[SCC_ENET]);
 	cep->sccp = (scc_t *)sccp;		/* Keep the pointer handy */
 
 	/* Disable receive and transmit in case EPPC-Bug started it.
@@ -686,6 +745,9 @@
 	/* Cookbook style from the MPC860 manual.....
 	 * Not all of this is necessary if EPPC-Bug has initialized
 	 * the network.
+	 * So far we are lucky, all board configurations use the same
+	 * pins, or at least the same I/O Port for these functions.....
+	 * It can't last though......
 	 */
 
 	/* Configure port A pins for Txd and Rxd.
@@ -706,7 +768,7 @@
 	immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK);
 
 	/* Configure Serial Interface clock routing.
-	 * First, clear all SCC1 bits to zero, then set the ones we want.
+	 * First, clear all SCC bits to zero, then set the ones we want.
 	 */
 	cp->cp_sicr &= ~SICR_ENET_MASK;
 	cp->cp_sicr |= SICR_ENET_CLKRT;
@@ -731,13 +793,13 @@
 	cep->dirty_tx = cep->cur_tx = cep->tx_bd_base;
 	cep->cur_rx = cep->rx_bd_base;
 
-	/* Issue init Rx BD command for SCC1.
+	/* Issue init Rx BD command for SCC.
 	 * Manual says to perform an Init Rx parameters here.  We have
 	 * to perform both Rx and Tx because the SCC may have been
 	 * already running.
 	 * In addition, we have to do it later because we don't yet have
 	 * all of the BD control/status set properly.
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG;
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_RX) | CPM_CR_FLG;
 	while (cp->cp_cpcr & CPM_CR_FLG);
 	 */
 
@@ -781,20 +843,17 @@
 	ep->sen_iaddr3 = 0;
 	ep->sen_iaddr4 = 0;
 
-	/* Set Ethernet station address.  This must come from the
-	 * Vital Product Data (VPD) EEPROM.....as soon as I get the
-	 * I2C interface working.....
+	/* Set Ethernet station address.
 	 *
-	 * Since we performed a diskless boot, the Ethernet controller
+	 * If we performed a MBX diskless boot, the Ethernet controller
 	 * has been initialized and we copy the address out into our
 	 * own structure.
 	 */
-#ifdef notdef
-	ep->sen_paddrh = my_enet_addr[0];
-	ep->sen_paddrm = my_enet_addr[1];
-	ep->sen_paddrl = my_enet_addr[2];
-#else
 	eap = (unsigned char *)&(ep->sen_paddrh);
+#ifndef CONFIG_MBX
+	for (i=5; i>=0; i--)
+		*eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i];
+#else
 	for (i=5; i>=0; i--)
 		dev->dev_addr[i] = *eap++;
 #endif
@@ -854,7 +913,7 @@
 	 * than the manual describes because we have just now finished
 	 * the BD initialization.
 	 */
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_TRX) | CPM_CR_FLG;
 	while (cp->cp_cpcr & CPM_CR_FLG);
 
 	cep->skb_cur = cep->skb_dirty = 0;
@@ -869,7 +928,7 @@
 
 	/* Install our interrupt handler.
 	*/
-	cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev);
+	cpm_install_handler(CPMVEC_ENET, cpm_enet_interrupt, dev);
 
 	/* Set GSMR_H to enable all normal operating modes.
 	 * Set GSMR_L to enable Ethernet to MC68160.
@@ -884,12 +943,42 @@
 	/* Set processing mode.  Use Ethernet CRC, catch broadcast, and
 	 * start frame search 22 bit times after RENA.
 	 */
-	sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22);
+	sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_NIB22);
 
 	/* It is now OK to enable the Ethernet transmitter.
-	*/
+	 * Unfortunately, there are board implementation differences here.
+	 */
+#ifdef CONFIG_MBX
 	immap->im_ioport.iop_pcpar |= PC_ENET_TENA;
 	immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA;
+#endif
+
+#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC)
+	cp->cp_pbpar |= PB_ENET_TENA;
+	cp->cp_pbdir |= PB_ENET_TENA;
+
+	/* And while we are here, set the configuration to enable ethernet.
+	*/
+	*((volatile uint *)RPX_CSR_ADDR) &= ~BCSR0_ETHLPBK;
+	*((volatile uint *)RPX_CSR_ADDR) |=
+			(BCSR0_ETHEN | BCSR0_COLTESTDIS | BCSR0_FULLDPLXDIS);
+#endif
+
+#ifdef CONFIG_BSEIP
+	cp->cp_pbpar |= PB_ENET_TENA;
+	cp->cp_pbdir |= PB_ENET_TENA;
+
+	/* BSE uses port B and C for PHY control.
+	*/
+	cp->cp_pbpar &= ~(PB_BSE_POWERUP | PB_BSE_FDXDIS);
+	cp->cp_pbdir |= (PB_BSE_POWERUP | PB_BSE_FDXDIS);
+	cp->cp_pbdat |= (PB_BSE_POWERUP | PB_BSE_FDXDIS);
+
+	immap->im_ioport.iop_pcpar &= ~PC_BSE_LOOPBACK;
+	immap->im_ioport.iop_pcdir |= PC_BSE_LOOPBACK;
+	immap->im_ioport.iop_pcso &= ~PC_BSE_LOOPBACK;
+	immap->im_ioport.iop_pcdat &= ~PC_BSE_LOOPBACK;
+#endif
 
 	dev->base_addr = (unsigned long)ep;
 	dev->priv = cep;
@@ -913,3 +1002,4 @@
 
 	return 0;
 }
+
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/Makefile linux/arch/ppc/Makefile
--- v2.2.3/linux/arch/ppc/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/Makefile	Fri Mar 19 10:50:03 1999
@@ -45,6 +45,7 @@
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot
 MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot
+MAKEMBXBOOT = $(MAKE) -C arch/$(ARCH)/mbxboot
 
 ifdef CONFIG_8xx
 SUBDIRS += arch/ppc/8xx_io
@@ -63,10 +64,16 @@
 BOOT_TARGETS = netboot znetboot zImage floppy install \
 	vmlinux.coff znetboot.initrd zImage.initrd vmlinux.coff.initrd
 
+ifdef CONFIG_MBX
+$(BOOT_TARGETS): $(CHECKS) vmlinux
+	@$(MAKECOFFBOOT) $@
+	@$(MAKEMBXBOOT) $@
+else
 $(BOOT_TARGETS): $(CHECKS) vmlinux
 	@$(MAKECOFFBOOT) $@
 	@$(MAKEBOOT) $@
 	@$(MAKECHRPBOOT) $@
+endif
 
 pmac_config:
 	rm -f .config arch/ppc/defconfig
@@ -100,10 +107,10 @@
 	@$(MAKECOFFBOOT) clean
 	@$(MAKEBOOT) clean
 	@$(MAKECHRPBOOT) clean
+	@$(MAKEMBXBOOT) clean
 
 archmrproper:
 
 archdep:
 	$(MAKEBOOT) fastdep
 	$(MAKECHRPBOOT) fastdep
-
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile
--- v2.2.3/linux/arch/ppc/boot/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/boot/Makefile	Fri Mar 19 10:50:03 1999
@@ -25,17 +25,14 @@
 IOFF = 0
 ISZ = 0
 
-ifeq ($(CONFIG_ALL_PPC),y)
-# yes, we want to build prep stuff
-CONFIG_PREP = y
-endif
-
-ifeq ($(CONFIG_MBX),y)
-ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000
+ifeq ($(CONFIG_SMP),y)
+TFTPIMAGE=/tftpboot/zImage.prep.smp
 else
-ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000
+TFTPIMAGE=/tftpboot/zImage.prep
 endif
 
+ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
+
 GZIP_FLAGS = -v9
 
 OBJECTS := head.o misc.o ../coffboot/zlib.o
@@ -43,19 +40,13 @@
 OBJCOPY = $(CROSS_COMPILE)objcopy
 OBJCOPY_ARGS = -O elf32-powerpc
 
-ifeq ($(CONFIG_MBX),y)
-OBJECTS += mbxtty.o
-CFLAGS += -DCONFIG_MBX
-else
-OBJECTS += vreset.o kbd.o
+OBJECTS += vreset.o kbd.o of1275.o
  ifeq ($(CONFIG_SERIAL_CONSOLE),y)
   OBJECTS += ns16550.o
  endif
-endif
 
 all:	zImage
 
-ifeq ($(CONFIG_PREP),y)
 zvmlinux.initrd: zvmlinux
 	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
@@ -73,47 +64,12 @@
 		--add-section=image=../coffboot/vmlinux.gz \
 		zvmlinux.initrd.tmp $@
 	rm zvmlinux.initrd.tmp
-endif
-ifeq ($(CONFIG_MBX),y)
-zvmlinux.initrd: zvmlinux
-	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
-	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
-		--add-section=initrd=ramdisk.image.gz \
-		--add-section=image=../coffboot/vmlinux.gz \
-		zvmlinux.initrd.tmp zvmlinux.initrd
-	$(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd initrd` \
-		-DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \
-		-DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \
-		-DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \
-		-DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
-	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
-	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
-		--add-section=initrd=ramdisk.image.gz \
-		--add-section=image=../coffboot/vmlinux.gz \
-		zvmlinux.initrd.tmp $@
-	rm zvmlinux.initrd.tmp
-endif
 
-ifeq ($(CONFIG_PREP),y)
 zImage: zvmlinux mkprep
 	./mkprep -pbp zvmlinux zImage
-else
-ifeq ($(CONFIG_MBX),y)
-zImage: zvmlinux
-	ln -sf zvmlinux zImage
-else
-zImage:
-endif
-endif
 
-ifeq ($(CONFIG_PREP),y)
 zImage.initrd: zvmlinux.initrd mkprep
 	./mkprep -pbp zvmlinux.initrd zImage.initrd
-endif
-ifeq ($(CONFIG_MBX),y)
-zImage.initrd: zvmlinux.initrd
-	ln -sf zvmlinux.initrd zImage.initrd
-endif
 
 zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
 #
@@ -136,34 +92,16 @@
 	rm zvmlinux.tmp
 
 floppy: $(TOPDIR)/vmlinux zImage
-ifeq ($(CONFIG_PREP),y)
 	dd if=zImage of=/dev/fd0H1440 bs=64b
-endif
 
-ifeq ($(CONFIG_PREP),y)
 mkprep : mkprep.c
 	$(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c
-endif
 
-ifeq ($(CONFIG_PREP),y)
 znetboot : zImage
-	cp zImage /tftpboot/zImage.prep
-else
-ifeq ($(CONFIG_MBX),y)
-znetboot : zImage
-	cp zImage /tftpboot/zImage.mbx
-else
-znetboot :
-endif
-endif
+	cp zImage $(TFTPIMAGE)
 
 znetboot.initrd : zImage.initrd
-ifeq ($(CONFIG_PREP),y)
-	cp zImage.initrd /tftpboot/zImage.prep
-endif
-ifeq ($(CONFIG_MBX),y)
-	cp zImage.initrd /tftpboot/zImage.mbx
-endif
+	cp zImage.initrd $(TFTPIMAGE)
 
 clean:
 	rm -f vmlinux* zvmlinux* mkprep zImage*
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S
--- v2.2.3/linux/arch/ppc/boot/head.S	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/boot/head.S	Fri Mar 19 10:50:03 1999
@@ -7,39 +7,24 @@
 	.text
 
 /*
- * $Id: head.S,v 1.26 1998/09/19 01:21:20 cort Exp $
+ * $Id: head.S,v 1.29 1999/03/08 23:41:17 cort Exp $
  *	
- * This code is loaded by the ROM loader at some arbitrary location.
- * Move it to high memory so that it can load the kernel at 0x0000.
- *
- * The MBX EPPC-Bug understands ELF, so it loads us into the location
- * specified in the header.  This is a two step process.  First, EPPC-Bug
- * loads the file into the intermediate buffer memory location specified
- * by the environment parameters.  When it discovers this is an ELF
- * binary, it relocates to the link address for us.  Unfortunately, the
- * header does not move with the file, so we have to find the
- * intermediate load location and read the header from there.  From
- * information provided by Motorola (thank you), we know this intermediate
- * location can be found from the NVRAM environment.
- * All of these addresses must be somewhat carefully chosen to make sure
- * we don't overlap the regions.  I chose to load the kernel at 0, the
- * compressed image loads at 0x00100000, and the MBX intermediate buffer
- * was set to 0x00200000.  Provided the loaded kernel image never grows
- * over one megabyte (which I am going to ensure never happens :-), these
- * will work fine.  When we get called from EPPC-Bug, registers are:
- *              R1 - Stack pointer at a high memory address.
- *              R3 - Pointer to Board Information Block.
- *              R4 - Pointer to argument string.
- *              Interrupts masked, cache and MMU disabled.
+ * Boot loader philosophy:
+ *      ROM loads us to some arbitrary location
+ *      Move the boot code to the link address (8M)
+ *      Call decompress_kernel()
+ *         Relocate the initrd, zimage and residual data to 8M
+ *         Decompress the kernel to 0
+ *      Jump to the kernel entry
+ *            -- Cort
  */
-
 	.globl	start
 start:
 	bl	start_
 start_:
 	mr	r11,r3		/* Save pointer to residual/board data */
-	
-#ifndef CONFIG_MBX
+	mr      r25,r5          /* Save OFW pointer */
+
 	mfmsr	r3		/* Turn off interrupts  */
 	li	r4,0
 	ori	r4,r4,MSR_EE
@@ -68,25 +53,6 @@
 	mr	r7,r5
 	b	start_ldr
 1010:
-#if 0	
-/* Copy relocation code down to location 0x0100 (where we hope it's safe!) */
-	mflr	r3
-	addi	r5,r3,start_ldr-start_
-	addi	r3,r3,relocate-start_
-	li	r4,0x0100
-	mtctr	r4
-	subi	r3,r3,4
-	subi	r4,r4,4
-00:	lwzu	r6,4(r3)
-	stwu	r6,4(r4)
-	cmp	0,r3,r5
-	bne	00b
-	mflr	r21
-	mfctr	r22
-	mtlr	r21
-	mtctr	r22
-	bctr			/* Jump to code */
-#endif	
 /* 
  * no matter where we're loaded, move ourselves to -Ttext address
  */
@@ -96,13 +62,8 @@
 	mr	r8,r3
 	lis	r4,start@h
 	ori	r4,r4,start@l
-#if 0	
-	lis	r5,edata@h
-	ori	r5,r5,edata@l
-#else
 	lis	r5,end@h
 	ori	r5,r5,end@l
-#endif		
 	addi	r5,r5,3			/* Round up - just in case */
 	sub	r5,r5,r4		/* Compute # longwords to move */
 	srwi	r5,r5,2
@@ -120,7 +81,6 @@
 	mtlr	r3			/* Easiest way to do an absolute jump */
 	blr
 start_ldr:
-#endif /* ndef CONFIG_MBX */
 /* Clear all of BSS */
 	lis	r3,edata@h
 	ori	r3,r3,edata@l
@@ -140,31 +100,11 @@
 	li	r2,0x000F		/* Mask pointer to 16-byte boundary */
 	andc	r1,r1,r2
 /* Run loader */
-#ifdef CONFIG_MBX
-	mr	r3, r11
-        mr      r21, r11
-	bl      serial_init		/* Init MBX serial port */
-
-	lis	r8, 0xfa200000@h	/* Disable Ethernet SCC */
-	li	r0, 0
-	stw	r0, 0x0a00(r8)
-
-	mr      r11, r21
-	lis	r8,start@h
-	ori	r8,r8,start@l
-	li	r9,end@h
-	ori	r9,r9,end@l
-	sub	r7,r8,r9
-	srwi	r7,r7,2
-#define ILAP_ADDRESS    0xfa000020
-        lis     r8, ILAP_ADDRESS@h
-        lwz     r8, ILAP_ADDRESS@l(r8)
-        addis   r8, r8, 1               /* Add 64K */
-#endif	
 	mr	r3,r8			/* Load point */
 	mr	r4,r7			/* Program length */
 	mr	r5,r6			/* Checksum */
 	mr	r6,r11			/* Residual data */
+	mr      r7,r25                  /* OFW interfaces */
 	bl	decompress_kernel
 	
 	/* changed to use r3 (as firmware does) for kernel
@@ -193,12 +133,10 @@
 	li	r9,0x0
 	lwz	r9,0(r9)
 	mtlr	r9
-#ifndef CONFIG_MBX
 	li	r9,0
 	lis	r10,0xdeadc0de@h
 	ori	r10,r10,0xdeadc0de@l
 	stw	r10,0(r9)
-#endif
 	blr
 hang:
 	b	hang	
@@ -269,7 +207,6 @@
 _GLOBAL(flush_instruction_cache)
 	mflr	r5
 	bl	flush_data_cache
-#ifndef CONFIG_MBX
 	mfspr	r3,HID0	/* Caches are controlled by this register */
 	li	r4,0
 	ori	r4,r4,(HID0_ICE|HID0_ICFI)
@@ -278,18 +215,12 @@
 	andc	r3,r3,r4
 	ori	r3,r3,HID0_ICE	/* Enable cache */
 	mtspr	HID0,r3
-#endif	
 	mtlr	r5
 	blr
 	
 #define NUM_CACHE_LINES 128*8
 #define CACHE_LINE_SIZE 32 
-#if 0
-cache_flush_buffer:
-	.space	NUM_CACHE_LINES*CACHE_LINE_SIZE	/* CAUTION! these need to match hardware */
-#else
 #define cache_flush_buffer 0x1000
-#endif
 
 /*
  * Flush data cache
@@ -300,11 +231,7 @@
 	ori	r3,r3,cache_flush_buffer@l
 	li	r4,NUM_CACHE_LINES
 	mtctr	r4
-#if 0
-00:	dcbz	0,r3			/* Flush cache line with minimal BUS traffic */
-#else
 00:	lwz	r4,0(r3)
-#endif
 	addi	r3,r3,CACHE_LINE_SIZE	/* Next line, please */
 	bdnz	00b	
 10:	blr
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/kbd.c linux/arch/ppc/boot/kbd.c
--- v2.2.3/linux/arch/ppc/boot/kbd.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/boot/kbd.c	Fri Mar 19 10:50:03 1999
@@ -1,7 +1,6 @@
-
 #include <linux/keyboard.h>
 
-#include <../drivers/char/defkeymap.c>	/* yeah I know it's bad */
+#include <../drivers/char/defkeymap.c>	/* yeah I know it's bad -- Cort */
 
 
 unsigned char shfts, ctls, alts, caps;
@@ -119,7 +118,7 @@
 		}
 		break;
 	}
-	if (brk) return (0);  /* Ignore initial 'key up' codes */
+	if (brk) return (-1);  /* Ignore initial 'key up' codes */
 	goto loop;
 }
 
@@ -144,22 +143,63 @@
 	while (inb(KBSTATP) & KBOUTRDY) ;
 	outb(KBDATAP,0x45);
 	for (i = 0;  i < 10000;  i++) udelay(1);
+	
+	while (inb(KBSTATP) & KBOUTRDY) ;
+	outb(KBSTATP,0x20);
+	while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */
+	if (! (inb(KBDATAP) & 0x40)) {
+		/*
+		 * Quote from PS/2 System Reference Manual:
+		 *
+		 * "Address hex 0060 and address hex 0064 should be
+		 * written only when the input-buffer-full bit and
+		 * output-buffer-full bit in the Controller Status
+		 * register are set 0." (KBINRDY and KBOUTRDY)
+		 */
+		
+		while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ;
+		outb(KBDATAP,0xF0);
+		while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) ;
+		outb(KBDATAP,0x01);
+	}
+	
 	while (inb(KBSTATP) & KBOUTRDY) ;
 	outb(KBSTATP,0xAE);
 }
 
+/* We have to actually read the keyboard when CRT_tstc is called,
+ * since the pending data might be a key release code, and therefore
+ * not valid data.  In this case, kbd() will return -1, even though there's
+ * data to be read.  Of course, we might actually read a valid key press,
+ * in which case it gets queued into key_pending for use by CRT_getc.
+ */
+
 static int kbd_reset = 0;
 
+static int key_pending = -1;
+
 int CRT_getc(void)
 {
 	int c;
 	if (!kbd_reset) {kbdreset(); kbd_reset++; }
+
+        if (key_pending != -1) {
+                c = key_pending;
+                key_pending = -1;
+                return c;
+        } else {
 	while ((c = kbd(0)) == 0) ;
-	return(c);
+                return c;
+        }
 }
 
 int CRT_tstc(void)
 {
 	if (!kbd_reset) {kbdreset(); kbd_reset++; }
-	return ((inb(KBSTATP) & KBINRDY) != 0);
+
+        while (key_pending == -1 && ((inb(KBSTATP) & KBINRDY) != 0)) {
+                key_pending = kbd(1);
+        }
+
+        return (key_pending != -1);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/mbxtty.c linux/arch/ppc/boot/mbxtty.c
--- v2.2.3/linux/arch/ppc/boot/mbxtty.c	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/boot/mbxtty.c	Wed Dec 31 16:00:00 1969
@@ -1,201 +0,0 @@
-
-
-/* Minimal serial functions needed to send messages out the serial
- * port on the MBX console.
- *
- * The MBX uxes SMC1 for the serial port.  We reset the port and use
- * only the first BD that EPPC-Bug set up as a character FIFO.
- *
- * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug
- * use COM1 instead of SMC1 as the console port.  This kinda sucks
- * for the rest of the kernel, so here we force the use of SMC1 again.
- * I f**ked around for a day trying to figure out how to make EPPC-Bug
- * use SMC1, but gave up and decided to fix it here.
- */
-#include <linux/config.h>
-#include <linux/types.h>
-#ifdef CONFIG_MBX
-#include <asm/mbx.h>
-#endif
-#ifdef CONFIG_FADS
-#include <asm/fads.h>
-#endif
-#include "../8xx_io/commproc.h"
-
-#ifdef CONFIG_MBX
-#define MBX_CSR1	((volatile u_char *)0xfa100000)
-#define CSR1_COMEN	(u_char)0x02
-#endif
-
-static cpm8xx_t	*cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm);
-
-void
-serial_init(bd_t *bd)
-{
-	volatile smc_t		*sp;
-	volatile smc_uart_t	*up;
-	volatile cbd_t	*tbdf, *rbdf;
-	volatile cpm8xx_t	*cp;
-	uint	dpaddr, memaddr;
-
-	cp = cpmp;
-	sp = (smc_t*)&(cp->cp_smc[0]);
-	up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1];
-
-	/* Disable transmitter/receiver.
-	*/
-	sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
-
-#ifdef CONFIG_MBX
-	if (*MBX_CSR1 & CSR1_COMEN) {
-		/* COM1 is enabled.  Initialize SMC1 and use it for
-		 * the console port.
-		 */
-
-		/* Enable SDMA.
-		*/
-		((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1;
-
-		/* Use Port B for SMCs instead of other functions.
-		*/
-		cp->cp_pbpar |= 0x00000cc0;
-		cp->cp_pbdir &= ~0x00000cc0;
-		cp->cp_pbodr &= ~0x00000cc0;
-
-		/* Allocate space for two buffer descriptors in the DP ram.
-		 * For now, this address seems OK, but it may have to
-		 * change with newer versions of the firmware.
-		 */
-		dpaddr = 0x0800;
-
-		/* Grab a few bytes from the top of memory.  EPPC-Bug isn't
-		 * running any more, so we can do this.
-		 */
-		memaddr = (bd->bi_memsize - 32) & ~15;
-
-		/* Set the physical address of the host memory buffers in
-		 * the buffer descriptors.
-		 */
-		rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr];
-		rbdf->cbd_bufaddr = memaddr;
-		rbdf->cbd_sc = 0;
-		tbdf = rbdf + 1;
-		tbdf->cbd_bufaddr = memaddr+4;
-		tbdf->cbd_sc = 0;
-
-		/* Set up the uart parameters in the parameter ram.
-		*/
-		up->smc_rbase = dpaddr;
-		up->smc_tbase = dpaddr+sizeof(cbd_t);
-		up->smc_rfcr = SMC_EB;
-		up->smc_tfcr = SMC_EB;
-
-		/* Set UART mode, 8 bit, no parity, one stop.
-		 * Enable receive and transmit.
-		 */
-		sp->smc_smcmr = smcr_mk_clen(9) |  SMCMR_SM_UART;
-
-		/* Mask all interrupts and remove anything pending.
-		*/
-		sp->smc_smcm = 0;
-		sp->smc_smce = 0xff;
-
-		/* Set up the baud rate generator.
-		 * See 8xx_io/commproc.c for details.
-		 */
-		cp->cp_simode = 0x10000000;
-		cp->cp_brgc1 =
-			((((bd->bi_intfreq * 1000000)/16) / 9600) << 1) | CPM_BRG_EN;
-
-		/* Enable SMC1 for console output.
-		*/
-		*MBX_CSR1 &= ~CSR1_COMEN;
-	}
-	else {
-#endif
-		/* SMC1 is used as console port.
-		*/
-		tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase];
-		rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase];
-
-		/* Issue a stop transmit, and wait for it.
-		*/
-		cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1,
-					CPM_CR_STOP_TX) | CPM_CR_FLG;
-		while (cp->cp_cpcr & CPM_CR_FLG);
-#ifdef CONFIG_MBX
-	}
-#endif
-
-	/* Make the first buffer the only buffer.
-	*/
-	tbdf->cbd_sc |= BD_SC_WRAP;
-	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
-
-	/* Single character receive.
-	*/
-	up->smc_mrblr = 1;
-	up->smc_maxidl = 0;
-
-	/* Initialize Tx/Rx parameters.
-	*/
-	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
-	while (cp->cp_cpcr & CPM_CR_FLG);
-
-	/* Enable transmitter/receiver.
-	*/
-	sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
-}
-
-void
-serial_putchar(const char c)
-{
-	volatile cbd_t		*tbdf;
-	volatile char		*buf;
-	volatile smc_uart_t	*up;
-
-	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
-	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
-
-	/* Wait for last character to go.
-	*/
-	buf = (char *)tbdf->cbd_bufaddr;
-	while (tbdf->cbd_sc & BD_SC_READY);
-
-	*buf = c;
-	tbdf->cbd_datlen = 1;
-	tbdf->cbd_sc |= BD_SC_READY;
-}
-
-char
-serial_getc()
-{
-	volatile cbd_t		*rbdf;
-	volatile char		*buf;
-	volatile smc_uart_t	*up;
-	char			c;
-
-	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
-	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
-
-	/* Wait for character to show up.
-	*/
-	buf = (char *)rbdf->cbd_bufaddr;
-	while (rbdf->cbd_sc & BD_SC_EMPTY);
-	c = *buf;
-	rbdf->cbd_sc |= BD_SC_EMPTY;
-
-	return(c);
-}
-
-int
-serial_tstc()
-{
-	volatile cbd_t		*rbdf;
-	volatile smc_uart_t	*up;
-
-	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
-	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
-
-	return(!(rbdf->cbd_sc & BD_SC_EMPTY));
-}
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c
--- v2.2.3/linux/arch/ppc/boot/misc.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/boot/misc.c	Fri Mar 19 10:50:03 1999
@@ -1,7 +1,7 @@
 /*
  * misc.c
  *
- * $Id: misc.c,v 1.53 1998/12/15 17:40:15 cort Exp $
+ * $Id: misc.c,v 1.61 1999/03/08 23:51:02 cort Exp $
  * 
  * Adapted for PowerPC by Gary Thomas
  *
@@ -17,13 +17,7 @@
 #include <asm/page.h>
 #include <asm/processor.h>
 #include <asm/mmu.h>
-#ifdef CONFIG_MBX
-#include <asm/mbx.h>
-#endif
-#ifdef CONFIG_FADS
-#include <asm/fads.h>
-#endif
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX)
+#if defined(CONFIG_SERIAL_CONSOLE)
 #include "ns16550.h"
 struct NS16550 *com_port;
 #endif /* CONFIG_SERIAL_CONSOLE */
@@ -37,30 +31,16 @@
  */
 char *avail_ram;
 char *end_avail;
+extern char _end[];
 
-/* Because of the limited amount of memory on the MBX, it presents
- * loading problems.  The biggest is that we load this boot program
- * into a relatively low memory address, and the Linux kernel Bss often
- * extends into this space when it get loaded.  When the kernel starts
- * and zeros the BSS space, it also writes over the information we
- * save here and pass to the kernel (command line and board info).
- * On the MBX we grab some known memory holes to hold this information.
- */
-char cmd_preset[] = "console=tty0 console=ttyS0,9600n8";
+#if defined(CONFIG_SERIAL_CONSOLE)
+char cmd_preset[] = "console=ttyS0,9600n8";
+#else
+char cmd_preset[] = "";
+#endif
 char	cmd_buf[256];
 char	*cmd_line = cmd_buf;
 
-#if defined(CONFIG_MBX) || defined(CONFIG_FADS)
-char	*root_string = "root=/dev/nfs";
-char	*nfsaddrs_string = "nfsaddrs=";
-char	*nfsroot_string = "nfsroot=";
-char	*defroot_string = "/sys/mbxroot";
-int	do_ipaddrs(char **cmd_cp, int echo);
-void	do_nfsroot(char **cmd_cp, char *dp);
-int	strncmp(const char * cs,const char * ct,size_t count);
-char	*strrchr(const char * s, int c);
-#endif
-
 RESIDUAL hold_resid_buf;
 RESIDUAL *hold_residual = &hold_resid_buf;
 unsigned long initrd_start = 0, initrd_end = 0;
@@ -78,6 +58,7 @@
 void * memcpy(void * __dest, __const void * __src,
 			    int __n);
 void gunzip(void *, int, unsigned char *, int *);
+int _cvt(unsigned long val, char *buf, long radix, char *digits);
 
 void pause()
 {
@@ -90,7 +71,6 @@
 	while(1); 
 }
 
-#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS)
 static void clear_screen()
 {
 	int i, j;
@@ -113,17 +93,17 @@
 
 tstc(void)
 {
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX)
-	return (CRT_tstc() || NS16550_tstc(com_port));
-#else
-	return (CRT_tstc() );
+	return (
+#if defined(CONFIG_SERIAL_CONSOLE)
+		NS16550_tstc(com_port) ||
 #endif /* CONFIG_SERIAL_CONSOLE */
+		CRT_tstc());
 }
 
 getc(void)
 {
 	while (1) {
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX)
+#if defined(CONFIG_SERIAL_CONSOLE)
 		if (NS16550_tstc(com_port)) return (NS16550_getc(com_port));
 #endif /* CONFIG_SERIAL_CONSOLE */
 		if (CRT_tstc()) return (CRT_getc());
@@ -135,7 +115,7 @@
 {
 	int x,y;
 
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX)
+#if defined(CONFIG_SERIAL_CONSOLE)
 	NS16550_putc(com_port, c);
 	if ( c == '\n' ) NS16550_putc(com_port, '\r');
 #endif /* CONFIG_SERIAL_CONSOLE */
@@ -149,6 +129,8 @@
 			scroll();
 			y--;
 		}
+	} else if (c == '\r') {
+		x = 0;
 	} else if (c == '\b') {
 		if (x > 0) {
 			x--;
@@ -179,7 +161,7 @@
 	y = orig_y;
 
 	while ( ( c = *s++ ) != '\0' ) {
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX)
+#if defined(CONFIG_SERIAL_CONSOLE)
 	        NS16550_putc(com_port, c);
 	        if ( c == '\n' ) NS16550_putc(com_port, '\r');
 #endif /* CONFIG_SERIAL_CONSOLE */
@@ -209,39 +191,6 @@
 	orig_x = x;
 	orig_y = y;
 }
-#else
-/* The MBX is just the serial port.
-*/
-tstc(void)
-{
-        return (serial_tstc());
-}
-
-getc(void)
-{
-        while (1) {
-                if (serial_tstc()) return (serial_getc());
-        }
-}
-
-void 
-putc(const char c)
-{
-        serial_putchar(c);
-}
-
-void puts(const char *s)
-{
-        char c;
-
-        while ( ( c = *s++ ) != '\0' ) {
-                serial_putchar(c);
-                if ( c == '\n' )
-                        serial_putchar('\r');
-        }
-}
-
-#endif /* CONFIG_MBX */
 
 void * memcpy(void * __dest, __const void * __src,
 			    int __n)
@@ -354,7 +303,8 @@
 unsigned char sanity[0x2000];
 
 unsigned long
-decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual)
+decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
+		  RESIDUAL *residual, void *OFW_interface)
 {
 	int timer;
 	extern unsigned long start;
@@ -362,9 +312,11 @@
 	unsigned long i;
 	BATU *u;
 	BATL *l;
-#if defined(CONFIG_MBX) || defined(CONFIG_KB)
-	char	*dp;
-#endif
+	unsigned long TotalMemory;
+	unsigned long orig_MSR;
+	int dev_handle;
+	int mem_info[2];
+	int res, size;
 
 	lines = 25;
 	cols = 80;
@@ -372,7 +324,6 @@
 	orig_y = 24;
 
 	
-#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS)
 	/*
 	 * IBM's have the MMU on, so we have to disable it or
 	 * things get really unhappy in the kernel when
@@ -381,42 +332,62 @@
 	 */
 	flush_instruction_cache();
 	_put_HID0(_get_HID0() & ~0x0000C000);
-	_put_MSR(_get_MSR() & ~0x0030);
+	_put_MSR((orig_MSR = _get_MSR()) & ~0x0030);
 	vga_init(0xC0000000);
 
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX)
-	com_port = (struct NS16550 *)NS16550_init(0);
+#if defined(CONFIG_SERIAL_CONSOLE)
+	com_port = (struct NS16550 *)NS16550_init(1);
 #endif /* CONFIG_SERIAL_CONSOLE */
 
 	if (residual)
+	{
 		memcpy(hold_residual,residual,sizeof(RESIDUAL));
-#else /* CONFIG_MBX */
-	
-	/* Grab some space for the command line and board info.  Since
-	 * we no longer use the ELF header, but it was loaded, grab
-	 * that space.
-	 */
-	cmd_line = (char *)(load_addr - 0x10000);
-	hold_residual = (RESIDUAL *)(cmd_line + sizeof(cmd_buf));
-	/* copy board data */
-	if (residual)
-		memcpy(hold_residual,residual,sizeof(bd_t));
-#endif /* CONFIG_MBX */
-
-	/* MBX/prep sometimes put the residual/board info at the end of mem 
-	 * assume 16M for now  -- Cort
-	 * To boot on standard MBX boards with 4M, we can't use initrd,
-	 * and we have to assume less memory.  -- Dan
-	 */
-	if ( INITRD_OFFSET )
-		end_avail = (char *)0x01000000;
-	else
-		end_avail = (char *)0x00400000;
+	} else {
+		/* Assume 32M in the absence of more info... */
+		TotalMemory = 0x02000000;
+		/*
+		 * This is a 'best guess' check.  We want to make sure
+		 * we don't try this on a PReP box without OF
+		 *     -- Cort
+		 */
+		while (OFW_interface && ((unsigned long)OFW_interface < 0x10000000) )
+		{
+			/* The MMU needs to be on when we call OFW */
+			_put_MSR(orig_MSR);
+			of_init(OFW_interface);
+
+			/* get handle to memory description */
+			res = of_finddevice("/memory@0", 
+					    &dev_handle);
+			// puthex(res);  puts("\n");
+			if (res) break;
+			
+			/* get the info */
+			// puts("get info = ");
+			res = of_getprop(dev_handle, 
+					 "reg", 
+					 mem_info, 
+					 sizeof(mem_info), 
+					 &size);
+			// puthex(res);  puts(", info = "); puthex(mem_info[0]);  
+			// puts(" ");  puthex(mem_info[1]);   puts("\n");
+			if (res) break;
+			
+			TotalMemory = mem_info[1];
+			break;
+		}
+		hold_residual->TotalMemory = TotalMemory;
+		residual = hold_residual;
+		/* Turn MMU back off */
+		_put_MSR(orig_MSR & ~0x0030);
+        }
 
-	/* let residual data tell us it's higher */
-	if ( (unsigned long)residual > 0x00800000 )
-		end_avail = (char *)PAGE_ALIGN((unsigned long)residual);
+	/* assume the chunk below 8M is free */
+	end_avail = (char *)0x00800000;
 
+	/* tell the user where we were loaded at and where we
+	 * were relocated to for debugging this process
+	 */
 	puts("loaded at:     "); puthex(load_addr);
 	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
 	if ( (unsigned long)load_addr != (unsigned long)&start )
@@ -431,20 +402,12 @@
 	{
 		puts("board data at: "); puthex((unsigned long)residual);
 		puts(" ");
-#if defined(CONFIG_MBX) || defined(CONFIG_FADS)
-		puthex((unsigned long)((unsigned long)residual + sizeof(bd_t)));
-#else
 		puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL)));
-#endif	
 		puts("\n");
 		puts("relocated to:  ");
 		puthex((unsigned long)hold_residual);
 		puts(" ");
-#if defined(CONFIG_MBX) || defined(CONFIG_FADS)
-		puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t)));
-#else
 		puthex((unsigned long)((unsigned long)hold_residual + sizeof(RESIDUAL)));
-#endif	
 		puts("\n");
 	}
 
@@ -460,33 +423,16 @@
 	initrd_end = INITRD_SIZE + initrd_start;
 
 	/*
-	 * setup avail_ram - this is the first part of ram usable
-	 * by the uncompress code. -- Cort
+	 * Find a place to stick the zimage and initrd and 
+	 * relocate them if we have to. -- Cort
 	 */
-	avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size);
-	if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram)
-		&& (load_addr <= 0x01000000) )
-		avail_ram = (char *)(load_addr+(num_words*4));
-	if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram)
-		&& (load_addr <= 0x01000000) )
-		avail_ram = (char *)((unsigned long)&start+(num_words*4));
-	
-	/* relocate zimage */
+	avail_ram = (char *)PAGE_ALIGN((unsigned long)_end);
 	puts("zimage at:     "); puthex((unsigned long)zimage_start);
 	puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
-	/*
-	 * don't relocate the zimage if it was loaded above 16M since
-	 * things get weird if we try to relocate -- Cort
-	 * We don't relocate zimage on a base MBX board because of
-	 * insufficient memory.  In this case we don't have initrd either,
-	 * so use that as an indicator.  -- Dan
-	 */
-	if (( (unsigned long)zimage_start <= 0x01000000 ) && initrd_start)
+	if ( (unsigned long)zimage_start <= 0x008000000 )
 	{
-		memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size),
-			(void *)zimage_start, zimage_size );	
-		zimage_start = (char *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size);
-		end_avail = (char *)zimage_start;
+		memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size );
+		zimage_start = (char *)avail_ram;
 		puts("relocated to:  "); puthex((unsigned long)zimage_start);
 		puts(" ");
 		puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
@@ -498,42 +444,21 @@
 	{
 		puts("initrd at:     "); puthex(initrd_start);
 		puts(" "); puthex(initrd_end); puts("\n");
-		/*
-		 * Memory is really tight on the MBX (we can assume 4M)
-		 * so put the initrd at the TOP of ram, and set end_avail
-		 * to right after that.
-		 *
-		 * I should do something like this for prep, too and keep
-		 * a variable end_of_DRAM to keep track of what we think the
-		 * max ram is.
-		 * -- Cort
-		 */
-#if 0		
-		memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE),
-			(void *)initrd_start,
-			INITRD_SIZE );
-		initrd_start = PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE);
+		avail_ram = (char *)PAGE_ALIGN(
+			(unsigned long)zimage_size+(unsigned long)zimage_start);
+		memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE );
+		initrd_start = (unsigned long)avail_ram;
 		initrd_end = initrd_start + INITRD_SIZE;
-		end_avail = (char *)initrd_start;
 		puts("relocated to:  "); puthex(initrd_start);
 		puts(" "); puthex(initrd_end); puts("\n");
-#endif		
 	}
 
-#ifndef CONFIG_MBX
-	/* this is safe, just use it */
-	/* I don't know why it didn't work for me on the MBX with 20 MB
-	 * memory.  I guess something was saved up there, but I can't
-	 * figure it out......we are running on luck.  -- Dan.
-	 */
 	avail_ram = (char *)0x00400000;
-	end_avail = (char *)0x00600000;
-#endif
+	end_avail = (char *)0x00800000;
 	puts("avail ram:     "); puthex((unsigned long)avail_ram); puts(" ");
 	puthex((unsigned long)end_avail); puts("\n");
 
-	
-#if !defined(CONFIG_MBX) && !defined(CONFIG_FADS)
+#if !defined(CONFIG_SERIAL_CONSOLE)
 	CRT_tstc();  /* Forces keyboard to be initialized */
 #endif
 
@@ -550,13 +475,6 @@
 						cp--;
 						puts("\b \b");
 					}
-#ifdef CONFIG_MBX
-				  } else if (ch == '?') {
-					if (!do_ipaddrs(&cp, 1)) {
-						  *cp++ = ch;
-						  putc(ch);
-					}
-#endif
 				} else {
 					*cp++ = ch;
 					putc(ch);
@@ -567,32 +485,6 @@
 		udelay(1000);  /* 1 msec */
 	}
 	*cp = 0;
-#ifdef CONFIG_MBX
-	/* The MBX does not currently have any default boot strategy.
-	 * If the command line is not filled in, we will automatically
-	 * create the default network boot.
-	 */
-	if (cmd_line[0] == 0) {
-		dp = root_string;
-		while (*dp != 0)
-			*cp++ = *dp++;
-		*cp++ = ' ';
-
-		dp = nfsaddrs_string;
-		while (*dp != 0)
-			*cp++ = *dp++;
-		dp = cp;
-		do_ipaddrs(&cp, 0);
-		*cp++ = ' ';
-
-		/* Add the server address to the root file system path.
-		*/
-		dp = strrchr(dp, ':');
-		dp++;
-		do_nfsroot(&cp, dp);
-		*cp = 0;
-	}
-#endif
 	puts("\n");
 
 	/* mappings on early boot can only handle 16M */
@@ -611,150 +503,6 @@
 	return (unsigned long)hold_residual;
 }
 
-#ifdef CONFIG_MBX
-int
-do_ipaddrs(char **cmd_cp, int echo)
-{
-	char	*cp, *ip, ch;
-	unsigned char	ipd;
-	int	i, j, retval;
-
-	/* We need to create the string:
-	 *	<my_ip>:<serv_ip>
-	 */
-	cp = *cmd_cp;
-	retval = 0;
-
-	if ((cp - 9) >= cmd_line) {
-		if (strncmp(cp - 9, "nfsaddrs=", 9) == 0) {
-			ip = (char *)0xfa000060;
-			retval = 1;
-			for (j=0; j<2; j++) {
-				for (i=0; i<4; i++) {
-					ipd = *ip++;
-
-					ch = ipd/100;
-					if (ch) {
-						ch += '0';
-						if (echo)
-							putc(ch);
-						*cp++ = ch;
-						ipd -= 100 * (ch - '0');
-					}
-
-					ch = ipd/10;
-					if (ch) {
-						ch += '0';
-						if (echo)
-							putc(ch);
-						*cp++ = ch;
-						ipd -= 10 * (ch - '0');
-					}
-
-					ch = ipd + '0';
-					if (echo)
-						putc(ch);
-					*cp++ = ch;
-
-					ch = '.';
-					if (echo)
-						putc(ch);
-					*cp++ = ch;
-				}
-
-				/* At the end of the string, remove the
-				 * '.' and replace it with a ':'.
-				 */
-				*(cp - 1) = ':';
-				if (echo) {
-					putc('\b'); putc(':');
-				}
-			}
-
-			/* At the end of the second string, remove the
-			 * '.' from both the command line and the
-			 * screen.
-			 */
-			--cp;
-			putc('\b'); putc(' '); putc('\b');
-		}
-	}
-	*cmd_cp = cp;
-	return(retval);
-}
-
-void
-do_nfsroot(char **cmd_cp, char *dp)
-{
-	char	*cp, *rp, *ep;
-
-	/* The boot argument (i.e /sys/mbxroot/zImage) is stored
-	 * at offset 0x0078 in NVRAM.  We use this path name to
-	 * construct the root file system path.
-	 */
-	cp = *cmd_cp;
-
-	/* build command string.
-	*/
-	rp = nfsroot_string;
-	while (*rp != 0)
-		*cp++ = *rp++;
-
-	/* Add the server address to the path.
-	*/
-	while (*dp != ' ')
-		*cp++ = *dp++;
-	*cp++ = ':';
-
-	rp = (char *)0xfa000078;
-	ep = strrchr(rp, '/');
-
-	if (ep != 0) {
-		while (rp < ep)
-			*cp++ = *rp++;
-	}
-	else {
-		rp = defroot_string;
-		while (*rp != 0)
-			*cp++ = *rp++;
-	}
-
-	*cmd_cp = cp;
-}
-
-size_t strlen(const char * s)
-{
-	const char *sc;
-
-	for (sc = s; *sc != '\0'; ++sc)
-		/* nothing */;
-	return sc - s;
-}
-
-int strncmp(const char * cs,const char * ct,size_t count)
-{
-	register signed char __res = 0;
-
-	while (count) {
-		if ((__res = *cs - *ct++) != 0 || !*cs++)
-			break;
-		count--;
-	}
-
-	return __res;
-}
-
-char * strrchr(const char * s, int c)
-{
-       const char *p = s + strlen(s);
-       do {
-           if (*p == (char)c)
-               return (char *)p;
-       } while (--p >= s);
-       return NULL;
-}
-#endif
-
 void puthex(unsigned long val)
 {
 	unsigned char buf[10];
@@ -801,4 +549,244 @@
 _bcopy(char *src, char *dst, int len)
 {
 	while (len--) *dst++ = *src++;
+}
+
+
+#define FALSE 0
+#define TRUE  1
+#include <stdarg.h>
+
+int
+strlen(char *s)
+{
+	int len = 0;
+	while (*s++) len++;
+	return len;
+}
+
+_printk(char const *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = _vprintk(putc, fmt, ap);
+	va_end(ap);
+	return (ret);
+}
+
+#define is_digit(c) ((c >= '0') && (c <= '9'))
+
+int
+_vprintk(putc, fmt0, ap)
+int (*putc)();
+const char *fmt0;
+va_list ap;
+{
+	char c, sign, *cp;
+	int left_prec, right_prec, zero_fill, length, pad, pad_on_right;
+	char buf[32];
+	long val;
+	while (c = *fmt0++)
+	{
+		if (c == '%')
+		{
+			c = *fmt0++;
+			left_prec = right_prec = pad_on_right = 0;
+			if (c == '-')
+			{
+				c = *fmt0++;
+				pad_on_right++;
+			}
+			if (c == '0')
+			{
+				zero_fill = TRUE;
+				c = *fmt0++;
+			} else
+			{
+				zero_fill = FALSE;
+			}
+			while (is_digit(c))
+			{
+				left_prec = (left_prec * 10) + (c - '0');
+				c = *fmt0++;
+			}
+			if (c == '.')
+			{
+				c = *fmt0++;
+				zero_fill++;
+				while (is_digit(c))
+				{
+					right_prec = (right_prec * 10) + (c - '0');
+					c = *fmt0++;
+				}
+			} else
+			{
+				right_prec = left_prec;
+			}
+			sign = '\0';
+			switch (c)
+			{
+			case 'd':
+			case 'x':
+			case 'X':
+				val = va_arg(ap, long);
+				switch (c)
+				{
+				case 'd':
+					if (val < 0)
+					{
+						sign = '-';
+						val = -val;
+					}
+					length = _cvt(val, buf, 10, "0123456789");
+					break;
+				case 'x':
+					length = _cvt(val, buf, 16, "0123456789abcdef");
+					break;
+				case 'X':
+					length = _cvt(val, buf, 16, "0123456789ABCDEF");
+					break;
+				}
+				cp = buf;
+				break;
+			case 's':
+				cp = va_arg(ap, char *);
+				length = strlen(cp);
+				break;
+			case 'c':
+				c = va_arg(ap, long /*char*/);
+				(*putc)(c);
+				continue;
+			default:
+				(*putc)('?');
+			}
+			pad = left_prec - length;
+			if (sign != '\0')
+			{
+				pad--;
+			}
+			if (zero_fill)
+			{
+				c = '0';
+				if (sign != '\0')
+				{
+					(*putc)(sign);
+					sign = '\0';
+				}
+			} else
+			{
+				c = ' ';
+			}
+			if (!pad_on_right)
+			{
+				while (pad-- > 0)
+				{
+					(*putc)(c);
+				}
+			}
+			if (sign != '\0')
+			{
+				(*putc)(sign);
+			}
+			while (length-- > 0)
+			{
+				(*putc)(c = *cp++);
+				if (c == '\n')
+				{
+					(*putc)('\r');
+				}
+			}
+			if (pad_on_right)
+			{
+				while (pad-- > 0)
+				{
+					(*putc)(c);
+				}
+			}
+		} else
+		{
+			(*putc)(c);
+			if (c == '\n')
+			{
+				(*putc)('\r');
+			}
+		}
+	}
+}
+
+int _cvt(unsigned long val, char *buf, long radix, char *digits)
+{
+	char temp[80];
+	char *cp = temp;
+	int length = 0;
+	if (val == 0)
+	{ /* Special case */
+		*cp++ = '0';
+	} else
+		while (val)
+		{
+			*cp++ = digits[val % radix];
+			val /= radix;
+		}
+	while (cp != temp)
+	{
+		*buf++ = *--cp;
+		length++;
+	}
+	*buf = '\0';
+	return (length);
+}
+
+_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base)
+{
+	int i, c;
+	if ((unsigned int)s > (unsigned int)p)
+	{
+		s = (unsigned int)s - (unsigned int)p;
+	}
+	while (s > 0)
+	{
+		if (base)
+		{
+			_printk("%06X: ", (int)p - (int)base);
+		} else
+		{
+			_printk("%06X: ", p);
+		}
+		for (i = 0;  i < 16;  i++)
+		{
+			if (i < s)
+			{
+				_printk("%02X", p[i] & 0xFF);
+			} else
+			{
+				_printk("  ");
+			}
+			if ((i % 2) == 1) _printk(" ");
+			if ((i % 8) == 7) _printk(" ");
+		}
+		_printk(" |");
+		for (i = 0;  i < 16;  i++)
+		{
+			if (i < s)
+			{
+				c = p[i] & 0xFF;
+				if ((c < 0x20) || (c >= 0x7F)) c = '.';
+			} else
+			{
+				c = ' ';
+			}
+			_printk("%c", c);
+		}
+		_printk("|\n");
+		s -= 16;
+		p += 16;
+	}
+}
+
+_dump_buf(unsigned char *p, int s)
+{
+	_printk("\n");
+	_dump_buf_with_offset(p, s, 0);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/of1275.c linux/arch/ppc/boot/of1275.c
--- v2.2.3/linux/arch/ppc/boot/of1275.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/of1275.c	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,427 @@
+/* Open Firmware Client Interface */
+
+
+#include "of1275.h"
+
+
+static int (*of_server)(void *) = (int(*)(void*))-1;
+
+void
+of_init(void *handler)
+{
+	of_server = (int(*)(void*))handler;
+}
+
+
+/* 6.3.2.1 Client interface */
+
+
+int
+of_test(const char *name, int *missing)
+{
+	int result;
+	static of_test_service s;
+	s.service = "test";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.name = name;
+	result = of_server(&s);
+	*missing = s.missing;
+	return result;
+}
+
+
+/* 6.3.2.2 Device tree */
+
+
+int
+of_peer(int phandle, int *sibling_phandle)
+{
+	int result;
+	static of_peer_service s;
+	s.service = "peer";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	result = of_server(&s);
+	*sibling_phandle = s.sibling_phandle;
+	return result;
+}
+
+int
+of_child(int phandle, int *child_phandle)
+{
+	int result;
+	static of_child_service s;
+	s.service = "child";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	result = of_server(&s);
+	*child_phandle = s.child_phandle;
+	return result;
+}
+
+int
+of_parent(int phandle, int *parent_phandle)
+{
+	int result;
+	static of_parent_service s;
+	s.service = "parent";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	result = of_server(&s);
+	*parent_phandle = s.parent_phandle;
+	return result;
+}
+
+int
+of_instance_to_package(int ihandle, int *phandle)
+{
+	int result;
+	static of_instance_to_package_service s;
+	s.service = "instance-to-package";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.ihandle = ihandle;
+	result = of_server(&s);
+	*phandle = s.phandle;
+	return result;
+}
+
+int
+of_getproplen(int phandle, const char *name, int *proplen)
+{
+	int result;
+	static of_getproplen_service s;
+	s.service = "getproplen";
+	s.n_args = 2;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	s.name = name;
+	result = of_server(&s);
+	*proplen = s.proplen;
+	return result;
+}
+
+int
+of_getprop(int phandle, const char *name, void *buf, int buflen, int *size)
+{
+	int result;
+	static of_getprop_service s;
+	s.service = "getprop";
+	s.n_args = 4;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	s.name = name;
+	s.buf = buf;
+	s.buflen = buflen;
+	result = of_server(&s);
+	*size = s.size;
+	return result;
+}
+
+int
+of_nextprop(int phandle, const char *previous, void *buf, int *flag)
+{
+	int result;
+	static of_nextprop_service s;
+	s.service = "nextprop";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	s.previous = previous;
+	s.buf = buf;
+	result = of_server(&s);
+	*flag = s.flag;
+	return result;
+}
+
+int
+of_setprop(int phandle, const char *name, void *buf, int len, int *size)
+{
+	int result;
+	static of_setprop_service s;
+	s.service = "setprop";
+	s.n_args = 4;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	s.name = name;
+	s.buf = buf;
+	s.len = len;
+	result = of_server(&s);
+	*size = s.size;
+	return result;
+}
+
+int
+of_canon(const char *device_specifier, void *buf, int buflen, int *length)
+{
+	int result;
+	static of_canon_service s;
+	s.service = "canon";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.device_specifier = device_specifier;
+	s.buf = buf;
+	s.buflen = buflen;
+	result = of_server(&s);
+	*length = s.length;
+	return result;
+}
+
+int
+of_finddevice(const char *device_specifier, int *phandle)
+{
+	int result;
+	static of_finddevice_service s;
+	s.service = "finddevice";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.device_specifier = device_specifier;
+	result = of_server(&s);
+	*phandle = s.phandle;
+	return result;
+}
+
+int
+of_instance_to_path(int ihandle, void *buf, int buflen, int *length)
+{
+	int result;
+	static of_instance_to_path_service s;
+	s.service = "instance-to-path";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.ihandle = ihandle;
+	s.buf = buf;
+	s.buflen = buflen;
+	result = of_server(&s);
+	*length = s.length;
+	return result;
+}
+
+int
+of_package_to_path(int phandle, void *buf, int buflen, int *length)
+{
+	int result;
+	static of_package_to_path_service s;
+	s.service = "package-to-path";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.phandle = phandle;
+	s.buf = buf;
+	s.buflen = buflen;
+	result = of_server(&s);
+	*length = s.length;
+	return result;
+}
+
+/* int of_call_method(const char *method, int ihandle, ...); */
+
+
+/* 6.3.2.3 Device I/O */
+
+
+int
+of_open(const char *device_specifier, int *ihandle)
+{
+	int result;
+	static of_open_service s;
+	s.service = "open";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.device_specifier = device_specifier;
+	result = of_server(&s);
+	*ihandle = s.ihandle;
+	return result;
+}
+
+int
+of_close(int ihandle)
+{
+	int result;
+	static of_close_service s;
+	s.service = "close";
+	s.n_args = 1;
+	s.n_returns = 0;
+	s.ihandle = ihandle;
+	result = of_server(&s);
+	return result;
+}
+
+int
+of_read(int ihandle, void *addr, int len, int *actual)
+{
+	int result;
+	static of_read_service s;
+	s.service = "read";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.ihandle = ihandle;
+	s.addr = addr;
+	s.len = len;
+	result = of_server(&s);
+	*actual = s.actual;
+	return result;
+}
+
+int
+of_write(int ihandle, void *addr, int len, int *actual)
+{
+	int result;
+	static of_write_service s;
+	s.service = "write";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.ihandle = ihandle;
+	s.addr = addr;
+	s.len = len;
+	result = of_server(&s);
+	*actual = s.actual;
+	return result;
+}
+
+int
+of_seek(int ihandle, int pos_hi, int pos_lo, int *status)
+{
+	int result;
+	static of_seek_service s;
+	s.service = "seek";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.ihandle = ihandle;
+	s.pos_hi = pos_hi;
+	s.pos_lo = pos_lo;
+	result = of_server(&s);
+	*status = s.status;
+	return result;
+}
+
+
+/* 6.3.2.4 Memory */
+
+
+int
+of_claim(void *virt, int size, int align, void **baseaddr)
+{
+	int result;
+	static of_claim_service s;
+	s.service = "claim";
+	s.n_args = 3;
+	s.n_returns = 1;
+	s.virt = virt;
+	s.size = size;
+	s.align = align;
+	result = of_server(&s);
+	*baseaddr = s.baseaddr;
+	return result;
+}
+
+int
+of_release(void *virt, int size)
+{
+	int result;
+	static of_release_service s;
+	s.service = "release";
+	s.n_args = 2;
+	s.n_returns = 0;
+	s.virt = virt;
+	s.size = size;
+	result = of_server(&s);
+	return result;
+}
+
+
+/* 6.3.2.5 Control transfer */
+
+
+int
+of_boot(const char *bootspec)
+{
+	int result;
+	static of_boot_service s;
+	s.service = "boot";
+	s.n_args = 1;
+	s.n_returns = 0;
+	s.bootspec = bootspec;
+	result = of_server(&s);
+	return result;
+}
+
+int
+of_enter(void)
+{
+	int result;
+	static of_enter_service s;
+	s.service = "enter";
+	s.n_args = 0;
+	s.n_returns = 0;
+	result = of_server(&s);
+	return result;
+}
+
+int
+of_exit(void)
+{
+	int result;
+	static of_exit_service s;
+	s.service = "exit";
+	s.n_args = 0;
+	s.n_returns = 0;
+	result = of_server(&s);
+	return result;
+}
+
+/* int of_chain(void *virt, int size, void *entry, void *args, int len); */
+
+
+/* 6.3.2.6 User interface */
+
+
+/* int of_interpret(const char *arg, ...); */
+
+int
+of_set_callback(void *newfunc, void **oldfunc)
+{
+	int result;
+	static of_set_callback_service s;
+	s.service = "set-callback";
+	s.n_args = 1;
+	s.n_returns = 1;
+	s.newfunc = newfunc;
+	result = of_server(&s);
+	*oldfunc = s.oldfunc;
+	return result;
+}
+
+int
+of_set_symbol_lookup(void *sym_to_value, void *value_to_sym)
+{
+	int result;
+	static of_set_symbol_lookup_service s;
+	s.service = "set-symbol-lookup";
+	s.n_args = 2;
+	s.n_returns = 0;
+	s.sym_to_value = sym_to_value;
+	s.value_to_sym = s.value_to_sym;
+	result = of_server(&s);
+	return result;
+}
+
+
+/* 6.3.2.7 Time */
+
+
+int
+of_milliseconds(int *ms)
+{
+	int result;
+	static of_milliseconds_service s;
+	s.service = "milliseconds";
+	s.n_args = 0;
+	s.n_returns = 1;
+	result = of_server(&s);
+	*ms = s.ms;
+	return result;
+}
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/of1275.h linux/arch/ppc/boot/of1275.h
--- v2.2.3/linux/arch/ppc/boot/of1275.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/of1275.h	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,421 @@
+/* 6.3.2.1 Client interface */
+
+
+typedef struct _of_test_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *name;
+	/*out*/
+	int missing;
+} of_test_service;
+
+int of_test(const char *name, int *missing);
+
+
+/* 6.3.2.2 Device tree */
+
+
+typedef struct _of_peer_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	/*out*/
+	int sibling_phandle;
+} of_peer_service;
+
+int of_peer(int phandle, int *sibling_phandle);
+
+
+typedef struct _of_child_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	/*out*/
+	int child_phandle;
+} of_child_service;
+
+int of_child(int phandle, int *child_phandle);
+
+
+typedef struct _of_parent_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	/*out*/
+	int parent_phandle;
+} of_parent_service;
+
+int of_child(int phandle, int *parent_phandle);
+
+
+typedef struct _of_instance_to_package_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int ihandle;
+	/*out*/
+	int phandle;
+} of_instance_to_package_service;
+
+int of_instance_to_package(int ihandle, int *phandle);
+
+
+typedef struct _of_getproplen_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	const char *name;
+	/*out*/
+	int proplen;
+} of_getproplen_service;
+
+int of_getproplen(int phandle, const char *name, int *proplen);
+
+
+typedef struct _of_getprop_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	const char *name;
+	void *buf;
+	int buflen;
+	/*out*/
+	int size;
+} of_getprop_service;
+
+int of_getprop(int phandle, const char *name, void *buf, int buflen,
+	       int *size);
+
+
+typedef struct _of_nextprop_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	const char *previous;
+	void *buf;
+	/*out*/
+	int flag;
+} of_nextprop_service;
+
+int of_nextprop(int phandle, const char *previous, void *buf, int *flag);
+
+
+typedef struct _of_setprop_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	const char *name;
+	void *buf;
+	int len;
+	/*out*/
+	int size;
+} of_setprop_service;
+
+int of_setprop(int phandle, const char *name, void *buf, int len, int *size);
+
+
+typedef struct _of_canon_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *device_specifier;
+	void *buf;
+	int buflen;
+	/*out*/
+	int length;
+} of_canon_service;
+
+int of_canon(const char *device_specifier, void *buf, int buflen, int *length);
+
+
+typedef struct _of_finddevice_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *device_specifier;
+	/*out*/
+	int phandle;
+} of_finddevice_service;
+
+int of_finddevice(const char *device_specifier, int *phandle);
+
+
+typedef struct _of_instance_to_path_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int ihandle;
+	void *buf;
+	int buflen;
+	/*out*/
+	int length;
+} of_instance_to_path_service;
+
+int of_instance_to_path(int ihandle, void *buf, int buflen, int *length);
+
+
+typedef struct _of_package_to_path_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int phandle;
+	void *buf;
+	int buflen;
+	/*out*/
+	int length;
+} of_package_to_path_service;
+
+int of_package_to_path(int phandle, void *buf, int buflen, int *length);
+
+
+typedef struct _of_call_method_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *method;
+	int ihandle;
+	/*...*/
+	int args[0]; 
+} of_call_method_service;
+
+int of_call_method(const char *method, int ihandle, ...);
+
+
+/* 6.3.2.3 Device I/O */
+
+
+typedef struct _of_open_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *device_specifier;
+	/*out*/
+	int ihandle;
+} of_open_service;
+
+int of_open(const char *device_specifier,
+	    int *ihandle);
+
+
+typedef struct _of_close_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int ihandle;
+	/*out*/
+} of_close_service;
+
+int of_close(int ihandle);
+
+
+typedef struct _of_read_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int ihandle;
+	void *addr;
+	int len;
+	/*out*/
+	int actual;
+} of_read_service;
+
+int of_read(int ihandle, void *addr, int len, int *actual);
+
+
+typedef struct _of_write_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int ihandle;
+	void *addr;
+	int len;
+	/*out*/
+	int actual;
+} of_write_service;
+
+int of_write(int ihandle, void *addr, int len, int *actual);
+
+
+typedef struct _of_seek_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	int ihandle;
+	int pos_hi;
+	int pos_lo;
+	/*out*/
+	int status;
+} of_seek_service;
+
+int of_seek(int ihandle, int pos_hi, int pos_lo, int *status);
+
+
+/* 6.3.2.4 Memory */
+
+
+typedef struct _of_claim_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	void *virt;
+	int size;
+	int align;
+	/*out*/
+	void *baseaddr;
+} of_claim_service;
+
+int of_claim(void *virt, int size, int align, void **baseaddr);
+
+
+typedef struct _of_release_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	void *virt;
+	int size;
+	int align;
+	/*out*/
+} of_release_service;
+
+int of_release(void *virt, int size);
+
+
+/* 6.3.2.5 Control transfer */
+
+
+typedef struct _of_boot_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *bootspec;
+	/*out*/
+} of_boot_service;
+
+int of_boot(const char *bootspec);
+
+
+typedef struct _of_enter_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	/*out*/
+} of_enter_service;
+
+int of_enter(void);
+
+
+typedef struct _of_exit_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	/*out*/
+} of_exit_service;
+
+int of_exit(void);
+
+
+typedef struct _of_chain_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	void *virt;
+	int size;
+	void *entry;
+	void *args;
+	int len;
+	/*out*/
+} of_chain_service;
+
+int of_chain(void *virt, int size, void *entry, void *args, int len);
+
+
+/* 6.3.2.6 User interface */
+
+
+typedef struct _of_interpret_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	const char *cmd;
+	int args[0];
+	/*...*/
+	/*out*/
+	/*...*/
+} of_interpret_service;
+
+int of_interpret(const char *arg, ...);
+
+
+typedef struct _of_set_callback_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	void *newfunc;
+	/*out*/
+	void *oldfunc;
+} of_set_callback_service;
+
+int of_set_callback(void *newfunc, void **oldfunc);
+
+
+typedef struct _of_set_symbol_lookup_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	void *sym_to_value;
+	void *value_to_sym;
+	/*out*/
+} of_set_symbol_lookup_service;
+
+int of_set_symbol_lookup(void *sym_to_value, void *value_to_sym);
+
+
+/* 6.3.2.7 Time */
+
+
+typedef struct _of_milliseconds_service {
+	const char *service;
+	int n_args;
+	int n_returns;
+	/*in*/
+	/*out*/
+	int ms;
+} of_milliseconds_service;
+
+int of_milliseconds(int *ms);
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/boot/piggyback.c linux/arch/ppc/boot/piggyback.c
--- v2.2.3/linux/arch/ppc/boot/piggyback.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/boot/piggyback.c	Wed Dec 31 16:00:00 1969
@@ -1,64 +0,0 @@
-#include <stdio.h>
-
-extern long ce_exec_config[];
-
-main(int argc, char *argv[])
-{
-	int i, cnt, pos, len;
-	unsigned int cksum, val;
-	unsigned char *lp;
-	unsigned char buf[8192];
-	if (argc != 1)
-	{
-		fprintf(stderr, "usage: %s <in-file >out-file\n", argv[0]);
-		exit(1);
-	}
-	fprintf(stdout, "#\n");
-	fprintf(stdout, "# Miscellaneous data structures:\n");
-	fprintf(stdout, "# WARNING - this file is automatically generated!\n");
-	fprintf(stdout, "#\n");
-	fprintf(stdout, "\n");
-	fprintf(stdout, "\t.data\n");
-	fprintf(stdout, "\t.globl input_data\n");
-	fprintf(stdout, "input_data:\n");
-	pos = 0;
-	cksum = 0;
-	while ((len = read(0, buf, sizeof(buf))) > 0)
-	{
-		cnt = 0;
-		lp = (unsigned char *)buf;
-		len = (len + 3) & ~3;  /* Round up to longwords */
-		for (i = 0;  i < len;  i += 4)
-		{
-			if (cnt == 0)
-			{
-				fprintf(stdout, "\t.long\t");
-			}
-			fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]);
-			val = *(unsigned long *)lp;
-			cksum ^= val;
-			lp += 4;
-			if (++cnt == 4)
-			{
-				cnt = 0;
-				fprintf(stdout, " # %x \n", pos+i-12);
-				fflush(stdout);
-			} else
-			{
-				fprintf(stdout, ",");
-			}
-		}
-		if (cnt)
-		{
-			fprintf(stdout, "0\n");
-		}
-		pos += len;
-	}
-	fprintf(stdout, "\t.globl input_len\n");
-	fprintf(stdout, "input_len:\t.long\t0x%x\n", pos);
-	fflush(stdout);
-	fclose(stdout);
-	fprintf(stderr, "cksum = %x\n", cksum);
-	exit(0);
-}
-
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/chrpboot/Makefile linux/arch/ppc/chrpboot/Makefile
--- v2.2.3/linux/arch/ppc/chrpboot/Makefile	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/chrpboot/Makefile	Fri Mar 19 10:50:03 1999
@@ -28,6 +28,12 @@
 CONFIG_CHRP = y
 endif
 
+ifeq ($(CONFIG_SMP),y)
+TFTPIMAGE=/tftpboot/zImage.chrp.smp
+else
+TFTPIMAGE=/tftpboot/zImage.chrp
+endif
+
 all:	$(TOPDIR)/zImage
 
 #
@@ -36,10 +42,10 @@
 #
 ifeq ($(CONFIG_CHRP),y)
 znetboot: zImage
-	cp zImage /tftpboot/zImage.chrp
+	cp zImage $(TFTPIMAGE)
 
 znetboot.initrd: zImage.initrd
-	cp zImage.initrd /tftpboot/zImage.chrp
+	cp zImage.initrd $(TFTPIMAGE)
 
 floppy: zImage
 	mcopy zImage a:zImage
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/coffboot/Makefile linux/arch/ppc/coffboot/Makefile
--- v2.2.3/linux/arch/ppc/coffboot/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/coffboot/Makefile	Fri Mar 19 10:50:03 1999
@@ -23,6 +23,12 @@
 CONFIG_PMAC = y
 endif
 
+ifeq ($(CONFIG_SMP),y)
+TFTPIMAGE=/tftpboot/zImage.pmac.smp
+else
+TFTPIMAGE=/tftpboot/zImage.pmac
+endif
+
 ifeq ($(CONFIG_PMAC),y)
 hack-coff: hack-coff.c
 	$(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c
@@ -33,10 +39,10 @@
 #	umount /mnt
 
 znetboot: vmlinux.coff
-	cp vmlinux.coff /tftpboot/zImage.pmac
+	cp vmlinux.coff $(TFTPIMAGE)
 
 znetboot.initrd: vmlinux.coff.initrd
-	cp vmlinux.coff.initrd /tftpboot/zImage.pmac
+	cp vmlinux.coff.initrd $(TFTPIMAGE)
 
 coffboot: $(OBJS) ld.script
 	$(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS)
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig
--- v2.2.3/linux/arch/ppc/common_defconfig	Wed Mar 10 15:29:45 1999
+++ linux/arch/ppc/common_defconfig	Fri Mar 19 10:50:03 1999
@@ -21,7 +21,7 @@
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
+# CONFIG_MODVERSIONS is not set
 CONFIG_KMOD=y
 CONFIG_PCI=y
 # CONFIG_PCI_QUIRKS is not set
@@ -32,10 +32,10 @@
 # CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
-# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_MISC=m
 # CONFIG_BINFMT_JAVA is not set
 # CONFIG_PARPORT is not set
-# CONFIG_VGA_CONSOLE is not set
+CONFIG_VGA_CONSOLE=y
 CONFIG_FB=y
 CONFIG_FB_COMPAT_XPMAC=y
 CONFIG_PMAC_PBOOK=y
@@ -64,14 +64,14 @@
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
+CONFIG_BLK_DEV_IDEFLOPPY=y
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
-CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_BLK_DEV_SL82C105 is not set
 # CONFIG_IDE_CHIPSETS is not set
-CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
@@ -84,26 +84,29 @@
 #
 # Networking options
 #
-# CONFIG_PACKET is not set
-# CONFIG_NETLINK is not set
+CONFIG_PACKET=y
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
 # CONFIG_FIREWALL is not set
 # CONFIG_FILTER is not set
 CONFIG_UNIX=y
 CONFIG_INET=y
-# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_MULTICAST=y
 # CONFIG_IP_ADVANCED_ROUTER is not set
 # CONFIG_IP_PNP is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
-CONFIG_SYN_COOKIES=y
-# CONFIG_INET_RARP is not set
-# CONFIG_IP_NOSR is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_RARP=y
+CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 # CONFIG_IPX is not set
-# CONFIG_ATALK is not set
+CONFIG_ATALK=m
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
@@ -124,12 +127,12 @@
 #
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
 # CONFIG_CHR_DEV_SG is not set
 # CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
+CONFIG_SCSI_CONSTANTS=y
 # CONFIG_SCSI_LOGGING is not set
 
 #
@@ -140,7 +143,10 @@
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
-# CONFIG_SCSI_AIC7XXX is not set
+CONFIG_SCSI_AIC7XXX=y
+# CONFIG_OVERRIDE_CMDS is not set
+CONFIG_AIC7XXX_PROC_STATS=y
+CONFIG_AIC7XXX_RESET_DELAY=15
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
@@ -158,10 +164,10 @@
 # CONFIG_SCSI_NCR53C7xx is not set
 CONFIG_SCSI_NCR53C8XX=y
 CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
-CONFIG_SCSI_NCR53C8XX_SYNC=5
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
+CONFIG_SCSI_NCR53C8XX_SYNC=20
 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
+# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
@@ -186,6 +192,7 @@
 # CONFIG_ARCNET is not set
 # CONFIG_DUMMY is not set
 # CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
 CONFIG_NET_ETHERNET=y
 CONFIG_MACE=y
 CONFIG_BMAC=y
@@ -198,7 +205,7 @@
 # CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
-CONFIG_PCNET32=m
+# CONFIG_PCNET32 is not set
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -218,16 +225,17 @@
 # CONFIG_FDDI is not set
 # CONFIG_HIPPI is not set
 # CONFIG_DLCI is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
 CONFIG_PPP=y
-CONFIG_SLIP=m
-# CONFIG_SLIP_COMPRESSED is not set
-# CONFIG_SLIP_SMART is not set
-# CONFIG_SLIP_MODE_SLIP6 is not set
+# CONFIG_SLIP is not set
 # CONFIG_NET_RADIO is not set
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
 # CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 
 #
 # Amateur Radio support
@@ -240,7 +248,7 @@
 # CONFIG_ISDN is not set
 
 #
-# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+# Old CD-ROM drivers (not SCSI, not IDE)
 #
 # CONFIG_CD_NO_IDESCSI is not set
 
@@ -266,9 +274,9 @@
 CONFIG_FBCON_CFB32=y
 # CONFIG_FBCON_FONTWIDTH8_ONLY is not set
 CONFIG_FBCON_FONTS=y
-CONFIG_FONT_8x8=y
+# CONFIG_FONT_8x8 is not set
 CONFIG_FONT_8x16=y
-# CONFIG_FONT_SUN8x16 is not set
+CONFIG_FONT_SUN8x16=y
 CONFIG_FONT_SUN12x22=y
 # CONFIG_FONT_6x11 is not set
 # CONFIG_FONT_PEARL_8x8 is not set
@@ -279,21 +287,12 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_UNIX98_PTYS is not set
-CONFIG_MOUSE=y
-
-#
-# Mice
-#
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+# CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_NVRAM is not set
@@ -318,20 +317,21 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS_FS=y
 # CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=y
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=m
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
 # CONFIG_QNX4FS_FS is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_EXT2_FS=y
@@ -343,7 +343,7 @@
 #
 # CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
-CONFIG_NFSD=m
+CONFIG_NFSD=y
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
@@ -363,7 +363,7 @@
 #
 # Native Language Support
 #
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=y
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
 # CONFIG_NLS_CODEPAGE_850 is not set
@@ -401,30 +401,4 @@
 # CONFIG_SOUND_SONICVIBES is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
-CONFIG_SOUND_OSS=y
-# CONFIG_SOUND_PAS is not set
-# CONFIG_SOUND_SB is not set
-# CONFIG_SOUND_ADLIB is not set
-# CONFIG_SOUND_GUS is not set
-# CONFIG_SOUND_MPU401 is not set
-# CONFIG_SOUND_PSS is not set
-# CONFIG_SOUND_MSS is not set
-# CONFIG_SOUND_SSCAPE is not set
-# CONFIG_SOUND_TRIX is not set
-# CONFIG_SOUND_MAD16 is not set
-# CONFIG_SOUND_WAVEFRONT is not set
-# CONFIG_SOUND_CS4232 is not set
-# CONFIG_SOUND_OPL3SA2 is not set
-# CONFIG_SOUND_MAUI is not set
-# CONFIG_SOUND_SGALAXY is not set
-# CONFIG_SOUND_AD1816 is not set
-# CONFIG_SOUND_OPL3SA1 is not set
-# CONFIG_SOUND_SOFTOSS is not set
-# CONFIG_SOUND_YM3812 is not set
-# CONFIG_SOUND_VMIDI is not set
-# CONFIG_SOUND_UART6850 is not set
-
-#
-# Additional low level sound drivers
-#
-# CONFIG_LOWLEVEL_SOUND is not set
+# CONFIG_SOUND_OSS is not set
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.2.3/linux/arch/ppc/config.in	Tue Feb 23 15:21:32 1999
+++ linux/arch/ppc/config.in	Wed Mar 10 21:30:31 1999
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.80 1998/11/11 03:54:56 paulus Exp $
+# $Id: config.in,v 1.84 1999/02/23 08:08:38 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -172,7 +172,7 @@
 comment 'Sound'
 tristate 'Sound card support' CONFIG_SOUND
 if [ "$CONFIG_SOUND" != "n" ]; then
-  tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND
+  dep_tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND $CONFIG_SOUND
   source drivers/sound/Config.in
 fi
 
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.2.3/linux/arch/ppc/defconfig	Wed Mar 10 15:29:45 1999
+++ linux/arch/ppc/defconfig	Wed Mar 10 21:30:31 1999
@@ -262,6 +262,7 @@
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
 # CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 
 #
 # Amateur Radio support
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- v2.2.3/linux/arch/ppc/kernel/chrp_pci.c	Thu Aug  6 14:06:29 1998
+++ linux/arch/ppc/kernel/chrp_pci.c	Fri Mar 19 10:50:03 1999
@@ -170,6 +170,79 @@
         return PCIBIOS_SUCCESSFUL;
 }
 
+#define python_config_address(bus) (unsigned *)((0xfef00000+0xf8000)-(bus*0x100000))
+#define python_config_data(bus) ((0xfef00000+0xf8010)-(bus*0x100000))
+#define PYTHON_CFA(b, d, o)	(0x80 | ((b) << 8) | ((d) << 16) \
+				 | (((o) & ~3) << 24))
+     
+int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned char *val)
+{
+	if (bus > 2) {
+		*val = 0xff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) );
+	*val = in_8((unsigned char *)python_config_data(bus) + (offset&3));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned short *val)
+{
+	if (bus > 2) {
+		*val = 0xffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) );
+	*val = in_le16((unsigned short *)(python_config_data(bus) + (offset&3)));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned int *val)
+{
+	if (bus > 2) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) );
+	*val = in_le32((unsigned *)python_config_data(bus));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned char val)
+{
+	if (bus > 2)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) );
+	out_8((volatile unsigned char *)python_config_data(bus) + (offset&3), val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned short val)
+{
+	if (bus > 2)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) );
+	out_le16((volatile unsigned short *)python_config_data(bus) + (offset&3),
+		 val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+				      unsigned char offset, unsigned int val)
+{
+	if (bus > 2)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset) );
+	out_le32((unsigned *)python_config_data(bus) + (offset&3), val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
     /*
      *  Temporary fixes for PCI devices. These should be replaced by OF query
      *  code -- Geert
@@ -214,77 +287,4 @@
 	OpenPIC_InitSenses = hydra_openpic_initsenses;
 	OpenPIC_NumInitSenses = sizeof(hydra_openpic_initsenses);
 	return 1;
-}
-
-
-extern int chrp_ide_irq;
-
-__initfunc(int w83c553f_init(void))
-{
-    u_char bus, dev;
-#if 0    
-    unsigned char t8;
-    unsigned short t16;
-#endif    
-    unsigned int t32;
-    struct pci_dev *pdev;
-    if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND,
-				PCI_DEVICE_ID_WINBOND_83C553, NULL))) {
-	bus = pdev->bus->number;
-	dev = pdev->devfn + 1;
-	pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
-	if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) {
-#if 0
-	    printk("Enabling SL82C105 IDE on W83C553F\n");
-	    /*
-	     *  FIXME: this doesn't help :-(
-	     */
-
-	    /* I/O mapping */
-	    pcibios_read_config_word(bus, dev, PCI_COMMAND, &t16);
-	    t16 |= PCI_COMMAND_IO;
-	    pcibios_write_config_word(bus, dev, PCI_COMMAND, t16);
-
-	    /* Standard IDE registers */
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,
-					    0xffffffff);
-	    pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,
-					    0x000001f0 | 1);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1,
-					    0xffffffff);
-	    pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1,
-					    0x000003f4 | 1);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2,
-					    0xffffffff);
-	    pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_2, &t32);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2,
-					    0x00000170 | 1);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3,
-					    0xffffffff);
-	    pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_3, &t32);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3,
-					    0x00000374 | 1);
-
-	    /* IDE Bus Master Control */
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4,
-					    0xffffffff);
-	    pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_4, &t32);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4,
-					    0x1000 | 1);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5,
-					    0xffffffff);
-	    pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_5, &t32);
-	    pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5,
-					    0x1010 | 1);
-
-	    /* IDE Interrupt */
-	    pcibios_read_config_byte(bus, dev, PCI_INTERRUPT_LINE, &t8);
-	    chrp_ide_irq = t8;
-#endif
-	    return 1;
-	}
-    }
-    return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.2.3/linux/arch/ppc/kernel/chrp_setup.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/chrp_setup.c	Fri Mar 19 10:50:03 1999
@@ -31,6 +31,7 @@
 #include <linux/ioport.h>
 #include <linux/console.h>
 #include <linux/pci.h>
+#include <linux/openpic.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -42,7 +43,6 @@
 #include <asm/pci-bridge.h>
 
 extern void hydra_init(void);
-extern void w83c553f_init(void);
 
 /* for the mac fs */
 kdev_t boot_dev;
@@ -53,7 +53,6 @@
 extern unsigned long loops_per_sec;
 
 unsigned long empty_zero_page[1024];
-extern unsigned char aux_device_present;
 
 #ifdef CONFIG_BLK_DEV_RAM
 extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
@@ -88,46 +87,53 @@
 	    model = get_property(root, "model", NULL);
 	len = sprintf(buffer,"machine\t\t: CHRP %s\n", model);
 
-	/* VLSI VAS96011/12 `Golden Gate 2' */
-	/* Memory banks */
-	sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_CTRL))
-		   >>31) & 1;
-	for (i = 0; i < (sdramen ? 4 : 6); i++) {
-	    t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_BANK0+
-				     i*4));
-	    if (!(t & 1))
-		continue;
-	    switch ((t>>8) & 0x1f) {
-		case 0x1f:
-		    model = "4 MB";
-		    break;
-		case 0x1e:
-		    model = "8 MB";
-		    break;
-		case 0x1c:
-		    model = "16 MB";
-		    break;
-		case 0x18:
-		    model = "32 MB";
-		    break;
-		case 0x10:
-		    model = "64 MB";
-		    break;
-		case 0x00:
-		    model = "128 MB";
-		    break;
-		default:
-		    model = "Reserved";
-		    break;
-	    }
-	    len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model,
-			   gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]);
+	/* longtrail (goldengate) stuff */
+	if ( !strncmp( model, "IBM,LongTrail", 9 ) )
+	{
+		/* VLSI VAS96011/12 `Golden Gate 2' */
+		/* Memory banks */
+		sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+
+						GG2_PCI_DRAM_CTRL))
+			   >>31) & 1;
+		for (i = 0; i < (sdramen ? 4 : 6); i++) {
+			t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+
+						 GG2_PCI_DRAM_BANK0+
+						 i*4));
+			if (!(t & 1))
+				continue;
+			switch ((t>>8) & 0x1f) {
+			case 0x1f:
+				model = "4 MB";
+				break;
+			case 0x1e:
+				model = "8 MB";
+				break;
+			case 0x1c:
+				model = "16 MB";
+				break;
+			case 0x18:
+				model = "32 MB";
+				break;
+			case 0x10:
+				model = "64 MB";
+				break;
+			case 0x00:
+				model = "128 MB";
+				break;
+			default:
+				model = "Reserved";
+				break;
+			}
+			len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model,
+				       gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]);
+		}
+		/* L2 cache */
+		t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL));
+		len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n",
+			       gg2_cachesizes[(t>>7) & 3],
+			       gg2_cachetypes[(t>>2) & 3],
+			       gg2_cachemodes[t & 3]);
 	}
-	/* L2 cache */
-	t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL));
-	len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n",
-		       gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3],
-		       gg2_cachemodes[t & 3]);
 	return len;
 }
 
@@ -190,8 +196,6 @@
 	/* init to some ~sane value until calibrate_delay() runs */
 	loops_per_sec = 50000000;
 	
-	aux_device_present = 0xaa;
-
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* this is fine for chrp */
 	initrd_below_start_ok = 1;
@@ -219,8 +223,15 @@
 	 *  -- Geert
 	 */
 	hydra_init();		/* Mac I/O */
-	w83c553f_init();	/* PCI-ISA bridge and IDE */
 
+	/* Some IBM machines don't have the hydra -- Cort */
+	if ( !OpenPIC )
+	{
+		OpenPIC = (struct OpenPIC *)*(unsigned long *)get_property(
+			find_path_device("/"), "platform-open-pic", NULL);
+		OpenPIC = ioremap((unsigned long)OpenPIC, sizeof(struct OpenPIC));
+	}
+	
 	/*
 	 *  Fix the Super I/O configuration
 	 */
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/chrp_time.c linux/arch/ppc/kernel/chrp_time.c
--- v2.2.3/linux/arch/ppc/kernel/chrp_time.c	Fri May  8 23:14:44 1998
+++ linux/arch/ppc/kernel/chrp_time.c	Fri Mar 19 10:50:03 1999
@@ -154,7 +154,8 @@
 __initfunc(void chrp_calibrate_decr(void))
 {
 	struct device_node *cpu;
-	int freq, *fp, divisor;
+	int *fp, divisor;
+	unsigned long freq;
 
 	if (via_calibrate_decr())
 		return;
@@ -170,10 +171,9 @@
 		if (fp != 0)
 			freq = *fp;
 	}
-
 	freq *= 60;	/* try to make freq/1e6 an integer */
         divisor = 60;
-        printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+        printk("time_init: decrementer frequency = %lu/%d\n", freq, divisor);
         decrementer_count = freq / HZ / divisor;
         count_period_num = divisor;
         count_period_den = freq / 1000000;
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c
--- v2.2.3/linux/arch/ppc/kernel/feature.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/feature.c	Wed Mar 10 21:30:31 1999
@@ -41,7 +41,6 @@
 	OH_BAY_FLOPPY_ENABLE,	/* FEATURE_Mediabay_floppy_enable */
 	0,			/* FEATURE_BMac_reset */
 	0,			/* FEATURE_BMac_IO_enable */
-	0,			/* FEATURE_Modem_PowerOn -> guess...*/
 	0			/* FEATURE_Modem_Reset -> guess...*/
 };
 
@@ -64,8 +63,7 @@
 	OH_BAY_FLOPPY_ENABLE,	/* FEATURE_Mediabay_floppy_enable */
 	0x80000000,		/* FEATURE_BMac_reset */
 	0x60000000,		/* FEATURE_BMac_IO_enable */
-	0x02000000,		/* FEATURE_Modem_PowerOn -> guess...*/
-	0x07000000		/* FEATURE_Modem_Reset -> guess...*/
+	0x02000000		/* FEATURE_Modem_Reset -> guess...*/
 };
 
 /* definition of a feature controller object */
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.2.3/linux/arch/ppc/kernel/head.S	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/kernel/head.S	Fri Mar 19 10:50:03 1999
@@ -1,7 +1,7 @@
 /*
  *  arch/ppc/kernel/head.S
  *
- *  $Id: head.S,v 1.114 1998/12/28 10:28:45 paulus Exp $
+ *  $Id: head.S,v 1.121 1999/03/16 10:40:29 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -44,8 +44,13 @@
 /* optimization for 603 to load the tlb directly from the linux table */
 #define NO_RELOAD_HTAB 1
 	
+#ifndef CONFIG_8xx
 CACHE_LINE_SIZE = 32
 LG_CACHE_LINE_SIZE = 5
+#else
+CACHE_LINE_SIZE = 16
+LG_CACHE_LINE_SIZE = 4
+#endif
 
 #define TOPHYS(x)	(x - KERNELBASE)
 
@@ -81,7 +86,6 @@
 	sync; \
 	isync
 
-/* This instruction is not implemented on the PPC 603 or 601 */
 #ifndef CONFIG_8xx
 /* This instruction is not implemented on the PPC 603 or 601 */
 #define tlbia \
@@ -212,6 +216,7 @@
 	mr	r29,r5
 	mr	r28,r6
 	mr	r27,r7
+	li	r24,0			/* cpu # */
 #ifndef CONFIG_8xx
 	bl	prom_init
 	.globl	__secondary_start
@@ -236,15 +241,33 @@
 	mtspr	IBAT1L,r10
 	b	5f
 4:
-#ifndef CONFIG_APUS
-	ori	r11,r11,0x1fe		/* set up BAT registers for 604 */
-	li	r8,2			/* R/W access */
-#else
+#ifdef CONFIG_APUS
+	ori	r11,r11,BL_8M<<2|0x2	/* set up an 8MB mapping */
 	ori	r11,r11,0xfe		/* set up an 8MB mapping */
 	lis	r8,CYBERBASEp@h
 	lwz	r8,0(r8)
 	addis	r8,r8,KERNELBASE@h
 	addi	r8,r8,2
+#else
+	ori	r11,r11,BL_256M<<2|0x2	/* set up BAT registers for 604 */
+	li	r8,2			/* R/W access */
+	/* 
+	 * allow secondary cpus to get at all of ram in early bootup
+	 * since their init_task may be up there -- Cort
+	 */
+	oris	r18,r8,0x10000000@h
+	oris	r21,r11,(KERNELBASE+0x10000000)@h
+	mtspr	DBAT1L,r18		/* N.B. 6xx (not 601) have valid */
+	mtspr	DBAT1U,r21		/* bit in upper BAT register */
+	mtspr	IBAT1L,r18
+	mtspr	IBAT1U,r21
+	
+	oris	r18,r8,0x20000000@h
+	oris	r21,r11,(KERNELBASE+0x20000000)@h
+	mtspr	DBAT2L,r18		/* N.B. 6xx (not 601) have valid */
+	mtspr	DBAT2U,r21		/* bit in upper BAT register */
+	mtspr	IBAT2L,r28
+	mtspr	IBAT2U,r21
 #endif
 	mtspr	DBAT0L,r8		/* N.B. 6xx (not 601) have valid */
 	mtspr	DBAT0U,r11		/* bit in upper BAT register */
@@ -327,20 +350,28 @@
 	lis	r8, MI_Kp@h		/* Set the protection mode */
 	mtspr	MI_AP, r8
 	mtspr	MD_AP, r8
-#ifdef CONFIG_MBX
+
+/* We will get these from a configuration file as soon as I verify
+ * the extraneous bits don't cause problems in the TLB.
+ */
+#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE)
+#define BOOT_IMMR      0xfa000000
+#endif
+#ifdef CONFIG_BSEIP
+#define BOOT_IMMR      0xff000000
+#endif
 	/* Map another 8 MByte at 0xfa000000 to get the processor
 	 * internal registers (among other things).
 	 */
-	lis	r8, 0xfa000000@h	/* Create vaddr for TLB */
+	lis     r8, BOOT_IMMR@h         /* Create vaddr for TLB */
 	ori	r8, r8, MD_EVALID	/* Mark it valid */
 	mtspr	MD_EPN, r8
 	li	r8, MD_PS8MEG		/* Set 8M byte page */
 	ori	r8, r8, MD_SVALID	/* Make it valid */
 	mtspr	MD_TWC, r8
-	lis	r8, 0xfa000000@h	/* Create paddr for TLB */
+	lis     r8, BOOT_IMMR@h         /* Create paddr for TLB */
 	ori	r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */
 	mtspr	MD_RPN, r8
-#endif
 
 	/* Since the cache is enabled according to the information we
 	 * just loaded into the TLB, invalidate and enable the caches here.
@@ -354,9 +385,8 @@
 #if 0
 	mtspr	DC_CST, r8
 #else
-	/* I still have a bug somewhere because the Ethernet driver
-	 * does not want to work with copyback enabled.  For now,
-	 * at least enable write through.
+	/* For a debug option, I left this here to easily enable
+	 * the write through cache mode
 	 */
 	lis	r8, DC_SFWT@h
 	mtspr	DC_CST, r8
@@ -442,7 +472,11 @@
 	.long	int_return
 
 /* System reset */
+#ifdef CONFIG_SMP /* MVME/MTX start the secondary here */
+	STD_EXCEPTION(0x100, Reset, __secondary_start_psurge)
+#else
 	STD_EXCEPTION(0x100, Reset, UnknownException)
+#endif	
 
 /* Machine check */
 	STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
@@ -1148,6 +1182,8 @@
 	mflr	r23
 	andi.	r24,r23,0x3f00		/* get vector offset */
 	stw	r24,TRAP(r21)
+	li	r22,RESULT
+	stwcx.	r22,r22,r21		/* to clear the reservation */
 	li	r22,0
 	stw	r22,RESULT(r21)
 	mtspr	SPRG2,r22		/* r1 is now kernel sp */
@@ -1155,7 +1191,7 @@
 	cmplw	0,r1,r2
 	cmplw	1,r1,r24
 	crand	1,1,4
-	bgt	stack_ovf		/* if r2 < r1 < r2+TASK_STRUCT_SIZE */
+	bgt-	stack_ovf		/* if r2 < r1 < r2+TASK_STRUCT_SIZE */
 	lwz	r24,0(r23)		/* virtual address of handler */
 	lwz	r23,4(r23)		/* where to go when done */
 	mtspr	SRR0,r24
@@ -1204,13 +1240,10 @@
 Hash_bits = 12				/* e.g. 256kB hash table */
 Hash_msk = (((1 << Hash_bits) - 1) * 64)
 	
-	.globl	hash_table_lock
-hash_table_lock:
-.long	0
-	
 	.globl	hash_page
 hash_page:
 #ifdef __SMP__
+	eieio
 	lis	r2,hash_table_lock@h
 	ori	r2,r2,hash_table_lock@l
 	tophys(r2,r2,r6)
@@ -1226,7 +1259,7 @@
 12:	cmpw	r6,r0
 	bdnzf	2,10b
 	tw	31,31,31
-11:
+11:	eieio
 #endif
 	/* Get PTE (linux-style) and check access */
 	lwz	r5,PG_TABLES(r5)		
@@ -1234,13 +1267,25 @@
 	rlwimi	r5,r3,12,20,29		/* insert top 10 bits of address */
 	lwz	r5,0(r5)		/* get pmd entry */
 	rlwinm.	r5,r5,0,0,19		/* extract address of pte page */
+#ifdef __SMP__
 	beq-	hash_page_out		/* return if no mapping */
+#else
+	/* XXX it seems like the 601 will give a machine fault on the
+	   rfi if its alignment is wrong (bottom 4 bits of address are
+	   8 or 0xc) and we have had a not-taken conditional branch
+	   to the address following the rfi. */
+	beqlr-
+#endif
 	tophys(r2,r5,r2)
 	rlwimi	r2,r3,22,20,29		/* insert next 10 bits of address */
 	lwz	r6,0(r2)		/* get linux-style pte */
 	ori	r4,r4,1			/* set _PAGE_PRESENT bit in access */
 	andc.	r0,r4,r6		/* check access & ~permission */
+#ifdef __SMP__
 	bne-	hash_page_out		/* return if access not permitted */
+#else
+	bnelr-
+#endif
 
 	ori	r6,r6,0x100		/* set _PAGE_ACCESSED in pte */
 	rlwinm	r5,r4,5,24,24		/* _PAGE_RW access -> _PAGE_DIRTY */
@@ -1257,7 +1302,9 @@
 	/* Construct the high word of the PPC-style PTE */
 	mfsrin	r5,r3			/* get segment reg for segment */
 	rlwinm	r5,r5,7,1,24		/* put VSID in 0x7fffff80 bits */
+#ifndef __SMP__				/* do this later for SMP */
 	oris	r5,r5,0x8000		/* set V (valid) bit */
+#endif
 	rlwimi	r5,r3,10,26,31		/* put in API (abbrev page index) */
 
 	/* Get the address of the primary PTE group in the hash table */
@@ -1274,9 +1321,6 @@
 	li	r2,8			/* PTEs/group */
 	bne	10f			/* no PTE: go look for an empty slot */
 	tlbie	r3			/* invalidate TLB entry */
-#ifdef __SMP__
-	tlbsync
-#endif
 
 	/* Search the primary PTEG for a PTE whose 1st word matches r5 */
 	mtctr	r2
@@ -1345,18 +1389,43 @@
 	addi	r4,r4,1
 	stw	r4,0(r2)
 
+#ifndef __SMP__
 	/* Store PTE in PTEG */
 found_empty:
 	stw	r5,0(r3)
 found_slot:
 	stw	r6,4(r3)
-	SYNC
+	sync
+
+#else /* __SMP__ */
 /*
- * These nop's seem to be necessary to avoid getting a machine
- * check on the rfi on 601 processors.
+ * Between the tlbie above and updating the hash table entry below,
+ * another CPU could read the hash table entry and put it in its TLB.
+ * There are 3 cases:
+ * 1. using an empty slot
+ * 2. updating an earlier entry to change permissions (i.e. enable write)
+ * 3. taking over the PTE for an unrelated address
+ *
+ * In each case it doesn't really matter if the other CPUs have the old
+ * PTE in their TLB.  So we don't need to bother with another tlbie here,
+ * which is convenient as we've overwritten the register that had the
+ * address. :-)  The tlbie above is mainly to make sure that this CPU comes
+ * and gets the new PTE from the hash table.
+ *
+ * We do however have to make sure that the PTE is never in an invalid
+ * state with the V bit set.
  */
-	nop
-	nop
+found_empty:
+found_slot:
+	stw	r5,0(r3)	/* clear V (valid) bit in PTE */
+	sync
+	tlbsync
+	sync
+	stw	r6,4(r3)	/* put in correct RPN, WIMG, PP bits */
+	sync
+	oris	r5,r5,0x8000
+	stw	r5,0(r3)	/* finally set V bit in PTE */
+#endif /* __SMP__ */
 
 /*
  * Update the hash table miss count.  We only want misses here
@@ -1380,6 +1449,7 @@
 	tophys(r2,r2,r6)
 	li	r0,0
 	stw	r0,hash_table_lock@l(r2)
+	eieio
 #endif
 
 	/* Return from the exception */
@@ -1398,17 +1468,22 @@
 	REST_GPR(20, r21)
 	REST_2GPRS(22, r21)
 	lwz	r21,GPR21(r21)
-	SYNC
 	rfi
 	
-hash_page_out:
 #ifdef __SMP__
+hash_page_out:
 	lis	r2,hash_table_lock@ha
 	tophys(r2,r2,r6)
 	li	r0,0
 	stw	r0,hash_table_lock@l(r2)
-#endif		
+	eieio
 	blr
+
+	.globl	hash_table_lock
+hash_table_lock:
+	.long	0
+#endif
+
 next_slot:
 	.long	0
 
@@ -1600,10 +1675,38 @@
 	. = 0x4000
 #endif
 
+#ifdef CONFIG_SMP
+	.globl	__secondary_start_psurge
+__secondary_start_psurge:
+	li	r24,1			/* cpu # */
+	b	__secondary_start
+
+	.globl	__secondary_hold
+__secondary_hold:
+	/* tell the master we're here */
+	lis	r5,0x4@h
+	ori	r5,r5,0x4@l
+	stw	r3,0(r5)
+	dcbf	0,r5
+100:
+	lis	r5,0
+	dcbi	0,r5
+	lwz	r4,0(r5)
+	/* wait until we're told to start */
+	cmp	0,r4,r3
+	bne	100b
+	/* our cpu # was at addr 0 - go */
+	lis	r5,__secondary_start@h
+	ori	r5,r5,__secondary_start@l
+	tophys(r5,r5,r4)
+	mtlr	r5
+	mr	r24,r3			/* cpu # */
+	blr
+#endif /* CONFIG_SMP */
+	
 /*
  * This is where the main kernel code starts.
  */
-
 start_here:
 #ifndef CONFIG_8xx
 	/*
@@ -1650,9 +1753,9 @@
 	/* get current */
 	lis	r2,current_set@h
 	ori	r2,r2,current_set@l
-	addi	r2,r2,4
+	slwi	r24,r24,2			/* cpu # to current_set[cpu#] */
+	add	r2,r2,r24
 	lwz	r2,0(r2)
-	
 	b	10f
 99:	
 #endif /* __SMP__ */
@@ -1677,12 +1780,10 @@
 #ifdef __SMP__	
 10:
 #endif /* __SMP__ */
-	
 	/* stack */
 	addi	r1,r2,TASK_UNION_SIZE
 	li	r0,0
 	stwu	r0,-STACK_FRAME_OVERHEAD(r1)
-	
 /*
  * Decide what sort of machine this is and initialize the MMU.
  */
@@ -1693,7 +1794,6 @@
 	mr	r7,r27
 	bl	identify_machine
 	bl	MMU_init
-	
 /*
  * Go back to running unmapped so we can load up new values
  * for SDR1 (hash table pointer) and the segment registers
@@ -1725,8 +1825,10 @@
 2:
 	SYNC			/* Force all PTE updates to finish */
 	tlbia			/* Clear all TLB entries */
+	sync			/* wait for tlbia/tlbie to finish */
 #ifdef __SMP__
-	tlbsync
+	tlbsync			/* ... on all CPUs */
+	sync
 #endif
 #ifndef CONFIG_8xx
 	mtspr	SDR1,r6
@@ -1976,7 +2078,7 @@
 	/* Set up segment registers for new task */
 	rlwinm	r5,r5,4,8,27	/* VSID = context << 4 */
 	addis	r5,r5,0x6000	/* Set Ks, Ku bits */
-	li	r0,8		/* TASK_SIZE / SEGMENT_SIZE */
+	li	r0,12		/* TASK_SIZE / SEGMENT_SIZE */
 	mtctr	r0
 	li	r3,0
 3:	mtsrin	r5,r3
@@ -2118,7 +2220,7 @@
 _GLOBAL(set_context)
 	rlwinm	r3,r3,4,8,27	/* VSID = context << 4 */
 	addis	r3,r3,0x6000	/* Set Ks, Ku bits */
-	li	r0,8		/* TASK_SIZE / SEGMENT_SIZE */
+	li	r0,12		/* TASK_SIZE / SEGMENT_SIZE */
 	mtctr	r0
 	li	r4,0
 3:	mtsrin	r3,r4
@@ -2177,6 +2279,27 @@
 	blr
 
 /*
+ * Like above, but only do the D-cache.  This is used by the 8xx
+ * to push the cache so the CPM doesn't get stale data.
+ *
+ * flush_dcache_range(unsigned long start, unsigned long stop)
+ */
+_GLOBAL(flush_dcache_range)
+       li      r5,CACHE_LINE_SIZE-1
+       andc    r3,r3,r5
+       subf    r4,r3,r4
+       add     r4,r4,r5
+       srwi.   r4,r4,LG_CACHE_LINE_SIZE
+       beqlr
+       mtctr   r4
+
+1:     dcbst   0,r3
+       addi    r3,r3,CACHE_LINE_SIZE
+       bdnz    1b
+       sync                            /* wait for dcbst's to get to ram */
+       blr
+
+/*
  * Flush a particular page from the DATA cache
  * Note: this is necessary because the instruction cache does *not*
  * snoop from the data cache.
@@ -2207,25 +2330,35 @@
 	blr
 
 /*
+ * Clear a page using the dcbz instruction, which doesn't cause any
+ * memory traffic (except to write out any cache lines which get
+ * displaced).  This only works on cacheable memory.
+ */
+_GLOBAL(clear_page)
+	li	r0,4096/CACHE_LINE_SIZE
+	mtctr	r0
+1:	dcbz	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	blr
+
+/*
  * Flush entries from the hash table with VSIDs in the range
  * given.
  */
 #ifndef CONFIG_8xx	
 _GLOBAL(flush_hash_segments)
+	lis	r5,Hash@ha
+	lwz	r5,Hash@l(r5)		/* base of hash table */
 #ifdef NO_RELOAD_HTAB
-/*
- * Bitmask of PVR numbers of 603-like chips,
- * for which we don't use the hash table at all.
- */
-#define PVR_603_LIKE	0x13000000	/* bits 3, 6, 7 set */
-
-	mfspr	r0,PVR
-	rlwinm	r0,r0,16,27,31
-	lis	r9,PVR_603_LIKE@h
-	rlwnm.	r0,r9,r0,0,0
-	beq+	99f
+	cmpwi	0,r5,0
+	bne+	99f
 	tlbia
-	isync
+	sync
+#ifdef __SMP__
+	tlbsync
+	sync
+#endif
 	blr
 99:
 #endif /* NO_RELOAD_HTAB */
@@ -2247,14 +2380,13 @@
 	bne-	10b
 	stwcx.	r8,0,r9
 	bne-	10b
+	eieio
 #endif
 	rlwinm	r3,r3,7,1,24		/* put VSID lower limit in position */
 	oris	r3,r3,0x8000		/* set V bit */
 	rlwinm	r4,r4,7,1,24		/* put VSID upper limit in position */
 	oris	r4,r4,0x8000
 	ori	r4,r4,0x7f
-	lis	r5,Hash@ha
-	lwz	r5,Hash@l(r5)		/* base of hash table */
 	lis	r6,Hash_size@ha
 	lwz	r6,Hash_size@l(r6)	/* size in bytes */
 	srwi	r6,r6,3			/* # PTEs */
@@ -2270,11 +2402,11 @@
 2:	bdnz	1b			/* continue with loop */
 	sync
 	tlbia
-	isync
+	sync
 #ifdef __SMP__
 	tlbsync
+	sync
 	lis	r3,hash_table_lock@ha
-	li	r0,0
 	stw	r0,hash_table_lock@l(r3)
 	mtmsr	r10
 	SYNC
@@ -2287,14 +2419,17 @@
  * flush_hash_page(unsigned context, unsigned long va)
  */
 _GLOBAL(flush_hash_page)
+	lis	r6,Hash@ha
+	lwz	r6,Hash@l(r6)		/* hash table base */
 #ifdef NO_RELOAD_HTAB
-	mfspr	r0,PVR
-	rlwinm	r0,r0,16,27,31
-	lis	r9,PVR_603_LIKE@h
-	rlwnm.	r0,r9,r0,0,0
-	beq+	99f
+	cmpwi	0,r6,0			/* hash table in use? */
+	bne+	99f
 	tlbie	r4			/* in hw tlb too */
-	isync
+	sync
+#ifdef __SMP__
+	tlbsync
+	sync
+#endif
 	blr
 99:
 #endif /* NO_RELOAD_HTAB */		
@@ -2311,11 +2446,12 @@
 	ori	r9,r9,hash_table_lock@l
 	lwz	r8,PROCESSOR(r2)
 	oris	r8,r8,9
-10:	lwarx	r6,0,r9
-	cmpi	0,r6,0
+10:	lwarx	r7,0,r9
+	cmpi	0,r7,0
 	bne-	10b
 	stwcx.	r8,0,r9
 	bne-	10b
+	eieio
 #endif
 	rlwinm	r3,r3,11,1,20		/* put context into vsid */
 	rlwimi	r3,r4,11,21,24		/* put top 4 bits of va into vsid */
@@ -2328,8 +2464,6 @@
 	lwz	r5,Hash_mask@l(r5)	/* hash mask */
 	slwi	r5,r5,6			/*  << 6 */
 	and	r7,r7,r5
-	lis	r6,Hash@ha
-	lwz	r6,Hash@l(r6)		/* hash table base */
 	add	r6,r6,r7		/* address of primary PTEG */
 	li	r8,8
 	mtctr	r8
@@ -2350,9 +2484,10 @@
 	stw	r0,0(r7)		/* invalidate entry */
 4:	sync
 	tlbie	r4			/* in hw tlb too */
-	isync
+	sync
 #ifdef __SMP__
 	tlbsync
+	sync
 	lis	r3,hash_table_lock@h
 	li	r0,0
 	stw	r0,hash_table_lock@l(r3)
@@ -2418,30 +2553,27 @@
 	rfi			/* return to caller */
 #endif /* CONFIG_8xx */
 
-#ifdef CONFIG_MBX
-/* Jump into the system reset for the MBX rom.
+#ifdef CONFIG_8xx
+/* Jump into the system reset for the rom.
  * We first disable the MMU, and then jump to the ROM reset address.
  *
- * This does not work, don't bother trying.  There is no place in
- * the ROM we can jump to cause a reset.  We will have to program
- * a watchdog of some type that we don't service to cause a processor
- * reset.
- */
-	.globl	MBX_gorom
-MBX_gorom:
-	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
-	lis	r4,2f@h
-	addis	r4,r4,-KERNELBASE@h
-	ori	r4,r4,2f@l
-	mtspr	SRR0,r4
-	mtspr	SRR1,r3
-	rfi
+ * r3 is the board info structure, r4 is the location for starting.
+ * I use this for building a small kernel that can load other kernels,
+ * rather than trying to write or rely on a rom monitor that can tftp load.
+ */
+       .globl  m8xx_gorom
+m8xx_gorom:
+       li      r5,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+       lis     r6,2f@h
+       addis   r6,r6,-KERNELBASE@h
+       ori     r6,r6,2f@l
+       mtspr   SRR0,r6
+       mtspr   SRR1,r5
+       rfi
 2:
-	lis	r4, 0xfe000000@h
-	addi	r4, r4, 0xfe000000@l
-	mtlr	r4
-	blr
-#endif /* CONFIG_MBX */
+       mtlr    r4
+       blr
+#endif /* CONFIG_8xx */
 	
 /*
  * We put a few things here that have to be page-aligned.
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.2.3/linux/arch/ppc/kernel/idle.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/kernel/idle.c	Wed Mar 10 21:30:32 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.57 1998/12/28 10:28:46 paulus Exp $
+ * $Id: idle.c,v 1.60 1999/02/12 07:06:26 cort Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -39,6 +39,12 @@
 unsigned long zero_paged_on = 0;
 unsigned long powersave_nap = 0;
 
+unsigned long *zero_cache;    /* head linked list of pre-zero'd pages */
+unsigned long zero_sz;	      /* # currently pre-zero'd pages */
+unsigned long zeropage_hits;  /* # zero'd pages request that we've done */
+unsigned long zeropage_calls; /* # zero'd pages request that've been made */
+unsigned long zerototal;      /* # pages zero'd over time */
+
 int idled(void *unused)
 {
 	/* endless loop with no priority at all */
@@ -108,8 +114,6 @@
 	/* if we don't have a htab */
 	if ( Hash_size == 0 )
 		return;
-	lock_dcache(1);
-
 #if 0	
 	/* find a random place in the htab to start each time */
 	start = &Hash[jiffies%(Hash_size/sizeof(PTE))];
@@ -147,7 +151,6 @@
 	}
 out:
 	if ( current->need_resched ) printk("need_resched: %lx\n", current->need_resched);
-	unlock_dcache();
 #endif /* CONFIG_8xx */
 }
 
@@ -159,7 +162,7 @@
 {
 	unsigned long page = 0;
 
-	atomic_inc((atomic_t *)&quicklists.zeropage_calls);
+	atomic_inc((atomic_t *)&zero_cache_calls);
 	if ( zero_quicklist )
 	{
 		/* atomically remove this page from the list */
@@ -177,10 +180,10 @@
 #endif /* __SMP__ */		
 		/* we can update zerocount after the fact since it is not
 		 * used for anything but control of a loop which doesn't
-		 * matter since it won't affect anything if it zero's one
+		 * matter since it won't affect anything if it zeros one
 		 * less page -- Cort
 		 */
-		atomic_inc((atomic_t *)&quicklists.zeropage_hits);
+		atomic_inc((atomic_t *)&zero_cache_hits);
 		atomic_dec((atomic_t *)&zero_cache_sz);
 		
 		/* zero out the pointer to next in the page */
@@ -222,7 +225,6 @@
 		
 		/*
 		 * Make the page no cache so we don't blow our cache with 0's
-		 * We should just turn off the cache instead. -- Cort
 		 */
 		pte = find_pte(init_task.mm, pageptr);
 		if ( !pte )
@@ -254,8 +256,8 @@
 		 * So we update the list atomically without locking it.
 		 * -- Cort
 		 */
+		
 		/* turn cache on for this page */
-
 		pte_cache(*pte);
 		flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
 		/* atomically add this page to the list */
@@ -280,7 +282,7 @@
 		 * reads it.  -- Cort
 		 */
 		atomic_inc((atomic_t *)&zero_cache_sz);
-		atomic_inc((atomic_t *)&quicklists.zerototal);
+		atomic_inc((atomic_t *)&zero_cache_total);
 	}
 }
 
@@ -307,5 +309,4 @@
 	default:
 		return;
 	}
-	
 }
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- v2.2.3/linux/arch/ppc/kernel/irq.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/kernel/irq.c	Wed Mar 10 21:30:32 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: irq.c,v 1.91 1998/12/28 10:28:47 paulus Exp $
+ * $Id: irq.c,v 1.102 1999/02/03 01:36:59 paulus Exp $
  *
  *  arch/ppc/kernel/irq.c
  *
@@ -42,6 +42,7 @@
 #include <linux/malloc.h>
 #include <linux/openpic.h>
 #include <linux/pci.h>
+#include <linux/delay.h>
 
 #include <asm/bitops.h>
 #include <asm/hydra.h>
@@ -56,58 +57,71 @@
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/amigappc.h>
+#include <asm/ptrace.h>
 #ifdef CONFIG_8xx
 #include <asm/8xx_immap.h>
 #include <asm/mbx.h>
 #endif
 
+static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
+extern volatile unsigned long ipi_count;
+static void dispatch_handler(struct pt_regs *regs, int irq);
+void enable_irq(unsigned int irq_nr);
+void disable_irq(unsigned int irq_nr);
+
+static void i8259_mask_and_ack_irq(unsigned int irq_nr);
+static void i8259_mask_irq(unsigned int irq_nr);
+static void i8259_unmask_irq(unsigned int irq_nr);
+#ifdef CONFIG_8xx
+static void mbx_mask_and_ack(unsigned int irq_nr);
+static void mbx_mask_irq(unsigned int irq_nr);
+static void mbx_unmask_irq(unsigned int irq_nr);
+static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs);
+#else /* CONFIG_8xx */
+static volatile unsigned char *chrp_int_ack_special;
 extern void process_int(unsigned long vec, struct pt_regs *fp);
 extern void apus_init_IRQ(void);
 extern void amiga_disable_irq(unsigned int irq);
 extern void amiga_enable_irq(unsigned int irq);
-static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
-static volatile unsigned char *chrp_int_ack_special;
-extern volatile unsigned long ipi_count;
 static void pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base);
+static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs);
+static void pmac_mask_irq(unsigned int irq_nr);
+static void pmac_unmask_irq(unsigned int irq_nr);
+static void pmac_mask_and_ack_irq(unsigned int irq_nr);
+static void chrp_mask_and_ack_irq(unsigned int irq_nr);
+static void chrp_unmask_irq(unsigned int irq_nr);
+static void chrp_mask_irq(unsigned int irq_nr);
+#ifdef __SMP__	
+static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs);
+extern void smp_message_recv(void);
+#endif /* __SMP__ */
+#endif /* CONFIG_8xx */
 
 #ifdef CONFIG_APUS
 /* Rename a few functions. Requires the CONFIG_APUS protection. */
 #define request_irq nop_ppc_request_irq
 #define free_irq nop_ppc_free_irq
 #define get_irq_list nop_get_irq_list
+#define VEC_SPUR    (24)
 #endif
-#ifndef CONFIG_8xx
-void (*mask_and_ack_irq)(int irq_nr);
-void (*mask_irq)(unsigned int irq_nr);
-void (*unmask_irq)(unsigned int irq_nr);
-#else /* CONFIG_8xx */
-/* init_IRQ() happens too late for the MBX because we initialize the
- * CPM early and it calls request_irq() before we have these function
- * pointers initialized.
- */
-#define mask_and_ack_irq(irq)	mbx_mask_irq(irq)
-#define mask_irq(irq) mbx_mask_irq(irq)
-#define unmask_irq(irq) mbx_unmask_irq(irq)
-#endif /* CONFIG_8xx */
 
-#define VEC_SPUR    (24)
-#undef SHOW_IRQ
-#undef SHOW_GATWICK_IRQS
 #define NR_MASK_WORDS	((NR_IRQS + 31) / 32)
-#define cached_21	(((char *)(cached_irq_mask))[3])
-#define cached_A1	(((char *)(cached_irq_mask))[2])
-#define PREP_IRQ_MASK	(((unsigned int)cached_A1)<<8) | (unsigned int)cached_21
+unsigned char cached_8259[2] = { 0xff, 0xff };
+#define cached_A1 (cached_8259[0])
+#define cached_21 (cached_8259[1])
 
 unsigned int local_bh_count[NR_CPUS];
 unsigned int local_irq_count[NR_CPUS];
 int max_irqs;
 int max_real_irqs;
-static struct irqaction *irq_action[NR_IRQS];
 static int spurious_interrupts = 0;
 static unsigned int cached_irq_mask[NR_MASK_WORDS];
 unsigned int lost_interrupts[NR_MASK_WORDS];
 atomic_t n_lost_interrupts;
 
+#ifndef CONFIG_8xx
+#define GATWICK_IRQ_POOL_SIZE	10
+static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE];
 /* pmac */
 struct pmac_irq_hw {
 	unsigned int	flag;
@@ -116,41 +130,22 @@
 	unsigned int	level;
 };
 
-/* XXX these addresses should be obtained from the device tree */
-volatile struct pmac_irq_hw *pmac_irq_hw[4] = {
+/* these addresses are obtained from the device tree now -- Cort */
+volatile struct pmac_irq_hw *pmac_irq_hw[4] __pmac = {
 	(struct pmac_irq_hw *) 0xf3000020,
 	(struct pmac_irq_hw *) 0xf3000010,
 	(struct pmac_irq_hw *) 0xf4000020,
 	(struct pmac_irq_hw *) 0xf4000010,
 };
-
-/* This is the interrupt used on the main controller for the secondary
-   controller. Happens on PowerBooks G3 Series (a second mac-io)
-   -- BenH
- */
-static int second_irq = -999;
-
-/* Returns the number of 0's to the left of the most significant 1 bit */
-static inline int cntlzw(int bits)
-{
-	int lz;
-
-	asm ("cntlzw %0,%1" : "=r" (lz) : "r" (bits));
-	return lz;
-}
-
-static inline void sync(void)
-{
-	asm volatile ("sync");
-}
+#endif /* CONFIG_8xx */
 
 /* nasty hack for shared irq's since we need to do kmalloc calls but
- * can't very very early in the boot when we need to do a request irq.
+ * can't very early in the boot when we need to do a request irq.
  * this needs to be removed.
  * -- Cort
  */
 static char cache_bitmask = 0;
-static struct irqaction malloc_cache[4];
+static struct irqaction malloc_cache[8];
 extern int mem_init_done;
 
 void *irq_kmalloc(size_t size, int pri)
@@ -179,168 +174,155 @@
 	kfree(ptr);
 }
 
-#ifndef CONFIG_8xx	
-void i8259_mask_and_ack_irq(int irq_nr)
-{
-  /*	spin_lock(&irq_controller_lock);*/
-	cached_irq_mask[0] |= 1 << irq_nr;
-	if (irq_nr > 7) {
-		inb(0xA1);	/* DUMMY */
-		outb(cached_A1,0xA1);
-		outb(0x62,0x20);	/* Specific EOI to cascade */
-		/*outb(0x20,0xA0);*/
-		outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */
-	} else {
-		inb(0x21);	/* DUMMY */
-		outb(cached_21,0x21);
-		/*outb(0x20,0x20);*/
-		outb(0x60|irq_nr,0x20); /* specific eoi */
-		  
-	}
-	/*	spin_unlock(&irq_controller_lock);*/
-}
-
-void __pmac pmac_mask_and_ack_irq(int irq_nr)
-{
-	unsigned long bit = 1UL << (irq_nr & 0x1f);
-	int i = irq_nr >> 5;
-
-	if ((unsigned)irq_nr >= max_irqs)
-		return;
-	/*spin_lock(&irq_controller_lock);*/
+struct hw_interrupt_type {
+	const char * typename;
+	void (*startup)(unsigned int irq);
+	void (*shutdown)(unsigned int irq);
+	void (*handle)(unsigned int irq, struct pt_regs * regs);
+	void (*enable)(unsigned int irq);
+	void (*disable)(unsigned int irq);
+	void (*mask_and_ack)(unsigned int irq);
+	int irq_offset;
+};
 
-	clear_bit(irq_nr, cached_irq_mask);
-	if (test_and_clear_bit(irq_nr, lost_interrupts))
-		atomic_dec(&n_lost_interrupts);
-	out_le32(&pmac_irq_hw[i]->ack, bit);
-	out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]);
-	out_le32(&pmac_irq_hw[i]->ack, bit);
-	/* make sure ack gets to controller before we enable interrupts */
-	sync();
+#define mask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->disable) irq_desc[irq].ctl->disable(irq);})
+#define unmask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->enable) irq_desc[irq].ctl->enable(irq);})
+#define mask_and_ack_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->mask_and_ack) irq_desc[irq].ctl->mask_and_ack(irq);})
 
-	/*spin_unlock(&irq_controller_lock);*/
-	/*if ( irq_controller_lock.lock )
-	  panic("irq controller lock still held in mask and ack\n");*/
-}
+struct irqdesc {
+	struct irqaction *action;
+	struct hw_interrupt_type *ctl;
+};
+static struct irqdesc irq_desc[NR_IRQS] = {{0, 0}, };
 
-void __openfirmware chrp_mask_and_ack_irq(int irq_nr)
-{
-	/* spinlocks are done by i8259_mask_and_ack() - Cort */
-	if (is_8259_irq(irq_nr))
-	    i8259_mask_and_ack_irq(irq_nr);
-}
+static struct hw_interrupt_type i8259_pic = {
+	" i8259    ",
+	NULL,
+	NULL,
+	NULL,
+	i8259_unmask_irq,
+	i8259_mask_irq,
+	i8259_mask_and_ack_irq,
+	0
+};
+#ifndef CONFIG_8xx
+static struct hw_interrupt_type pmac_pic = {
+	" PMAC-PIC ",
+	NULL,
+	NULL,
+	NULL,
+	pmac_unmask_irq,
+	pmac_mask_irq,
+	pmac_mask_and_ack_irq,
+	0
+};
 
+static struct hw_interrupt_type gatwick_pic = {
+	" GATWICK  ",
+	NULL,
+	NULL,
+	NULL,
+	pmac_unmask_irq,
+	pmac_mask_irq,
+	pmac_mask_and_ack_irq,
+	0
+};
 
-static void i8259_set_irq_mask(int irq_nr)
-{
-	if (irq_nr > 7) {
-		outb(cached_A1,0xA1);
-	} else {
-		outb(cached_21,0x21);
-	}
-}
+static struct hw_interrupt_type open_pic = {
+	" OpenPIC  ",
+	NULL,
+	NULL,
+	NULL,
+	chrp_unmask_irq,
+	chrp_mask_irq,
+	chrp_mask_and_ack_irq,
+	0
+};
+#else
+static struct hw_interrupt_type ppc8xx_pic = {
+	" 8xx SIU  ",
+	NULL,
+	NULL,
+	NULL,
+	mbx_unmask_irq,
+	mbx_mask_irq,
+	mbx_mask_and_ack,
+	0
+};
+#endif /* CONFIG_8xx */
 
-static void __pmac pmac_set_irq_mask(int irq_nr)
+int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+	unsigned long irqflags, const char * devname, void *dev_id)
 {
-	unsigned long bit = 1UL << (irq_nr & 0x1f);
-	int i = irq_nr >> 5;
-
-	if ((unsigned)irq_nr >= max_irqs)
-		return;
-
-	/* enable unmasked interrupts */
-	out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]);
+	struct irqaction *old, **p, *action;
+	unsigned long flags;
 
-	/*
-	 * Unfortunately, setting the bit in the enable register
-	 * when the device interrupt is already on *doesn't* set
-	 * the bit in the flag register or request another interrupt.
-	 */
-	if ((bit & cached_irq_mask[i])
-	    && (ld_le32(&pmac_irq_hw[i]->level) & bit)
-	    && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) {
-		if (!test_and_set_bit(irq_nr, lost_interrupts))
-			atomic_inc(&n_lost_interrupts);
+	if (irq >= NR_IRQS)
+		return -EINVAL;
+	if (!handler)
+	{
+		/* Free */
+		for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next)
+		{
+			/* Found it - now free it */
+			save_flags(flags);
+			cli();
+			*p = action->next;
+			restore_flags(flags);
+			irq_kfree(action);
+			return 0;
+		}
+		return -ENOENT;
 	}
-}
-
-/*
- * These have to be protected by the spinlock
- * before being called.
- */
-static void i8259_mask_irq(unsigned int irq_nr)
-{
-	cached_irq_mask[0] |= 1 << irq_nr;
-	i8259_set_irq_mask(irq_nr);
-}
-
-static void i8259_unmask_irq(unsigned int irq_nr)
-{
-	cached_irq_mask[0] &= ~(1 << irq_nr);
-	i8259_set_irq_mask(irq_nr);
-}
-
-static void __pmac pmac_mask_irq(unsigned int irq_nr)
-{
-	clear_bit(irq_nr, cached_irq_mask);
-	pmac_set_irq_mask(irq_nr);
-	sync();
-}
-
-static void __pmac pmac_unmask_irq(unsigned int irq_nr)
-{
-	set_bit(irq_nr, cached_irq_mask);
-	pmac_set_irq_mask(irq_nr);
-}
-
-static void __openfirmware chrp_mask_irq(unsigned int irq_nr)
-{
-	if (is_8259_irq(irq_nr))
-		i8259_mask_irq(irq_nr);
-	else
-		openpic_disable_irq(irq_to_openpic(irq_nr));
-}
+	
+	action = (struct irqaction *)
+		irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
+	
+	save_flags(flags);
+	cli();
+	
+	action->handler = handler;
+	action->flags = irqflags;					
+	action->mask = 0;
+	action->name = devname;
+	action->dev_id = dev_id;
+	action->next = NULL;
+	enable_irq(irq);
+	
+	p = &irq_desc[irq].action;
+	
+	if ((old = *p) != NULL) {
+		/* Can't share interrupts unless both agree to */
+		if (!(old->flags & action->flags & SA_SHIRQ))
+			return -EBUSY;
+		/* add new interrupt at end of irq queue */
+		do {
+			p = &old->next;
+			old = *p;
+		} while (old);
+	}
+	*p = action;
 
-static void __openfirmware chrp_unmask_irq(unsigned int irq_nr)
-{
-	if (is_8259_irq(irq_nr))
-		i8259_unmask_irq(irq_nr);
-	else
-		openpic_enable_irq(irq_to_openpic(irq_nr));
-}
-#else /* CONFIG_8xx */
-static void mbx_mask_irq(unsigned int irq_nr)
-{
-	cached_irq_mask[0] &= ~(1 << (31-irq_nr));
-	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
-							cached_irq_mask[0];
+	restore_flags(flags);	
+	return 0;
 }
 
-static void mbx_unmask_irq(unsigned int irq_nr)
+void free_irq(unsigned int irq, void *dev_id)
 {
-	cached_irq_mask[0] |= (1 << (31-irq_nr));
-	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
-							cached_irq_mask[0];
+	request_irq(irq, NULL, 0, NULL, dev_id);
 }
-#endif /* CONFIG_8xx */
 
 void disable_irq(unsigned int irq_nr)
 {
-	/*unsigned long flags;*/
-
-	/*	spin_lock_irqsave(&irq_controller_lock, flags);*/
 	mask_irq(irq_nr);
-	/*	spin_unlock_irqrestore(&irq_controller_lock, flags);*/
 	synchronize_irq();
 }
 
 void enable_irq(unsigned int irq_nr)
 {
-	/*unsigned long flags;*/
-
-	/*	spin_lock_irqsave(&irq_controller_lock, flags);*/
 	unmask_irq(irq_nr);
-	/*	spin_unlock_irqrestore(&irq_controller_lock, flags);*/
 }
 
 int get_irq_list(char *buf)
@@ -354,8 +336,8 @@
 	*(char *)(buf+len++) = '\n';
 
 	for (i = 0 ; i < NR_IRQS ; i++) {
-		action = irq_action[i];
-		if ((!action || !action->handler) && (i != second_irq))
+		action = irq_desc[i].action;
+		if ( !action || !action->handler )
 			continue;
 		len += sprintf(buf+len, "%3d: ", i);		
 #ifdef __SMP__
@@ -365,36 +347,13 @@
 #else		
 		len += sprintf(buf+len, "%10u ", kstat_irqs(i));
 #endif /* __SMP__ */
-		switch( _machine )
-		{
-		case _MACH_prep:
-			len += sprintf(buf+len, "    82c59 ");
-			break;
-		case _MACH_Pmac:
-			if (i < 64)
-				len += sprintf(buf+len, " PMAC-PIC ");
-			else
-				len += sprintf(buf+len, "  GATWICK ");
-			break;
-		case _MACH_chrp:
-			if ( is_8259_irq(i) )
-				len += sprintf(buf+len, "    82c59 ");
-			else
-				len += sprintf(buf+len, "  OpenPIC ");
-			break;
-		case _MACH_mbx:
-			len += sprintf(buf+len, "   MPC8xx ");
-			break;
+		if ( irq_desc[i].ctl )		
+			len += sprintf(buf+len, " %s ", irq_desc[i].ctl->typename );
+		len += sprintf(buf+len, "    %s",action->name);
+		for (action=action->next; action; action = action->next) {
+			len += sprintf(buf+len, ", %s", action->name);
 		}
-
-		if (i != second_irq) {
-			len += sprintf(buf+len, "    %s",action->name);
-			for (action=action->next; action; action = action->next) {
-				len += sprintf(buf+len, ", %s", action->name);
-			}
-			len += sprintf(buf+len, "\n");
-		} else
-			len += sprintf(buf+len, "    Gatwick secondary IRQ controller\n");
+		len += sprintf(buf+len, "\n");
 	}
 #ifdef __SMP__
 	/* should this be per processor send/receive? */
@@ -405,269 +364,53 @@
 #endif		
 	len += sprintf(buf+len, "BAD: %10u",spurious_interrupts);
 	for ( i = 0 ; i <= smp_num_cpus-1; i++ )
-		len += sprintf(buf+len,"          ");
-	len += sprintf(buf+len, "     spurious or short\n");
+		len += sprintf(buf+len,"        ");
+	len += sprintf(buf+len, "         spurious or short\n");
 	return len;
 }
 
-
-/*
- * Global interrupt locks for SMP. Allow interrupts to come in on any
- * CPU, yet make cli/sti act globally to protect critical regions..
- */
-#ifdef __SMP__
-unsigned char global_irq_holder = NO_PROC_ID;
-unsigned volatile int global_irq_lock;
-atomic_t global_irq_count;
-
-atomic_t global_bh_count;
-atomic_t global_bh_lock;
-
-static void show(char * str)
+static void dispatch_handler(struct pt_regs *regs, int irq)
 {
-	int i;
-	unsigned long *stack;
+	int status;
+	struct irqaction *action;
 	int cpu = smp_processor_id();
-
-	printk("\n%s, CPU %d:\n", str, cpu);
-	printk("irq:  %d [%d %d]\n",
-		atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]);
-	printk("bh:   %d [%d %d]\n",
-		atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]);
-	stack = (unsigned long *) &str;
-	for (i = 40; i ; i--) {
-		unsigned long x = *++stack;
-		if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) {
-			printk("<[%08lx]> ", x);
-		}
+	
+	mask_and_ack_irq(irq);
+	status = 0;
+	action = irq_desc[irq].action;
+	kstat.irqs[cpu][irq]++;
+	if (action && action->handler) {
+		if (!(action->flags & SA_INTERRUPT))
+			__sti();
+		do { 
+			status |= action->flags;
+			action->handler(irq, action->dev_id, regs);
+			action = action->next;
+		} while ( action );
+		__cli();
+		unmask_irq(irq);
+	} else {
+		spurious_interrupts++;
+		disable_irq( irq );
 	}
 }
 
 #define MAXCOUNT 100000000
-static inline void wait_on_bh(void)
-{
-	int count = MAXCOUNT;
-	do {
-		if (!--count) {
-			show("wait_on_bh");
-			count = ~0;
-		}
-		/* nothing .. wait for the other bh's to go away */
-	} while (atomic_read(&global_bh_count) != 0);
-}
-
-
-static inline void wait_on_irq(int cpu)
-{
-	int count = MAXCOUNT;
-
-	for (;;) {
-
-		/*
-		 * Wait until all interrupts are gone. Wait
-		 * for bottom half handlers unless we're
-		 * already executing in one..
-		 */
-		if (!atomic_read(&global_irq_count)) {
-			if (local_bh_count[cpu] || !atomic_read(&global_bh_count))
-				break;
-		}
-
-		/* Duh, we have to loop. Release the lock to avoid deadlocks */
-		clear_bit(0,&global_irq_lock);
-
-		for (;;) {
-			if (!--count) {
-				show("wait_on_irq");
-				count = ~0;
-			}
-			__sti();
-			/* don't worry about the lock race Linus found
-			 * on intel here. -- Cort
-			 */
-			__cli();
-			if (atomic_read(&global_irq_count))
-				continue;
-			if (global_irq_lock)
-				continue;
-			if (!local_bh_count[cpu] && atomic_read(&global_bh_count))
-				continue;
-			if (!test_and_set_bit(0,&global_irq_lock))
-				break;
-		}
-	}
-}
-
-/*
- * This is called when we want to synchronize with
- * bottom half handlers. We need to wait until
- * no other CPU is executing any bottom half handler.
- *
- * Don't wait if we're already running in an interrupt
- * context or are inside a bh handler.
- */
-void synchronize_bh(void)
-{
-	if (atomic_read(&global_bh_count) && !in_interrupt())
-		wait_on_bh();
-}
-
-
-/*
- * This is called when we want to synchronize with
- * interrupts. We may for example tell a device to
- * stop sending interrupts: but to make sure there
- * are no interrupts that are executing on another
- * CPU we need to call this function.
- */
-void synchronize_irq(void)
-{
-	if (atomic_read(&global_irq_count)) {
-		/* Stupid approach */
-		cli();
-		sti();
-	}
-}
-
-static inline void get_irqlock(int cpu)
-{
-	unsigned int loops = MAXCOUNT;
-
-	if (test_and_set_bit(0,&global_irq_lock)) {
-		/* do we already hold the lock? */
-		if ((unsigned char) cpu == global_irq_holder)
-			return;
-		/* Uhhuh.. Somebody else got it. Wait.. */
-		do {
-			do {
-				if (loops-- == 0) {
-					printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder);
-#ifdef CONFIG_XMON
-					xmon(0);
-#endif
-				}
-			} while (test_bit(0,&global_irq_lock));
-		} while (test_and_set_bit(0,&global_irq_lock));		
-	}
-	/* 
-	 * We also need to make sure that nobody else is running
-	 * in an interrupt context. 
-	 */
-	wait_on_irq(cpu);
-
-	/*
-	 * Ok, finally..
-	 */
-	global_irq_holder = cpu;
-}
-
-/*
- * A global "cli()" while in an interrupt context
- * turns into just a local cli(). Interrupts
- * should use spinlocks for the (very unlikely)
- * case that they ever want to protect against
- * each other.
- *
- * If we already have local interrupts disabled,
- * this will not turn a local disable into a
- * global one (problems with spinlocks: this makes
- * save_flags+cli+sti usable inside a spinlock).
- */
-void __global_cli(void)
-{
-	unsigned int flags;
-	
-	__save_flags(flags);
-	if (flags & (1 << 15)) {
-		int cpu = smp_processor_id();
-		__cli();
-		if (!local_irq_count[cpu])
-			get_irqlock(cpu);
-	}
-}
-
-void __global_sti(void)
-{
-	int cpu = smp_processor_id();
-
-	if (!local_irq_count[cpu])
-		release_irqlock(cpu);
-	__sti();
-}
-
-/*
- * SMP flags value to restore to:
- * 0 - global cli
- * 1 - global sti
- * 2 - local cli
- * 3 - local sti
- */
-unsigned long __global_save_flags(void)
-{
-	int retval;
-	int local_enabled;
-	unsigned long flags;
-
-	__save_flags(flags);
-	local_enabled = (flags >> 15) & 1;
-	/* default to local */
-	retval = 2 + local_enabled;
-
-	/* check for global flags if we're not in an interrupt */
-	if (!local_irq_count[smp_processor_id()]) {
-		if (local_enabled)
-			retval = 1;
-		if (global_irq_holder == (unsigned char) smp_processor_id())
-			retval = 0;
-	}
-	return retval;
-}
-
-void __global_restore_flags(unsigned long flags)
-{
-	switch (flags) {
-	case 0:
-		__global_cli();
-		break;
-	case 1:
-		__global_sti();
-		break;
-	case 2:
-		__cli();
-		break;
-	case 3:
-		__sti();
-		break;
-	default:
-		printk("global_restore_flags: %08lx (%08lx)\n",
-			flags, (&flags)[-1]);
-	}
-}
-
-#endif /* __SMP__ */
-
-asmlinkage void do_IRQ(struct pt_regs *regs, int isfake)
+asmlinkage void do_IRQ(struct pt_regs *regs, int isfake)
 {
 	int irq;
-	unsigned long bits;
-	struct irqaction *action;
+	unsigned long bits = 0;
 	int cpu = smp_processor_id();
-	int status;
 	int openpic_eoi_done = 0;
 
-	/* save the HID0 in case dcache was off - see idle.c
-	 * this hack should leave for a better solution -- Cort */
-	unsigned dcache_locked;
-	
-	dcache_locked = unlock_dcache();	
 	hardirq_enter(cpu);
 #ifndef CONFIG_8xx		  
 #ifdef __SMP__
-	if ( cpu != 0 )
+	/* IPI's are a hack on the powersurge -- Cort */
+	if ( (_machine == _MACH_Pmac) && (cpu != 0) )
 	{
 		if (!isfake)
 		{
-			extern void smp_message_recv(void);
 #ifdef CONFIG_XMON
 			static int xmon_2nd;
 			if (xmon_2nd)
@@ -713,36 +456,6 @@
 			irq -= cntlzw(bits);
 			break;
 		}
-
-		/* Here, we handle interrupts coming from Gatwick,
-		 * normal interrupt code will take care of acking and
-		 * masking the irq on Gatwick itself but we ack&mask
-		 * the Gatwick main interrupt on Heathrow now. It's
-		 * unmasked later, after interrupt handling. -- BenH 
-		 */
-		if (irq == second_irq) {
-			mask_and_ack_irq(second_irq);
-			for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) {
-				int i = irq >> 5;
-				bits = ld_le32(&pmac_irq_hw[i]->flag)
-					| lost_interrupts[i];
-				if (bits == 0)
-					continue;
-				irq -= cntlzw(bits);
-				break;
-			}
-			/* If not found, on exit, irq is 63 (128-1-32-32).
-			 * We set it to -1 and revalidate second controller
-			 */
-			if (irq < max_real_irqs) {
-				irq = -1;
-				unmask_irq(second_irq);
-			}
-#ifdef SHOW_GATWICK_IRQS
-			printk("Gatwick irq %d (i:%d, bits:0x%08lx\n", irq, i, bits);
-#endif
-		}
-
 		break;
 	case _MACH_chrp:
 		irq = openpic_irq(0);
@@ -761,42 +474,50 @@
 			openpic_eoi(0);
 			openpic_eoi_done = 1;
 		}
-		else if (irq >= OPENPIC_VEC_TIMER)
+		if (irq == OPENPIC_VEC_SPURIOUS)
 		{
-			/*
-			 *  OpenPIC interrupts >64 will be used for other purposes
-			 *  like interprocessor interrupts and hardware errors
-			 */
-			if (irq == OPENPIC_VEC_SPURIOUS) {
 				/*
 				 * Spurious interrupts should never be
 				 * acknowledged
 				 */
-				spurious_interrupts++;
-				openpic_eoi_done = 1;
-			} else {
-				/*
-				 * Here we should process IPI timer
-				 * for now the interrupt is dismissed.
-				 */
-			}
-			goto out;
+			spurious_interrupts++;
+			openpic_eoi_done = 1;
 		}
+		bits = 1UL << irq;
 		break;
 	case _MACH_prep:
 		outb(0x0C, 0x20);
 		irq = inb(0x20) & 7;
 		if (irq == 2)
 		{
-retry_cascade:
 			outb(0x0C, 0xA0);
-			irq = inb(0xA0);
-			/* if no intr left */
-			if ( !(irq & 128 ) )
-				goto out;
-			irq = (irq&7) + 8;
+			irq = (inb(0xA0) & 7) + 8;
+			bits |= 1UL << irq;
+#if 0			
+			/* It's possible to loose intrs here
+			 * if we get 2 intrs in the upper 8
+			 * bits.  We eoi irq 2 and handle one of
+			 * the upper intrs but then ignore it
+			 * since we've already eoi-d 2.  So,
+			 * we must keep track of lost intrs.
+			 * -- Cort
+			 */
+			while (1)
+			{
+				int i;
+				outb(0x0C, 0xA0);
+				i = inb(0xA0);
+				if ( !(i & 128) )
+					break;
+				irq &= 7;
+				irq += 8;
+				bits |= 1UL << irq;
+			}
+#endif			
 		}
-		bits = 1UL << irq;
+		else
+			bits = 1UL << irq;
+		  
 		break;
 #ifdef CONFIG_APUS
 	case _MACH_apus:
@@ -822,7 +543,6 @@
 		APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK);
 		APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET
 					  | (~(old_level) & IPLEMU_IPLMASK)));
-		
 apus_out:
 		hardirq_exit(cpu);
 		APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT);
@@ -831,14 +551,11 @@
 #endif	
 	}
 
-	if (irq < 0) {
-		/* we get here with Gatwick but the 'bogus' isn't correct in that case -- Cort */
-		if ( irq != second_irq )
-		{
-			printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
-			       irq, regs->nip);
-			spurious_interrupts++;
-		}
+	if (irq < 0)
+	{
+		printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
+		       irq, regs->nip);
+		spurious_interrupts++;
 		goto out;
 	}					
 	
@@ -848,136 +565,349 @@
 	 */
 	bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec;
 	irq = bits >> 26;
+	irq += ppc8xx_pic.irq_offset;
+	bits = 1UL << irq;
 #endif /* CONFIG_8xx */
-	mask_and_ack_irq(irq);
-	status = 0;
-	action = irq_action[irq];
-	kstat.irqs[cpu][irq]++;
-	if (action && action->handler) {
-		if (!(action->flags & SA_INTERRUPT))
-			__sti();
-		do { 
-			status |= action->flags;
-			action->handler(irq, action->dev_id, regs);
-			action = action->next;
-		} while ( action );
-		__cli();
-		unmask_irq(irq);
-	} else {
-#ifndef CONFIG_8xx	  
-		if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */
-#endif		  
-			spurious_interrupts++;
-		disable_irq( irq );
-	}
+#if 0
+	/*
+	 * this allows for > 1 interrupt at a time so we can
+	 * clear out any 'double' interrupts on prep and
+	 * finish up the lost interrupts.
+	 * It doesn't currently work for irqs > 31 so I'm leaving
+	 * it commented out for now.
+	 * -- Cort
+	 */
+	for ( i = 0 ; i < sizeof(bits)*8 ; i++ )
+		if ( bits & (1UL<<i) )
+			dispatch_handler( regs, i );
+#else	
+	dispatch_handler( regs, irq );
+#endif	
 	
-	/* This was a gatwick sub-interrupt, we re-enable them on Heathrow
-           now */
-	if (_machine == _MACH_Pmac && irq >= max_real_irqs)
-		unmask_irq(second_irq);
-
-	/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
-#ifndef CONFIG_8xx											
-	if ( is_prep && (irq > 7) )
-		goto retry_cascade;
-	/* do_bottom_half is called if necessary from int_return in head.S */
+#ifndef CONFIG_8xx
 out:
 	if (_machine == _MACH_chrp && !openpic_eoi_done)
 		openpic_eoi(0);
 #endif /* CONFIG_8xx */
 	hardirq_exit(cpu);
-
 #ifdef CONFIG_APUS
 out2:
 #endif
-	/* restore the HID0 in case dcache was off - see idle.c
-	 * this hack should leave for a better solution -- Cort */
-	lock_dcache(dcache_locked);
 }
 
-int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
-	unsigned long irqflags, const char * devname, void *dev_id)
+unsigned long probe_irq_on (void)
 {
-	struct irqaction *old, **p, *action;
-	unsigned long flags;
-
-#ifdef SHOW_IRQ
-	printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n",
-	       irq,(int)handler,devname,(int)dev_id);
-#endif /* SHOW_IRQ */
+	return 0;
+}
 
-	if (irq >= NR_IRQS)
-		return -EINVAL;
-
-	/* Cannot allocate second controller IRQ */
-	if (irq == second_irq)
-		return -EBUSY;
+int probe_irq_off (unsigned long irqs)
+{
+	return 0;
+}
 
-	if (!handler)
-	{
-		/* Free */
-		for (p = irq + irq_action; (action = *p) != NULL; p = &action->next)
-		{
-			/* Found it - now free it */
-			save_flags(flags);
-			cli();
-			*p = action->next;
-			restore_flags(flags);
-			irq_kfree(action);
-			return 0;
-		}
-		return -ENOENT;
+static void i8259_mask_and_ack_irq(unsigned int irq_nr)
+{
+	if ( irq_nr >= i8259_pic.irq_offset )	
+		irq_nr -= i8259_pic.irq_offset;
+	if (irq_nr > 7) {
+		cached_A1 |= 1 << (irq_nr-8);
+		inb(0xA1);	/* DUMMY */
+		outb(cached_A1,0xA1);
+		outb(0x62,0x20);	/* Specific EOI to cascade */
+		/*outb(0x20,0xA0);*/
+		outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */
+	} else {
+		cached_21 |= 1 << irq_nr;
+		inb(0x21);	/* DUMMY */
+		outb(cached_21,0x21);
+		/*outb(0x20,0x20);*/
+		outb(0x60|irq_nr,0x20); /* specific eoi */
 	}
+}
+
+static void i8259_set_irq_mask(int irq_nr)
+{
+	outb(cached_A1,0xA1);
+	outb(cached_21,0x21);
+}
+
+static void i8259_mask_irq(unsigned int irq_nr)
+{
+	if ( irq_nr >= i8259_pic.irq_offset )	
+		irq_nr -= i8259_pic.irq_offset;
+	if ( irq_nr < 8 )
+		cached_21 |= 1 << irq_nr;
+	else
+		cached_A1 |= 1 << (irq_nr-8);
+	i8259_set_irq_mask(irq_nr);
+}
+
+static void i8259_unmask_irq(unsigned int irq_nr)
+{
+  
+	if ( irq_nr >= i8259_pic.irq_offset )	
+		irq_nr -= i8259_pic.irq_offset;
+	if ( irq_nr < 8 )
+		cached_21 &= ~(1 << irq_nr);
+	else
+		cached_A1 &= ~(1 << (irq_nr-8));
+	i8259_set_irq_mask(irq_nr);
+}
+
+#ifndef CONFIG_8xx
+static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+	int irq, bits;
 	
-	action = (struct irqaction *)
-		irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL);
-	if (!action)
-		return -ENOMEM;
-	save_flags(flags);
-	cli();
-	
-	action->handler = handler;
-	action->flags = irqflags;					
-	action->mask = 0;
-	action->name = devname;
-	action->dev_id = dev_id;
-	action->next = NULL;
-	enable_irq(irq);
-	p = irq_action + irq;
+	for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) {
+		int i = irq >> 5;
+		bits = ld_le32(&pmac_irq_hw[i]->flag)
+			| lost_interrupts[i];
+		if (bits == 0)
+			continue;
+		irq -= cntlzw(bits);
+		break;
+	}
+	/* The previous version of this code allowed for this case, we
+	 * don't.  Put this here to check for it.
+	 * -- Cort
+	 */
+	if ( irq_desc[irq].ctl != &gatwick_pic )
+		printk("gatwick irq not from gatwick pic\n");
+	else
+		dispatch_handler( regs, irq );
+}
+
+void pmac_mask_and_ack_irq(unsigned int irq_nr)
+{
+        unsigned long bit = 1UL << (irq_nr & 0x1f);
+        int i = irq_nr >> 5;
+
+        if ((unsigned)irq_nr >= max_irqs)
+                return;
+
+        clear_bit(irq_nr, cached_irq_mask);
+        if (test_and_clear_bit(irq_nr, lost_interrupts))
+                atomic_dec(&n_lost_interrupts);
+        out_le32(&pmac_irq_hw[i]->ack, bit);
+        out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]);
+        out_le32(&pmac_irq_hw[i]->ack, bit);
+        do {
+                /* make sure ack gets to controller before we enable interrupts */
+		mb();
+        } while(in_le32(&pmac_irq_hw[i]->flag) & bit);
+
+}
+
+void __openfirmware chrp_mask_and_ack_irq(unsigned int irq_nr)
+{
+	if (is_8259_irq(irq_nr))
+	    i8259_mask_and_ack_irq(irq_nr);
+}
+
+static void pmac_set_irq_mask(int irq_nr)
+{
+	unsigned long bit = 1UL << (irq_nr & 0x1f);
+	int i = irq_nr >> 5;
+
+	if ((unsigned)irq_nr >= max_irqs)
+		return;
+
+	/* enable unmasked interrupts */
+	out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]);
 	
-	if ((old = *p) != NULL) {
-		/* Can't share interrupts unless both agree to */
-		if (!(old->flags & action->flags & SA_SHIRQ))
-			return -EBUSY;
-		/* add new interrupt at end of irq queue */
-		do {
-			p = &old->next;
-			old = *p;
-		} while (old);
+	do {
+		/* make sure mask gets to controller before we
+		   return to user */
+		mb();
+	} while((in_le32(&pmac_irq_hw[i]->enable) & bit)
+		!= (cached_irq_mask[i] & bit));
+
+	/*
+	 * Unfortunately, setting the bit in the enable register
+	 * when the device interrupt is already on *doesn't* set
+	 * the bit in the flag register or request another interrupt.
+	 */
+	if ((bit & cached_irq_mask[i])
+	    && (ld_le32(&pmac_irq_hw[i]->level) & bit)
+	    && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) {
+		if (!test_and_set_bit(irq_nr, lost_interrupts))
+			atomic_inc(&n_lost_interrupts);
 	}
-	*p = action;
+}
+
+static void pmac_mask_irq(unsigned int irq_nr)
+{
+	clear_bit(irq_nr, cached_irq_mask);
+	pmac_set_irq_mask(irq_nr);
+	mb();
+}
+
+static void pmac_unmask_irq(unsigned int irq_nr)
+{
+	set_bit(irq_nr, cached_irq_mask);
+	pmac_set_irq_mask(irq_nr);
+}
+
+static void __openfirmware chrp_mask_irq(unsigned int irq_nr)
+{
+	if (is_8259_irq(irq_nr))
+		i8259_mask_irq(irq_nr);
+	else
+		openpic_disable_irq(irq_to_openpic(irq_nr));
+}
+
+static void __openfirmware chrp_unmask_irq(unsigned int irq_nr)
+{
+	if (is_8259_irq(irq_nr))
+		i8259_unmask_irq(irq_nr);
+	else
+		openpic_enable_irq(irq_to_openpic(irq_nr));
+}
+
+/* This routine will fix some missing interrupt values in the device tree
+ * on the gatwick mac-io controller used by some PowerBooks
+ */
+static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base)
+{
+	struct device_node *node;
+	int count;
 	
-	restore_flags(flags);	
-	return 0;
+	memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool));
+	node = gw->child;
+	count = 0;
+	while(node)
+	{
+		/* Fix SCC */
+		if (strcasecmp(node->name, "escc") == 0)
+			if (node->child) {
+				if (node->child->n_intrs < 3) {
+					node->child->intrs = &gatwick_int_pool[count];
+					count += 3;
+				}
+				node->child->n_intrs = 3;				
+				node->child->intrs[0].line = 15+irq_base;
+				node->child->intrs[1].line =  4+irq_base;
+				node->child->intrs[2].line =  5+irq_base;
+				printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n",
+					node->child->intrs[0].line,
+					node->child->intrs[1].line,
+					node->child->intrs[2].line);
+			}
+		/* Fix media-bay & left SWIM */
+		if (strcasecmp(node->name, "media-bay") == 0) {
+			struct device_node* ya_node;
+
+			if (node->n_intrs == 0)
+				node->intrs = &gatwick_int_pool[count++];
+			node->n_intrs = 1;
+			node->intrs[0].line = 29+irq_base;
+			printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n",
+					node->intrs[0].line);
+			
+			ya_node = node->child;
+			while(ya_node)
+			{
+				if (strcasecmp(ya_node->name, "floppy") == 0) {
+					if (ya_node->n_intrs < 2) {
+						ya_node->intrs = &gatwick_int_pool[count];
+						count += 2;
+					}
+					ya_node->n_intrs = 2;
+					ya_node->intrs[0].line = 19+irq_base;
+					ya_node->intrs[1].line =  1+irq_base;
+					printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n",
+						ya_node->intrs[0].line, ya_node->intrs[1].line);
+				} 
+				if (strcasecmp(ya_node->name, "ata4") == 0) {
+					if (ya_node->n_intrs < 2) {
+						ya_node->intrs = &gatwick_int_pool[count];
+						count += 2;
+					}
+					ya_node->n_intrs = 2;
+					ya_node->intrs[0].line = 14+irq_base;
+					ya_node->intrs[1].line =  3+irq_base;
+					printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n",
+						ya_node->intrs[0].line, ya_node->intrs[1].line);
+				} 
+				ya_node = ya_node->sibling;
+			}
+		}
+		node = node->sibling;
+	}
+	if (count > 10) {
+		printk("WARNING !! Gatwick interrupt pool overflow\n");
+		printk("  GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE);
+		printk("              requested = %d\n", count);
+	}
 }
 
-void free_irq(unsigned int irq, void *dev_id)
+#ifdef __SMP__
+static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs)
 {
-	request_irq(irq, NULL, 0, NULL, dev_id);
+	smp_message_recv();
 }
+#endif /* __SMP__ */
 
-unsigned long probe_irq_on (void)
+
+#else /* CONFIG_8xx */
+
+static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs)
 {
-	return 0;
+	int bits, irq;
+
+	/* A bug in the QSpan chip causes it to give us 0xff always
+	 * when doing a character read.  So read 32 bits and shift.
+	 * This doesn't seem to return useful values anyway, but
+	 * read it to make sure things are acked.
+	 * -- Cort
+	 */
+	irq = (inl(0x508) >> 24)&0xff;
+	if ( irq != 0xff ) printk("iack %d\n", irq);
+	
+	outb(0x0C, 0x20);
+	irq = inb(0x20) & 7;
+	if (irq == 2)
+	{
+		outb(0x0C, 0xA0);
+		irq = inb(0xA0);
+		irq = (irq&7) + 8;
+	}
+	bits = 1UL << irq;
+	irq += i8259_pic.irq_offset;
+	dispatch_handler( regs, irq );
 }
 
-int probe_irq_off (unsigned long irqs)
+static void mbx_mask_and_ack(unsigned int irq_nr)
+{
+	/* this shouldn't be masked, we mask the 8259 if we need to -- Cort */
+	if ( irq_nr != ISA_BRIDGE_INT )
+		mbx_mask_irq(irq_nr);
+	if ( irq_nr >= ppc8xx_pic.irq_offset )
+		irq_nr -= ppc8xx_pic.irq_offset;
+	/* clear the pending bits */
+	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-irq_nr);
+}
+
+static void mbx_mask_irq(unsigned int irq_nr)
 {
-	return 0;
+	if ( irq_nr == ISA_BRIDGE_INT ) return;
+	if ( irq_nr >= ppc8xx_pic.irq_offset )
+		irq_nr -= ppc8xx_pic.irq_offset;
+	cached_irq_mask[0] &= ~(1 << (31-irq_nr));
+	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =	cached_irq_mask[0];
 }
 
-#ifndef CONFIG_8xx 
-__initfunc(static void i8259_init(void))
+static void mbx_unmask_irq(unsigned int irq_nr)
+{
+	if ( irq_nr >= ppc8xx_pic.irq_offset )
+		irq_nr -= ppc8xx_pic.irq_offset;
+	cached_irq_mask[0] |= (1 << (31-irq_nr));
+	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =	cached_irq_mask[0];
+}
+#endif /* CONFIG_8xx */
+
+static void __init i8259_init(void)
 {
 	/* init master interrupt controller */
 	outb(0x11, 0x20); /* Start init sequence */
@@ -985,7 +915,6 @@
 	outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
 	outb(0x01, 0x21); /* Select 8086 mode */
 	outb(0xFF, 0x21); /* Mask all */
-	
 	/* init slave interrupt controller */
 	outb(0x11, 0xA0); /* Start init sequence */
 	outb(0x08, 0xA1); /* Vector base */
@@ -994,32 +923,31 @@
 	outb(0xFF, 0xA1); /* Mask all */
 	outb(cached_A1, 0xA1);
 	outb(cached_21, 0x21);
-	if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0)
-		panic("Could not allocate cascade IRQ!");
-	enable_irq(2);  /* Enable cascade interrupt */
+	request_irq( i8259_pic.irq_offset + 2, no_action, SA_INTERRUPT,
+		     "8259 secondary cascade", NULL );
+	enable_irq(i8259_pic.irq_offset + 2);  /* Enable cascade interrupt */
 }
-#endif /* CONFIG_8xx */
 
-/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug.  External
- * interrupts can be either edge or level triggered, but there is no
- * reason for us to change the EPPC-bug values (it would not work if we did).
- */
-__initfunc(void init_IRQ(void))
+void __init init_IRQ(void)
 {
 	extern void xmon_irq(int, void *, struct pt_regs *);
 	int i;
+	static int once = 0;
+#ifndef CONFIG_8xx
 	struct device_node *irqctrler;
 	unsigned long addr;
 	struct device_node *np;
-
+	int second_irq = -999;
+#endif
+	if ( once )
+		return;
+	else
+		once++;
+	
 #ifndef CONFIG_8xx
 	switch (_machine)
 	{
 	case _MACH_Pmac:
-		mask_and_ack_irq = pmac_mask_and_ack_irq;
-		mask_irq = pmac_mask_irq;
-		unmask_irq = pmac_unmask_irq;
-
 		/* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, 
 		   others have 32 */
 		max_irqs = max_real_irqs = 32;
@@ -1032,7 +960,9 @@
 			else
 				max_irqs = 64;
 		}
-		
+		for ( i = 0; i < max_real_irqs ; i++ )
+			irq_desc[i].ctl = &pmac_pic;
+
 		/* get addresses of first controller */
 		if (irqctrler) {
 			if  (irqctrler->n_addrs > 0) {
@@ -1057,7 +987,6 @@
 		/* disable all interrupts in all controllers */
 		for (i = 0; i * 32 < max_irqs; ++i)
 			out_le32(&pmac_irq_hw[i]->enable, 0);
-			
 		
 		/* get interrupt line of secondary interrupt controller */
 		if (irqctrler) {
@@ -1066,7 +995,10 @@
 				(int)second_irq);
 			if (device_is_compatible(irqctrler, "gatwick"))
 				pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs);
-			enable_irq(second_irq);
+			for ( i = max_real_irqs ; i < max_irqs ; i++ )
+				irq_desc[i].ctl = &gatwick_pic;
+			request_irq( second_irq, gatwick_action, SA_INTERRUPT,
+				     "gatwick cascade", 0 );
 		}
 		printk("System has %d possible interrupts\n", max_irqs);
 		if (max_irqs != max_real_irqs)
@@ -1074,124 +1006,287 @@
 				max_real_irqs);
 
 #ifdef CONFIG_XMON
-		request_irq(20, xmon_irq, 0, "NMI", 0);
+		request_irq(20, xmon_irq, 0, "NMI - XMON", 0);
 #endif	/* CONFIG_XMON */
 		break;
 	case _MACH_chrp:
-		mask_and_ack_irq = chrp_mask_and_ack_irq;
-		mask_irq = chrp_mask_irq;
-		unmask_irq = chrp_unmask_irq;
-		
 		if ( !(np = find_devices("pci") ) )
 			printk("Cannot find pci to get ack address\n");
 		else
 		{
 			chrp_int_ack_special = (volatile unsigned char *)
-			   (*(unsigned long *)get_property(np,
+				(*(unsigned long *)get_property(np,
 					"8259-interrupt-acknowledge", NULL));
 		}
+		for ( i = 16 ; i < 36 ; i++ )
+			irq_desc[i].ctl = &open_pic;
+		/* openpic knows that it's at irq 16 offset
+		 * so we don't need to set it in the pic structure
+		 * -- Cort
+		 */
 		openpic_init(1);
+		for ( i = 0 ; i < 16  ; i++ )
+			irq_desc[i].ctl = &i8259_pic;
 		i8259_init();
-		cached_irq_mask[0] = cached_irq_mask[1] = ~0UL;
 #ifdef CONFIG_XMON
 		request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI),
 			    xmon_irq, 0, "NMI", 0);
 #endif	/* CONFIG_XMON */
+#ifdef __SMP__
+		request_irq(openpic_to_irq(OPENPIC_VEC_SPURIOUS),
+			    openpic_ipi_action, 0, "IPI0", 0);
+#endif	/* __SMP__ */
 		break;
 	case _MACH_prep:
-		mask_and_ack_irq = i8259_mask_and_ack_irq;
-		mask_irq = i8259_mask_irq;
-		unmask_irq = i8259_unmask_irq;
-		cached_irq_mask[0] = ~0UL;
-		
+		for ( i = 0 ; i < 16  ; i++ )
+			irq_desc[i].ctl = &i8259_pic;
 		i8259_init();
-		/*
-		 * According to the Carolina spec from ibm irqs 0,1,2, and 8
-		 * must be edge triggered.  Also, the pci intrs must be level
-		 * triggered and _only_ isa intrs can be level sensitive
-		 * which are 3-7,9-12,14-15. 13 is special - it can be level.
-		 *
-		 * power on default is 0's in both regs - all edge.
-		 *
-		 * These edge/level control regs allow edge/level status
-		 * to be decided on a irq basis instead of on a PIC basis.
-		 * It's still pretty ugly.
-		 * - Cort
-		 */
-		{
-			unsigned char irq_mode1 = 0, irq_mode2 = 0;
-			irq_mode1 = 0; /* to get rid of compiler warnings */
-			/*
-			 * On Carolina, irq 15 and 13 must be level (scsi/ide/net).
-			 */
-			if ( _prep_type == _PREP_IBM )
-				irq_mode2 |= 0xa0;
-		}
 		break;
 #ifdef CONFIG_APUS		
 	case _MACH_apus:
-		mask_irq = amiga_disable_irq;
-		unmask_irq = amiga_enable_irq;
 		apus_init_IRQ();
 		break;
 #endif	
 	}
-#endif /* CONFIG_8xx */
+#else /* CONFIG_8xx */
+	ppc8xx_pic.irq_offset = 16;
+	for ( i = 16 ; i < 32 ; i++ )
+		irq_desc[i].ctl = &ppc8xx_pic;
+	unmask_irq(CPM_INTERRUPT);
+
+	for ( i = 0 ; i < 16 ; i++ )
+		irq_desc[i].ctl = &i8259_pic;
+	i8259_init();
+	request_irq(ISA_BRIDGE_INT, mbx_i8259_action, 0, "8259 cascade", NULL);
+	enable_irq(ISA_BRIDGE_INT);
+#endif  /* CONFIG_8xx */
 }
 
-/* This routine will fix some missing interrupt values in the device tree
- * on the gatwick mac-io controller used by some PowerBooks
- */
-static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base)
+#ifdef __SMP__
+unsigned char global_irq_holder = NO_PROC_ID;
+unsigned volatile int global_irq_lock;
+atomic_t global_irq_count;
+
+atomic_t global_bh_count;
+atomic_t global_bh_lock;
+
+static void show(char * str)
 {
-	struct device_node *node;
-	static struct interrupt_info int_pool[4];
-	
-	memset(int_pool, 0, sizeof(int_pool));
-	node = gw->child;
-	while(node)
-	{
-		/* Fix SCC */
-		if (strcasecmp(node->name, "escc") == 0)
-			if (node->child && node->child->n_intrs == 0)
-			{
-				node->child->n_intrs = 1;
-				node->child->intrs = &int_pool[0];
-				int_pool[0].line = 15+irq_base;
-				printk(KERN_INFO "irq: fixed SCC on second controller (%d)\n",
-					int_pool[0].line);
-			}
-		/* Fix media-bay & left SWIM */
-		if (strcasecmp(node->name, "media-bay") == 0)
-		{
-			struct device_node* ya_node;
+	int i;
+	unsigned long *stack;
+	int cpu = smp_processor_id();
 
-			if (node->n_intrs == 0)
-			{
-				node->n_intrs = 1;
-				node->intrs = &int_pool[1];
-				int_pool[1].line = 29+irq_base;
-				printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n",
-					int_pool[1].line);
-			}
-			ya_node = node->child;
-			while(ya_node)
-			{
-				if ((strcasecmp(ya_node->name, "floppy") == 0) &&
-					ya_node->n_intrs == 0)
-				{
-					ya_node->n_intrs = 2;
-					ya_node->intrs = &int_pool[2];
-					int_pool[2].line = 19+irq_base;
-					int_pool[3].line =  1+irq_base;
-					printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n",
-						int_pool[2].line, int_pool[3].line);
-				} 
-				ya_node = ya_node->sibling;
+	printk("\n%s, CPU %d:\n", str, cpu);
+	printk("irq:  %d [%d %d]\n",
+		atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]);
+	printk("bh:   %d [%d %d]\n",
+		atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]);
+	stack = (unsigned long *) &str;
+	for (i = 40; i ; i--) {
+		unsigned long x = *++stack;
+		if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) {
+			printk("<[%08lx]> ", x);
+		}
+	}
+}
+
+static inline void wait_on_bh(void)
+{
+	int count = MAXCOUNT;
+	do {
+		if (!--count) {
+			show("wait_on_bh");
+			count = ~0;
+		}
+		/* nothing .. wait for the other bh's to go away */
+	} while (atomic_read(&global_bh_count) != 0);
+}
+
+
+static inline void wait_on_irq(int cpu)
+{
+	int count = MAXCOUNT;
+
+	for (;;) {
+
+		/*
+		 * Wait until all interrupts are gone. Wait
+		 * for bottom half handlers unless we're
+		 * already executing in one..
+		 */
+		if (!atomic_read(&global_irq_count)) {
+			if (local_bh_count[cpu] || !atomic_read(&global_bh_count))
+				break;
+		}
+
+		/* Duh, we have to loop. Release the lock to avoid deadlocks */
+		clear_bit(0,&global_irq_lock);
+
+		for (;;) {
+			if (!--count) {
+				show("wait_on_irq");
+				count = ~0;
 			}
+			__sti();
+			/* don't worry about the lock race Linus found
+			 * on intel here. -- Cort
+			 */
+			__cli();
+			if (atomic_read(&global_irq_count))
+				continue;
+			if (global_irq_lock)
+				continue;
+			if (!local_bh_count[cpu] && atomic_read(&global_bh_count))
+				continue;
+			if (!test_and_set_bit(0,&global_irq_lock))
+				break;
 		}
-		node = node->sibling;
 	}
+}
+
+/*
+ * This is called when we want to synchronize with
+ * bottom half handlers. We need to wait until
+ * no other CPU is executing any bottom half handler.
+ *
+ * Don't wait if we're already running in an interrupt
+ * context or are inside a bh handler.
+ */
+void synchronize_bh(void)
+{
+	if (atomic_read(&global_bh_count) && !in_interrupt())
+		wait_on_bh();
+}
+
+/*
+ * This is called when we want to synchronize with
+ * interrupts. We may for example tell a device to
+ * stop sending interrupts: but to make sure there
+ * are no interrupts that are executing on another
+ * CPU we need to call this function.
+ */
+void synchronize_irq(void)
+{
+	if (atomic_read(&global_irq_count)) {
+		/* Stupid approach */
+		cli();
+		sti();
+	}
+}
+
+static inline void get_irqlock(int cpu)
+{
+	unsigned int loops = MAXCOUNT;
+
+	if (test_and_set_bit(0,&global_irq_lock)) {
+		/* do we already hold the lock? */
+		if ((unsigned char) cpu == global_irq_holder)
+			return;
+		/* Uhhuh.. Somebody else got it. Wait.. */
+		do {
+			do {
+				if (loops-- == 0) {
+					printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder);
+#ifdef CONFIG_XMON
+					xmon(0);
+#endif
+				}
+			} while (test_bit(0,&global_irq_lock));
+		} while (test_and_set_bit(0,&global_irq_lock));		
+	}
+	/* 
+	 * We also need to make sure that nobody else is running
+	 * in an interrupt context. 
+	 */
+	wait_on_irq(cpu);
+
+	/*
+	 * Ok, finally..
+	 */
+	global_irq_holder = cpu;
+}
+
+/*
+ * A global "cli()" while in an interrupt context
+ * turns into just a local cli(). Interrupts
+ * should use spinlocks for the (very unlikely)
+ * case that they ever want to protect against
+ * each other.
+ *
+ * If we already have local interrupts disabled,
+ * this will not turn a local disable into a
+ * global one (problems with spinlocks: this makes
+ * save_flags+cli+sti usable inside a spinlock).
+ */
+void __global_cli(void)
+{
+	unsigned int flags;
 	
+	__save_flags(flags);
+	if (flags & (1 << 15)) {
+		int cpu = smp_processor_id();
+		__cli();
+		if (!local_irq_count[cpu])
+			get_irqlock(cpu);
+	}
 }
+
+void __global_sti(void)
+{
+	int cpu = smp_processor_id();
+
+	if (!local_irq_count[cpu])
+		release_irqlock(cpu);
+	__sti();
+}
+
+/*
+ * SMP flags value to restore to:
+ * 0 - global cli
+ * 1 - global sti
+ * 2 - local cli
+ * 3 - local sti
+ */
+unsigned long __global_save_flags(void)
+{
+	int retval;
+	int local_enabled;
+	unsigned long flags;
+
+	__save_flags(flags);
+	local_enabled = (flags >> 15) & 1;
+	/* default to local */
+	retval = 2 + local_enabled;
+
+	/* check for global flags if we're not in an interrupt */
+	if (!local_irq_count[smp_processor_id()]) {
+		if (local_enabled)
+			retval = 1;
+		if (global_irq_holder == (unsigned char) smp_processor_id())
+			retval = 0;
+	}
+	return retval;
+}
+
+void __global_restore_flags(unsigned long flags)
+{
+	switch (flags) {
+	case 0:
+		__global_cli();
+		break;
+	case 1:
+		__global_sti();
+		break;
+	case 2:
+		__cli();
+		break;
+	case 3:
+		__sti();
+		break;
+	default:
+		printk("global_restore_flags: %08lx (%08lx)\n",
+			flags, (&flags)[-1]);
+	}
+}
+#endif /* __SMP__ */
 
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.2.3/linux/arch/ppc/kernel/misc.S	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/kernel/misc.S	Wed Mar 10 21:30:32 1999
@@ -109,7 +109,13 @@
  * Flush MMU TLB
  */
 _GLOBAL(_tlbia)
+	sync
 	tlbia
+	sync
+#ifdef __SMP__
+	tlbsync
+	sync
+#endif
 	blr	
 
 /*
@@ -117,11 +123,17 @@
  */
 _GLOBAL(_tlbie)
 	tlbie	r3
+	sync
+#ifdef __SMP__
+	tlbsync
+	sync
+#endif
 	blr
+
 /*
  * Atomic [test&set] exchange
  *
- *	void *xchg_u32(void *ptr, unsigned long val)
+ *	unsigned long xchg_u32(void *ptr, unsigned long val)
  * Changes the memory location '*ptr' to be val and returns
  * the previous value stored there.
  */
@@ -133,6 +145,27 @@
 	blr
 
 /*
+ * Try to acquire a spinlock.
+ * Only does the stwcx. if the load returned 0 - the Programming
+ * Environments Manual suggests not doing unnecessary stcwx.'s
+ * since they may inhibit forward progress by other CPUs in getting
+ * a lock.
+ */
+_GLOBAL(__spin_trylock)
+	mr	r4,r3
+	eieio			/* prevent reordering of stores */
+	li	r5,-1
+	lwarx	r3,0,r4		/* fetch old value, establish reservation */
+	cmpwi	0,r3,0		/* is it 0? */
+	bnelr-			/* return failure if not */
+	stwcx.	r5,0,r4		/* try to update with new value */
+	bne-	1f		/* if we failed */
+	eieio			/* prevent reordering of stores */
+	blr
+1:	li	r3,1		/* return non-zero for failure */
+	blr
+
+/*
  * Atomic add/sub/inc/dec operations
  *
  * void atomic_add(int c, int *v)
@@ -580,6 +613,16 @@
 	stfd	0,-4(r5)
 	blr
 
+        .globl  __clear_msr_me
+__clear_msr_me:
+        mfmsr   r0                      /* Get current interrupt state */
+        lis     r3,0
+        ori     r3,r3,MSR_ME
+        andc    r0,r0,r3                /* Clears bit in (r4) */
+        sync                            /* Some chip revs have problems here */
+        mtmsr   r0                      /* Update machine state */
+        blr
+
 _GLOBAL(cvt_df)
 cvt_df:
 	lfd	0,-4(r5)	/* load up fpscr value */
@@ -836,4 +879,5 @@
 	.long sys_sendfile
 	.long sys_ni_syscall		/* streams1 */
 	.long sys_ni_syscall		/* streams2 */
+	.long sys_vfork
 	.space (NR_syscalls-183)*4
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/openpic.c linux/arch/ppc/kernel/openpic.c
--- v2.2.3/linux/arch/ppc/kernel/openpic.c	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/kernel/openpic.c	Fri Mar 19 10:50:03 1999
@@ -107,7 +107,7 @@
 }
 #endif
 
-static inline u_int openpic_read(volatile u_int *addr)
+u_int openpic_read(volatile u_int *addr)
 {
     u_int val;
 
@@ -176,8 +176,8 @@
 __initfunc(void openpic_init(int main_pic))
 {
     u_int t, i;
-    u_int vendorid, devid, stepping, timerfreq;
-    const char *version, *vendor, *device;
+    u_int timerfreq;
+    const char *version;
 
     if (!OpenPIC)
 	panic("No OpenPIC found");
@@ -200,32 +200,6 @@
 		  OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1;
     printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version,
 	   NumProcessors, NumSources, OpenPIC);
-
-    t = openpic_read(&OpenPIC->Global.Vendor_Identification);
-    vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK;
-    devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >>
-	    OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT;
-    stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >>
-	       OPENPIC_VENDOR_ID_STEPPING_SHIFT;
-    switch (vendorid) {
-	case OPENPIC_VENDOR_ID_APPLE:
-	    vendor = "Apple";
-	    break;
-	default:
-	    vendor = "Unknown";
-	    break;
-    }
-    switch (devid) {
-	case OPENPIC_DEVICE_ID_APPLE_HYDRA:
-	    device = "Hydra";
-	    break;
-	default:
-	    device = "Unknown";
-	    break;
-    }
-    printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid,
-	   vendor, devid, device, stepping);
-
     timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency);
     printk("OpenPIC timer frequency is ");
     if (timerfreq)
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- v2.2.3/linux/arch/ppc/kernel/pci.c	Fri Jan  8 22:36:03 1999
+++ linux/arch/ppc/kernel/pci.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: pci.c,v 1.43 1998/12/29 18:55:11 cort Exp $
+ * $Id: pci.c,v 1.53 1999/03/12 23:38:02 cort Exp $
  * Common pmac/prep/chrp pci routines. -- Cort
  */
 
@@ -24,10 +24,11 @@
 unsigned long isa_io_base;
 unsigned long isa_mem_base;
 unsigned long pci_dram_offset;
-
 unsigned int * pci_config_address;
 unsigned char * pci_config_data;
 
+static void fix_intr(struct device_node *node, struct pci_dev *dev);
+
 /*
  * It would be nice if we could create a include/asm/pci.h and have just
  * function ptrs for all these in there, but that isn't the case.
@@ -73,12 +74,14 @@
 	ptr_pcibios_write_config_word = name##_pcibios_write_config_word; \
 	ptr_pcibios_write_config_dword = name##_pcibios_write_config_dword
 
+decl_config_access_method(mech1);
 decl_config_access_method(pmac);
 decl_config_access_method(grackle);
 decl_config_access_method(gg2);
 decl_config_access_method(raven);
 decl_config_access_method(prep);
 decl_config_access_method(mbx);
+decl_config_access_method(python);
 
 int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
 			     unsigned char offset, unsigned char *val)
@@ -116,17 +119,24 @@
 	return 1;
 }
 
-__initfunc(void pcibios_init(void))
+void __init pcibios_init(void)
 {
 }
 
-__initfunc(void
-	   setup_pci_ptrs(void))
+void __init setup_pci_ptrs(void)
 {
-#ifndef CONFIG_MBX  
+#ifndef CONFIG_MBX
 	PPC_DEVICE *hostbridge;
 	switch (_machine) {
 	case _MACH_prep:
+                printk("PReP architecture\n");
+                if ( _prep_type == _PREP_Radstone )
+                {
+                        printk("Setting PCI access method to mech1\n");
+                        set_config_access_method(mech1);
+                        break;
+                }
+
 	  	hostbridge=residual_find_device(PROCESSORDEVICE, NULL,
 						BridgeController, PCIBridge,
 						-1, 0);
@@ -163,13 +173,30 @@
 		if ( !strncmp("MOT",
 			      get_property(find_path_device("/"), "model", NULL),3) )
 		{
+			pci_dram_offset = 0;
+			isa_mem_base = 0xf7000000;
 			isa_io_base = 0xfe000000;
 			set_config_access_method(grackle);
 		}
 		else
 		{
-			isa_io_base = GG2_ISA_IO_BASE;
-			set_config_access_method(gg2);
+			if ( !strncmp("F5",
+			      get_property(find_path_device("/"),
+					   "ibm,model-class", NULL),2) )
+		
+			{
+				pci_dram_offset = 0x80000000;
+				isa_mem_base = 0xa0000000;
+				isa_io_base = 0x88000000;
+				set_config_access_method(python);
+			}
+			else
+			{
+				pci_dram_offset = 0;
+				isa_mem_base = 0xf7000000;
+				isa_io_base = 0xf8000000;
+				set_config_access_method(gg2);
+			}
 		}
 		break;
 	default:
@@ -181,7 +208,7 @@
 #undef set_config_access_method
 }
 
-__initfunc(void pcibios_fixup(void))
+void __init pcibios_fixup(void)
 {
 	extern unsigned long route_pci_interrupts(void);
 	struct pci_dev *dev;
@@ -193,6 +220,12 @@
 	switch (_machine )
 	{
 	case _MACH_prep:
+                if ( _prep_type == _PREP_Radstone )
+                {
+                        printk("Radstone boards require no PCI fixups\n");
+                        break;
+                }
+
 		route_pci_interrupts();
 		for(dev=pci_devices; dev; dev=dev->next)
 		{
@@ -207,7 +240,7 @@
 			{
 				if ( dev->base_address[i] > 0x10000000 )
 				{
-					printk("Relocating PCI address %x -> %x\n",
+					printk("Relocating PCI address %lx -> %lx\n",
 					       dev->base_address[i],
 					       (dev->base_address[i] & 0x00FFFFFF)
 				               | 0x01000000);
@@ -245,23 +278,12 @@
 			 * AAPL,interrupts property.
 			 */
 			struct bridge_data *bp = bridges[dev->bus->number];
-			struct device_node *node;
-			unsigned int *reg;
 			unsigned char pin;
-			
+
 			if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) ||
-			    !pin)
+			    !pin) 
 				continue;	/* No interrupt generated -> no fixup */
-			for (node = bp->node->child; node != 0;
-			     node = node->sibling) {
-				reg = (unsigned int *) get_property(node, "reg", 0);
-				if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn)
-					continue;
-				/* this is the node, see if it has interrupts */
-				if (node->n_intrs > 0)
-					dev->irq = node->intrs[0].line;
-				break;
-			}
+			fix_intr(bp->node->child, dev);
 		}
 		break;
 	}
@@ -272,11 +294,36 @@
 #endif /* CONFIG_MBX */
 }
 
-__initfunc(void pcibios_fixup_bus(struct pci_bus *bus))
+void __init pcibios_fixup_bus(struct pci_bus *bus)
 {
 }
 
-__initfunc(char *pcibios_setup(char *str))
+char __init *pcibios_setup(char *str)
 {
 	return str;
 }
+
+#ifndef CONFIG_MBX
+/* Recursively searches any node that is of type PCI-PCI bridge. Without
+ * this, the old code would miss children of P2P bridges and hence not
+ * fix IRQ's for cards located behind P2P bridges.
+ * - Ranjit Deshpande, 01/20/99
+ */
+static void __init fix_intr(struct device_node *node, struct pci_dev *dev)
+{
+	unsigned int *reg, *class_code;
+
+	for (; node != 0;node = node->sibling) {
+		class_code = (unsigned int *) get_property(node, "class-code", 0);
+		if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI)
+			fix_intr(node->child, dev);
+		reg = (unsigned int *) get_property(node, "reg", 0);
+		if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn)
+			continue;
+		/* this is the node, see if it has interrupts */
+		if (node->n_intrs > 0) 
+			dev->irq = node->intrs[0].line;
+		break;
+	}
+}
+#endif
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- v2.2.3/linux/arch/ppc/kernel/pmac_pci.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/pmac_pci.c	Wed Mar 10 21:30:32 1999
@@ -84,7 +84,12 @@
 			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
 			 + (offset & ~3));
 	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+		/* Bus number once again taken into consideration.
+		 * Change applied from 2.1.24. This makes devices located
+		 * behind PCI-PCI bridges visible.
+		 * -Ranjit Deshpande, 01/20/99
+		 */
+		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
 	}
 	udelay(2);
 	*val = in_8(bp->cfg_data + (offset & 3));
@@ -109,7 +114,8 @@
 			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
 			 + (offset & ~3));
 	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+		/* See pci_read_config_byte */
+		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
 	}
 	udelay(2);
 	*val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)));
@@ -134,7 +140,8 @@
 			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
 			 + offset);
 	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
+		/* See pci_read_config_byte */
+		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + offset + 1);
 	}
 	udelay(2);
 	*val = in_le32((volatile unsigned int *)bp->cfg_data);
@@ -156,7 +163,8 @@
 			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
 			 + (offset & ~3));
 	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+		/* See pci_read_config_byte */
+		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
 	}
 	udelay(2);
 	out_8(bp->cfg_data + (offset & 3), val);
@@ -180,7 +188,8 @@
 			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
 			 + (offset & ~3));
 	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+		/* See pci_read_config_byte */
+		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
 	}
 	udelay(2);
 	out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val);
@@ -204,7 +213,8 @@
 			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
 			 + offset);
 	} else {
-		out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
+		/* See pci_read_config_byte */
+		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
 	}
 	udelay(2);
 	out_le32((volatile unsigned int *)bp->cfg_data, val);
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- v2.2.3/linux/arch/ppc/kernel/pmac_setup.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/pmac_setup.c	Wed Mar 10 21:30:32 1999
@@ -56,6 +56,9 @@
 
 unsigned char drive_info;
 
+int ppc_override_l2cr = 0;
+int ppc_override_l2cr_value;
+
 extern char saved_command_line[];
 
 #define DEFAULT_ROOT_DEVICE 0x0801	/* sda1 - slightly silly choice */
@@ -132,6 +135,16 @@
 		}
 	}
 
+	/* Checks "l2cr-value" property in the registry */
+	np = find_devices("cpus");		
+	if (np != 0) {
+		unsigned int *l2cr = (unsigned int *)
+			get_property(np, "l2cr-value", NULL);
+		if (l2cr != 0) {
+			len += sprintf(buffer+len, "l2cr override\t: 0x%x\n", *l2cr);
+		}
+	}
+	
 	return len;
 }
 
@@ -209,6 +222,26 @@
 	ohare_init();
 
 	*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+
+	/* Checks "l2cr-value" property in the registry */
+	if ( (_get_PVR() >> 16) == 8) {
+		struct device_node *np = find_devices("cpus");		
+		if (np != 0) {
+			unsigned int *l2cr = (unsigned int *)
+				get_property(np, "l2cr-value", NULL);
+			if (l2cr != 0) {
+				ppc_override_l2cr = 1;
+				ppc_override_l2cr_value = *l2cr;
+				_set_L2CR(0);
+				_set_L2CR(ppc_override_l2cr_value);
+			}
+		}
+	}
+
+	if (ppc_override_l2cr)
+		printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n",
+			ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000)
+				? "enabled" : "disabled");
 
 	feature_init();
 
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/ppc_defs.h linux/arch/ppc/kernel/ppc_defs.h
--- v2.2.3/linux/arch/ppc/kernel/ppc_defs.h	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/kernel/ppc_defs.h	Wed Dec 31 16:00:00 1969
@@ -1,69 +0,0 @@
-/*
- * WARNING! This file is automatically generated - DO NOT EDIT!
- */
-#define	KERNELBASE	-1073741824
-#define	STATE	0
-#define	NEXT_TASK	48
-#define	COUNTER	24
-#define	PROCESSOR	36
-#define	SIGPENDING	8
-#define	TSS	568
-#define	MM	872
-#define	TASK_STRUCT_SIZE	912
-#define	KSP	0
-#define	PG_TABLES	4
-#define	PGD	8
-#define	LAST_SYSCALL	20
-#define	PT_REGS	12
-#define	PF_TRACESYS	32
-#define	TASK_FLAGS	4
-#define	NEED_RESCHED	20
-#define	TSS_FPR0	24
-#define	TSS_FPSCR	284
-#define	TSS_SMP_FORK_RET	288
-#define	TASK_UNION_SIZE	8192
-#define	STACK_FRAME_OVERHEAD	16
-#define	INT_FRAME_SIZE	192
-#define	GPR0	16
-#define	GPR1	20
-#define	GPR2	24
-#define	GPR3	28
-#define	GPR4	32
-#define	GPR5	36
-#define	GPR6	40
-#define	GPR7	44
-#define	GPR8	48
-#define	GPR9	52
-#define	GPR10	56
-#define	GPR11	60
-#define	GPR12	64
-#define	GPR13	68
-#define	GPR14	72
-#define	GPR15	76
-#define	GPR16	80
-#define	GPR17	84
-#define	GPR18	88
-#define	GPR19	92
-#define	GPR20	96
-#define	GPR21	100
-#define	GPR22	104
-#define	GPR23	108
-#define	GPR24	112
-#define	GPR25	116
-#define	GPR26	120
-#define	GPR27	124
-#define	GPR28	128
-#define	GPR29	132
-#define	GPR30	136
-#define	GPR31	140
-#define	_NIP	144
-#define	_MSR	148
-#define	_CTR	156
-#define	_LINK	160
-#define	_CCR	168
-#define	_XER	164
-#define	_DAR	180
-#define	_DSISR	184
-#define	ORIG_GPR3	152
-#define	RESULT	188
-#define	TRAP	176
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- v2.2.3/linux/arch/ppc/kernel/ppc_ksyms.c	Fri Jan  8 22:36:03 1999
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Wed Mar 10 21:30:32 1999
@@ -8,11 +8,12 @@
 #include <linux/vt_kern.h>
 #include <linux/nvram.h>
 
+#include <asm/page.h>
 #include <asm/semaphore.h>
 #include <asm/processor.h>
 #include <asm/uaccess.h>
-#include <asm/ide.h>
 #include <asm/io.h>
+#include <asm/ide.h>
 #include <asm/atomic.h>
 #include <asm/bitops.h>
 #include <asm/checksum.h>
@@ -46,6 +47,7 @@
 asmlinkage long long __ashrdi3(long long, int);
 asmlinkage int abs(int);
 
+EXPORT_SYMBOL(clear_page);
 EXPORT_SYMBOL(do_signal);
 EXPORT_SYMBOL(syscall_trace);
 EXPORT_SYMBOL(transfer_to_handler);
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- v2.2.3/linux/arch/ppc/kernel/prep_pci.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/prep_pci.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: prep_pci.c,v 1.24 1998/12/10 02:39:51 cort Exp $
+ * $Id: prep_pci.c,v 1.25 1999/03/03 15:09:45 cort Exp $
  * PReP pci functions.
  * Originally by Gary Thomas
  * rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
@@ -312,6 +312,106 @@
 #define CAROLINA_IRQ_EDGE_MASK_LO   0x00  /* IRQ's 0-7  */
 #define CAROLINA_IRQ_EDGE_MASK_HI   0xA4  /* IRQ's 8-15 [10,13,15] */
 
+/*
+ * 8259 edge/level control definitions
+ */
+#define ISA8259_M_ELCR 0x4d0
+#define ISA8259_S_ELCR 0x4d1
+
+#define ELCRS_INT15_LVL         0x80
+#define ELCRS_INT14_LVL         0x40
+#define ELCRS_INT12_LVL         0x10
+#define ELCRS_INT11_LVL         0x08
+#define ELCRS_INT10_LVL         0x04
+#define ELCRS_INT9_LVL          0x02
+#define ELCRS_INT8_LVL          0x01
+#define ELCRM_INT7_LVL          0x80
+#define ELCRM_INT5_LVL          0x20
+
+/*
+ * Mechanism 1 configuration space access as defined in the PCI spec.
+ */
+#define CFG_ADDR (volatile u_int *)0x80000cf8
+#define CFG_DATA 0x80000cfc
+
+#define CFG_DEV_ADDR(b, d, o)	(0x80000000 |	\
+				 ((b) << 16) |	\
+				 ((d) << 8) |	\
+				 ((o) & ~3))
+
+unsigned char max_bus=255;
+
+int mech1_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned char *val)
+{
+	*val = 0xff;
+	if (bus > max_bus)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset));
+	*val = in_8((unsigned char *)CFG_DATA + (offset & 3));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mech1_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned short *val)
+{
+	*val = 0xffff;
+	if (bus > max_bus)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if ((offset & 1) != 0)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset));
+	*val = in_le16((volatile unsigned short *)(CFG_DATA + (offset&3)));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mech1_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+				      unsigned char offset, unsigned int *val)
+{
+	*val = 0xffffffff;
+	if (bus > max_bus)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if ((offset & 3) != 0)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset));
+	*val = in_le32((volatile unsigned int *)CFG_DATA);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mech1_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+				      unsigned char offset, unsigned char val)
+{
+	if (bus > max_bus)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset));
+	out_8((unsigned char *)CFG_DATA + (offset & 3), val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mech1_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+				      unsigned char offset, unsigned short val)
+{
+	if (bus > max_bus)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if ((offset & 1) != 0)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset));
+	out_le16((volatile unsigned short *)(CFG_DATA + (offset&3)), val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int mech1_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned int val)
+{
+	if (bus > max_bus)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if ((offset & 1) != 0)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	out_le32(CFG_ADDR, CFG_DEV_ADDR(bus, dev_fn, offset));
+	out_le32((volatile unsigned int *)CFG_DATA, val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
 __prep
 int
 prep_pcibios_read_config_dword (unsigned char bus,
@@ -526,6 +626,71 @@
 		outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1);
 		pl_id=inb(0x04d1);
 		/*printk("Hi mask now %#0x\n", pl_id);*/
+	} else if ( _prep_type == _PREP_Radstone )
+	{
+		unsigned char ucElcrM, ucElcrS;
+
+		/*
+		 * Set up edge/level
+		 */
+		switch(ucSystemType)
+		{
+			case RS_SYS_TYPE_PPC1:
+			{
+				if(ucBoardRevMaj<5)
+				{
+					ucElcrS=ELCRS_INT15_LVL;
+				}
+				else
+				{
+					ucElcrS=ELCRS_INT9_LVL |
+					        ELCRS_INT11_LVL |
+					        ELCRS_INT14_LVL |
+					        ELCRS_INT15_LVL;
+				}
+				ucElcrM=ELCRM_INT5_LVL | ELCRM_INT7_LVL;
+				break;
+			}
+
+			case RS_SYS_TYPE_PPC1a:
+			{
+				ucElcrS=ELCRS_INT9_LVL |
+				        ELCRS_INT11_LVL |
+				        ELCRS_INT14_LVL |
+				        ELCRS_INT15_LVL;
+				ucElcrM=ELCRM_INT5_LVL;
+				break;
+			}
+
+			case RS_SYS_TYPE_PPC2:
+			case RS_SYS_TYPE_PPC2a:
+			case RS_SYS_TYPE_PPC2ep:
+			case RS_SYS_TYPE_PPC4:
+			case RS_SYS_TYPE_PPC4a:
+			default:
+			{
+				ucElcrS=ELCRS_INT9_LVL |
+				        ELCRS_INT10_LVL |
+				        ELCRS_INT11_LVL |
+				        ELCRS_INT14_LVL |
+				        ELCRS_INT15_LVL;
+				ucElcrM=ELCRM_INT5_LVL |
+				        ELCRM_INT7_LVL;
+				break;
+			}
+		}
+
+		/*
+		 * Write edge/level selection
+		 */
+		outb(ucElcrS, ISA8259_S_ELCR);
+		outb(ucElcrM, ISA8259_M_ELCR);
+
+		/*
+		 * Radstone boards have PCI interrupts all set up
+		 * so leave well alone
+		 */
+		return 0;
 	} else
 	{
 		printk("No known machine pci routing!\n");
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- v2.2.3/linux/arch/ppc/kernel/prep_setup.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/ppc/kernel/prep_setup.c	Fri Mar 19 10:50:03 1999
@@ -54,7 +54,6 @@
 extern unsigned long Hash_size, Hash_mask;
 extern int probingmem;
 extern unsigned long loops_per_sec;
-extern unsigned char aux_device_present;
 
 #ifdef CONFIG_BLK_DEV_RAM
 extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
@@ -136,6 +135,8 @@
 			break;
 		}
 		break;
+	default:
+		break;
 	}
 	
 	
@@ -163,11 +164,12 @@
 {
 	extern char cmd_line[];
 	unsigned char reg;
+	unsigned char ucMothMemType;
+	unsigned char ucEquipPres1;
 
 	/* init to some ~sane value until calibrate_delay() runs */
 	loops_per_sec = 50000000;
 	
-	aux_device_present = 0xaa;
 	/* Set up floppy in PS/2 mode */
 	outb(0x09, SIO_CONFIG_RA);
 	reg = inb(SIO_CONFIG_RD);
@@ -179,16 +181,38 @@
 	switch ( _prep_type )
 	{
 	case _PREP_IBM:
+		/* Enable L2.  Assume we don't need to flush -- Cort*/
+		*(unsigned char *)(0x8000081c) |= 3;
 		ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
 		break;
 	case _PREP_Motorola:
+		/* Enable L2.  Assume we don't need to flush -- Cort*/
+		*(unsigned char *)(0x8000081c) |= 3;
 		ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
 		break;
+	case _PREP_Radstone:
+		ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
+
+		/*
+		 * Determine system type
+		 */
+		ucMothMemType=inb(0x866);
+		ucEquipPres1=inb(0x80c);
+
+		ucSystemType=((ucMothMemType&0x03)<<1) |
+			     ((ucEquipPres1&0x80)>>7);
+		ucSystemType^=7;
+
+		/*
+		 * Determine board revision for use by
+		 * rev. specific code
+		 */
+		ucBoardRev=inb(0x854);
+		ucBoardRevMaj=ucBoardRev>>5;
+		ucBoardRevMin=ucBoardRev&0x1f;
+		break;
 	}
 
-	/* Enable L2.  Assume we don't need to flush -- Cort*/
-	*(unsigned char *)(0x8000081c) = *(unsigned char *)(0x8000081c)|3;
-	
 	printk("Boot arguments: %s\n", cmd_line);
 	
 #ifdef CONFIG_SOUND_CS4232
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prep_time.c linux/arch/ppc/kernel/prep_time.c
--- v2.2.3/linux/arch/ppc/kernel/prep_time.c	Thu Aug  6 14:06:29 1998
+++ linux/arch/ppc/kernel/prep_time.c	Fri Mar 19 10:50:03 1999
@@ -54,6 +54,41 @@
 		       MOTO_RTC_CONTROLA, MOTO_RTC_CONTROLB /* 10,11 */
 };
 
+/*
+ * The following struture is used to access the MK48T18
+ */
+typedef volatile struct _MK48T18 {
+	unsigned char	ucNvRAM[0x3ff8]; /* NvRAM locations */
+	unsigned char	ucControl;
+	unsigned char	ucSecond;	/* 0-59 */
+	unsigned char	ucMinute;	/* 0-59 */
+	unsigned char	ucHour;		/* 0-23 */
+	unsigned char	ucDay;		/* 1-7 */
+	unsigned char	ucDate;		/* 1-31 */
+	unsigned char	ucMonth;	/* 1-12 */
+	unsigned char	ucYear;		/* 0-99 */
+} MK48T18, *PMK48T18;
+
+/*
+ * The control register contains a 5 bit calibration value plus sign
+ * and read/write enable bits
+ */
+#define MK48T18_CTRL_CAL_MASK	0x1f
+#define MK48T18_CTRL_CAL_SIGN	0x20
+#define MK48T18_CTRL_READ	0x40
+#define MK48T18_CTRL_WRITE	0x80
+/*
+ * The STOP bit is the most significant bit of the seconds location
+ */
+#define MK48T18_SEC_MASK	0x7f
+#define MK48T18_SEC_STOP	0x80
+/*
+ * The day location also contains the frequency test bit which should
+ * be zero for normal operation
+ */
+#define MK48T18_DAY_MASK	0x07
+#define MK48T18_DAY_FT		0x40
+
 __prep
 int prep_cmos_clock_read(int addr)
 {
@@ -65,6 +100,8 @@
 		outb(clock_transl[addr], NVRAM_AS0);
 		return (inb(NVRAM_DATA));
 	}
+        else if ( _prep_type == _PREP_Radstone )
+                return CMOS_READ(addr);
 
 	printk("Unknown machine in prep_cmos_clock_read()!\n");
 	return -1;
@@ -85,6 +122,12 @@
 		outb(val,NVRAM_DATA);
 		return;
 	}
+        else if ( _prep_type == _PREP_Radstone )
+        {
+                CMOS_WRITE(val,addr);
+                return;
+        }
+
 	printk("Unknown machine in prep_cmos_clock_write()!\n");
 }
 
@@ -113,6 +156,7 @@
 		BIN_TO_BCD(tm.tm_min);
 		BIN_TO_BCD(tm.tm_hour);
 		BIN_TO_BCD(tm.tm_mon);
+		BIN_TO_BCD(tm.tm_wday);
 		BIN_TO_BCD(tm.tm_mday);
 		BIN_TO_BCD(tm.tm_year);
 	}
@@ -120,9 +164,10 @@
 	prep_cmos_clock_write(tm.tm_min,RTC_MINUTES);
 	prep_cmos_clock_write(tm.tm_hour,RTC_HOURS);
 	prep_cmos_clock_write(tm.tm_mon,RTC_MONTH);
+	prep_cmos_clock_write(tm.tm_wday+1,RTC_DAY_OF_WEEK);
 	prep_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH);
 	prep_cmos_clock_write(tm.tm_year,RTC_YEAR);
-	
+
 	/* The following flags have to be released exactly in this order,
 	 * otherwise the DS12887 (popular MC146818A clone with integrated
 	 * battery and quartz) will not reset the oscillator and will not
@@ -132,6 +177,42 @@
 	 */
 	prep_cmos_clock_write(save_control, RTC_CONTROL);
 	prep_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT);
+
+	/*
+	 * Radstone Technology PPC1a boards use an MK48T18 device
+	 * as the "master" RTC but also have a DS1287 equivalent incorporated
+	 * into the PCI-ISA bridge device. The DS1287 is initialised by the boot
+	 * firmware to reflect the value held in the MK48T18 and thus the
+	 * time may be read from this device both here and in the rtc driver.
+	 * Whenever we set the time, however, if it is to be preserved across
+	 * boots we must also update the "master" RTC.
+	 */
+	if((_prep_type==_PREP_Radstone) && (ucSystemType==RS_SYS_TYPE_PPC1a))
+	{
+		PMK48T18 pMk48t18=(PMK48T18)(_ISA_MEM_BASE+0x00800000);
+
+		/*
+		 * Set the write enable bit
+		 */
+		pMk48t18->ucControl|=MK48T18_CTRL_WRITE;
+		eieio();
+		/*
+		 * Update the clock
+		 */
+		pMk48t18->ucSecond=tm.tm_sec;
+		pMk48t18->ucMinute=tm.tm_min;
+		pMk48t18->ucHour=tm.tm_hour;
+		pMk48t18->ucMonth=tm.tm_mon;
+		pMk48t18->ucDay=tm.tm_wday+1;
+		pMk48t18->ucDate=tm.tm_mday;
+		pMk48t18->ucYear=tm.tm_year;
+
+		eieio();
+		/*
+		 * Clear the write enable bit
+		 */
+		pMk48t18->ucControl&=~MK48T18_CTRL_WRITE;
+	}
 
 	if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) )
 		time_state = TIME_OK;
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.2.3/linux/arch/ppc/kernel/process.c	Fri Jan  8 22:36:03 1999
+++ linux/arch/ppc/kernel/process.c	Wed Mar 10 21:30:32 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: process.c,v 1.70 1999/01/07 16:28:59 cort Exp $
+ * $Id: process.c,v 1.75 1999/02/12 07:06:29 cort Exp $
  *
  *  linux/arch/ppc/kernel/process.c
  *
@@ -49,6 +49,15 @@
 extern spinlock_t scheduler_lock;
 
 struct task_struct *last_task_used_math = NULL;
+static struct vm_area_struct init_mmap = INIT_MMAP;
+static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS;
+struct mm_struct init_mm = INIT_MM;
+union task_union init_task_union = { INIT_TASK };
+/* only used to get secondary processor up */
+struct task_struct *current_set[NR_CPUS] = {&init_task, };
 
 #undef SHOW_TASK_SWITCHES 1
 #undef CHECK_STACK 1
@@ -65,18 +74,6 @@
 	return ((unsigned long)tsk) + sizeof(struct task_struct);
 }
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
-static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
-static struct files_struct init_files = INIT_FILES;
-static struct signal_struct init_signals = INIT_SIGNALS;
-
-struct mm_struct init_mm = INIT_MM;
-union task_union init_task_union = { INIT_TASK };
-
-/* only used to get secondary processor up */
-struct task_struct *current_set[NR_CPUS] = {&init_task, };
-
 int
 dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs)
 {
@@ -397,7 +394,6 @@
 
 	int res;
 	
-	lock_kernel();
 	res = do_fork(SIGCHLD, regs->gpr[1], regs);
 	/* only parent returns here */
 #ifdef __SMP__
@@ -408,8 +404,13 @@
 	if ((current->pid == 0) && (current == &init_task))
 		res = 1;
 #endif /* __SMP__ */
-	unlock_kernel();
 	return res;
+}
+
+asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6,
+			 struct pt_regs *regs)
+{
+	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs);
 }
 
 asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- v2.2.3/linux/arch/ppc/kernel/prom.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/prom.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: prom.c,v 1.46 1998/11/11 03:55:09 paulus Exp $
+ * $Id: prom.c,v 1.50 1999/03/16 10:40:34 cort Exp $
  *
  * Procedures for interfacing to the Open Firmware PROM on
  * Power Macintosh computers.
@@ -99,10 +99,12 @@
 static struct device_node *allnodes = 0;
 
 static void clearscreen(void);
+static void flushscreen(void);
 
 #ifdef CONFIG_BOOTX_TEXT
 
 static void drawchar(char c);
+static void drawhex(unsigned long v);
 static void drawstring(const char *c);
 static void scrollscreen(void);
 
@@ -167,6 +169,11 @@
 
 #define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
 
+/* Is boot-info compatible ? */
+#define BOOT_INFO_IS_COMPATIBLE(bi)		((bi)->compatible_version <= BOOT_INFO_VERSION)
+#define BOOT_INFO_IS_V2_COMPATIBLE(bi)	((bi)->version >= 2)
+#define BOOT_INFO_IS_V4_COMPATIBLE(bi)	((bi)->version >= 4)
+
 __init
 static void
 prom_exit()
@@ -256,6 +263,9 @@
 void
 prom_init(int r3, int r4, prom_entry pp)
 {
+	int cpu = 0, i;
+	phandle node;
+	char type[16], *path;
 	unsigned long mem;
 	ihandle prom_rtas;
 	unsigned long offset = reloc_offset();
@@ -273,6 +283,9 @@
 		unsigned long space;
 		unsigned long ptr, x;
 		char *model;
+#ifdef CONFIG_BOOTX_TEXT
+		unsigned long flags;
+#endif		
 
 		RELOC(boot_infos) = PTRUNRELOC(bi);
 
@@ -283,32 +296,72 @@
 		RELOC(g_loc_Y) = 0;
 		RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8;
 		RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16;
-		prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE " booting...\n"));
+		
+		/* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since
+		   there is nothing much we can do with an incompatible version, except display
+		   a message and eventually hang the processor...
+		   
+		   I'll try to keep enough of boot-info compatible in the future to always allow
+		   display of this message;
+		*/
+		if (!BOOT_INFO_IS_COMPATIBLE(bi))
+			prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"));
+		
+		prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n"));
+		prom_print(RELOC("\nstarted at       : 0x"));
+		drawhex(reloc_offset() + KERNELBASE);
+		prom_print(RELOC("\nlinked at        : 0x"));
+		drawhex(KERNELBASE);
+		prom_print(RELOC("\nframe buffer at  : 0x"));
+		drawhex((unsigned long)bi->dispDeviceBase);
+		prom_print(RELOC(" (phys), 0x"));
+		drawhex((unsigned long)bi->logicalDisplayBase);
+		prom_print(RELOC(" (log)"));
+		prom_print(RELOC("\nMSR              : 0x"));
+		__asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory");
+		drawhex(flags);
+		prom_print(RELOC("\n\n"));
 #endif
-
-		/*
-		 * XXX If this is an iMac, turn off the USB controller.
+		/* Out of the #if/#endif since it flushes the clearscreen too */
+		flushscreen();
+		
+		/* New BootX enters kernel with MMU off, i/os are not allowed
+		   here. This hack will have been done by the boostrap anyway.
 		 */
-		model = (char *) early_get_property
-			(r4 + bi->deviceTreeOffset, 4, RELOC("model"));
-		if (model && strcmp(model, RELOC("iMac,1")) == 0) {
-			out_le32((unsigned *)0x80880008, 1);	/* XXX */
+		if (bi->version < 4) {
+			/*
+			 * XXX If this is an iMac, turn off the USB controller.
+			 */
+			model = (char *) early_get_property
+				(r4 + bi->deviceTreeOffset, 4, RELOC("model"));
+			if (model && strcmp(model, RELOC("iMac,1")) == 0) {
+				out_le32((unsigned *)0x80880008, 1);	/* XXX */
+			}
 		}
-
+		
 		space = bi->deviceTreeOffset + bi->deviceTreeSize;
 		if (bi->ramDisk)
 			space = bi->ramDisk + bi->ramDiskSize;
 		RELOC(klimit) = PTRUNRELOC((char *) bi + space);
 
-		/*
-		 * Touch each page to make sure the PTEs for them
-		 * are in the hash table - the aim is to try to avoid
-		 * getting DSI exceptions while copying the kernel image.
+		/* New BootX will have flushed all TLBs and enters kernel with
+		   MMU switched OFF, so this should not be useful anymore.
 		 */
-		for (ptr = (KERNELBASE + offset) & PAGE_MASK;
-		     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
-			x = *(volatile unsigned long *)ptr;
-
+		if (bi->version < 4) {
+			/*
+			 * Touch each page to make sure the PTEs for them
+			 * are in the hash table - the aim is to try to avoid
+			 * getting DSI exceptions while copying the kernel image.
+			 */
+			for (ptr = (KERNELBASE + offset) & PAGE_MASK;
+			     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
+				x = *(volatile unsigned long *)ptr;
+		}
+		
+#ifdef CONFIG_BOOTX_TEXT
+		prom_print(RELOC("booting...\n"));
+		flushscreen();
+#endif
 		return;
 	}
 
@@ -379,7 +432,7 @@
 			prom_args.nret = 2;
 			prom_args.args[0] = RELOC("instantiate-rtas");
 			prom_args.args[1] = prom_rtas;
-			prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE-offset);
+			prom_args.args[2] = ((void *)(RELOC(rtas_data)-KERNELBASE));
 			RELOC(prom)(&prom_args);
 			if (prom_args.args[nargs] != 0)
 				i = 0;
@@ -393,6 +446,81 @@
 			prom_print(RELOC(" done\n"));
 	}
 	RELOC(klimit) = (char *) (mem - offset);
+#ifdef CONFIG_SMP
+	/*
+	 * With CHRP SMP we need to use the OF to start the other
+	 * processors so we can't wait until smp_boot_cpus (the OF is
+	 * trashed by then) so we have to put the processors into
+	 * a holding pattern controlled by the kernel (not OF) before
+	 * we destroy the OF.
+	 *
+	 * This used a chunk of high memory, puts some holding pattern
+	 * code there and sends the other processors off to there until
+	 * smp_boot_cpus tells them to do something.  We do that by using
+	 * physical address 0x0.  The holding pattern checks that address
+	 * until its cpu # is there, when it is that cpu jumps to
+	 * __secondary_start().  smp_boot_cpus() takes care of setting those
+	 * values.
+	 *
+	 * We also use physical address 0x4 here to tell when a cpu
+	 * is in its holding pattern code.
+	 *
+	 * -- Cort
+	 */
+	{
+		extern void __secondary_hold(void);
+		unsigned long i;
+		char type[16];
+		
+
+		/*
+		 * XXX: hack to make sure we're chrp, assume that if we're
+		 *      chrp we have a device_type property -- Cort
+		 */
+		node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/"));
+		if ( (int)call_prom(RELOC("getprop"), 4, 1, node,
+			    RELOC("device_type"),type, sizeof(type)) <= 0)
+			return;
+		
+		/* copy the holding pattern code to someplace safe (8M) */
+		memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x10000 );
+		for (i = 8<<20; i < ((8<<20)+0x10000); i += 32)
+		{
+			asm volatile("dcbf 0,%0" : : "r" (i) : "memory");
+			asm volatile("icbi 0,%0" : : "r" (i) : "memory");
+		}
+	}
+
+	/* look for cpus */
+	for (node = 0; prom_next_node(&node);)
+	{
+		type[0] = 0;
+		call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),
+			  type, sizeof(type));
+		if (strcmp(type, RELOC("cpu")) != 0)
+			continue;
+		path = (char *) mem;
+		memset(path, 0, 256);
+		if ((int) call_prom(RELOC("package-to-path"), 3, 1,
+				    node, path, 255) < 0)
+			continue;
+		/* XXX: hack - don't start cpu 0, this cpu -- Cort */
+		if ( cpu++ == 0 )
+			continue;
+		prom_print(RELOC("starting cpu "));
+		prom_print(path);
+		*(unsigned long *)(0x4) = 0;
+		asm volatile("dcbf 0,%0": : "r" (0x4) : "memory");
+		call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, cpu-1);
+		for ( i = 0 ; (i < 10000) &&
+			      (*(ulong *)(0x4) == (ulong)0); i++ )
+			;
+		if (*(ulong *)(0x4) == (ulong)cpu-1 )
+			prom_print(RELOC("...ok\n"));
+		else
+			prom_print(RELOC("...failed\n"));
+	}
+#endif	
 }
 
 /*
@@ -631,6 +759,12 @@
 		mem_start = ifunc(np, mem_start);
 	}
 
+	/* the f50 sets the name to 'display' and 'compatible' to what we
+	 * expect for the name -- Cort
+	 */
+	if (!strcmp(np->name, "display"))
+		np->name = get_property(np, "compatible", 0);
+
 	if (!strcmp(np->name, "device-tree"))
 		ifunc = interpret_root_props;
 	else if (np->type == 0)
@@ -1191,8 +1325,11 @@
 	prom_exit();
 }
 
-#define CALC_BASE(y)	(bi->dispDeviceBase + bi->dispDeviceRect[0] * \
-			(bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * (y))
+/* Calc the base address of a given point (x,y) */
+#define CALC_BASE(x,y)	((BOOT_INFO_IS_V2_COMPATIBLE(bi) ? bi->logicalDisplayBase :	\
+			bi->dispDeviceBase) + (bi->dispDeviceRect[0] + (x)) *		\
+			(bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes *		\
+			((y) + bi->dispDeviceRect[1]))
 
 __init
 static void
@@ -1200,7 +1337,7 @@
 {
 	unsigned long offset	= reloc_offset();
 	boot_infos_t* bi	= PTRRELOC(RELOC(boot_infos));
-	unsigned long *base	= (unsigned long *)CALC_BASE(0);
+	unsigned long *base	= (unsigned long *)CALC_BASE(0,0);
 	unsigned long width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
 					(bi->dispDeviceDepth >> 3)) >> 2;
 	int i,j;
@@ -1214,6 +1351,33 @@
 	}
 }
 
+__inline__ void dcbst(const void* addr)
+{
+	__asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
+}
+
+__init
+static void
+flushscreen(void)
+{
+	unsigned long offset	= reloc_offset();
+	boot_infos_t* bi	= PTRRELOC(RELOC(boot_infos));
+	unsigned long *base	= (unsigned long *)CALC_BASE(0,0);
+	unsigned long width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+					(bi->dispDeviceDepth >> 3)) >> 2;
+	int i,j;
+	
+	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+	{
+		unsigned long *ptr = base;
+		for(j=width; j>0; j-=8) {
+			dcbst(ptr);
+			ptr += 8;
+		}
+		base += (bi->dispDeviceRowBytes >> 2);
+	}
+}
+
 #ifdef CONFIG_BOOTX_TEXT
 
 __init
@@ -1222,8 +1386,8 @@
 {
 	unsigned long offset		= reloc_offset();
 	boot_infos_t* bi		= PTRRELOC(RELOC(boot_infos));
-	unsigned long *src		= (unsigned long *)CALC_BASE(16);
-	unsigned long *dst		= (unsigned long *)CALC_BASE(0);
+	unsigned long *src		= (unsigned long *)CALC_BASE(0,16);
+	unsigned long *dst		= (unsigned long *)CALC_BASE(0,0);
 	unsigned long width		= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
 						(bi->dispDeviceDepth >> 3)) >> 2;
 	int i,j;
@@ -1252,20 +1416,17 @@
 {
 	unsigned long offset = reloc_offset();
 
-	switch(c)
-	{
-		case '\r':	RELOC(g_loc_X) = 0;				break;
-		case '\n':	RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++;		break;
+	switch(c) {
+		case '\r': RELOC(g_loc_X) = 0;			break;
+		case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++;	break;
 		default:
 			draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y));
-			if (RELOC(g_loc_X) >= RELOC(g_max_loc_X))
-			{
+			if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) {
 				RELOC(g_loc_X) = 0;
 				RELOC(g_loc_Y)++;
 			}
 	}
-	while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y))
-	{
+	while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) {
 		scrollscreen();
 		RELOC(g_loc_Y)--;
 	}
@@ -1281,17 +1442,32 @@
 
 __init
 static void
+drawhex(unsigned long v)
+{
+	static char hex_table[] = "0123456789abcdef";
+	unsigned long offset	= reloc_offset();
+	
+	drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >>  8) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >>  4) & 0x0000000FUL]);
+	drawchar(RELOC(hex_table)[(v >>  0) & 0x0000000FUL]);
+}
+
+
+__init
+static void
 draw_byte(unsigned char c, long locX, long locY)
 {
 	unsigned long offset	= reloc_offset();
-	boot_infos_t* bi		= PTRRELOC(RELOC(boot_infos));
-	unsigned char *base	=	bi->dispDeviceBase
-							 + (bi->dispDeviceRowBytes * ((locY * 16) +  bi->dispDeviceRect[1]))
-							 + (bi->dispDeviceDepth >> 3) * ((locX * 8) + bi->dispDeviceRect[0]);
-	unsigned char *font	=	&RELOC(vga_font)[((unsigned long)c) * 16];
+	boot_infos_t* bi	= PTRRELOC(RELOC(boot_infos));
+	unsigned char *base	= CALC_BASE(locX << 3, locY << 4);
+	unsigned char *font	= &RELOC(vga_font)[((unsigned long)c) * 16];
 	
-	switch(bi->dispDeviceDepth)
-	{
+	switch(bi->dispDeviceDepth) {
 		case 32:
 			draw_byte_32(font, (unsigned long *)base);
 			break;
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- v2.2.3/linux/arch/ppc/kernel/setup.c	Fri Jan  8 22:36:03 1999
+++ linux/arch/ppc/kernel/setup.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: setup.c,v 1.122 1998/12/31 20:51:19 cort Exp $
+ * $Id: setup.c,v 1.130 1999/03/11 01:45:15 cort Exp $
  * Common prep/pmac/chrp boot and setup code.
  */
 
@@ -27,6 +27,7 @@
 #include <asm/smp.h>
 #ifdef CONFIG_MBX
 #include <asm/mbx.h>
+#include <asm/8xx_immap.h>
 #endif
 #include <asm/bootx.h>
 
@@ -42,6 +43,7 @@
 #endif
 /* END APUS defs */
 
+extern boot_infos_t *boot_infos;
 extern char cmd_line[512];
 char saved_command_line[256];
 unsigned char aux_device_present;
@@ -55,12 +57,22 @@
 #endif /* ! CONFIG_MACH_SPECIFIC */
 
 /* copy of the residual data */
+#ifndef CONFIG_MBX
 unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,};
+#else
+unsigned char __res[sizeof(bd_t)] = {0,};
+#endif
+
 RESIDUAL *res = (RESIDUAL *)&__res;
 
 int _prep_type;
-
-extern boot_infos_t *boot_infos;
+/*
+ * This is used to identify the board type from a given PReP board
+ * vendor. Board revision is also made available.
+ */
+unsigned char ucSystemType;
+unsigned char ucBoardRev;
+unsigned char ucBoardRevMaj, ucBoardRevMin;
 
 /*
  * Perhaps we can put the pmac screen_info[] here
@@ -114,12 +126,8 @@
 void machine_restart(char *cmd)
 {
 #ifndef CONFIG_MBX
-	struct adb_request req;
 	unsigned long flags;
-	unsigned long i = 10000;
-#if 0
-	int err;
-#endif	
+	struct adb_request req;
 
 	switch(_machine)
 	{
@@ -142,31 +150,32 @@
 #if 0		/* RTAS doesn't seem to work on Longtrail.
 		   For now, do it the same way as the PReP. */
 	        /*err = call_rtas("system-reboot", 0, 1, NULL);
-		printk("RTAS system-reboot returned %d\n", err);
-		for (;;);*/
+		  printk("RTAS system-reboot returned %d\n", err);
+		  for (;;);*/
 		
 		{
 			extern unsigned int rtas_entry, rtas_data, rtas_size;
 			unsigned long status, value;
-			printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n",
+			printk("rtas_ent`ry: %08x rtas_data: %08x rtas_size: %08x\n",
 			       rtas_entry,rtas_data,rtas_size);
-	}
+		}
 #endif
 	case _MACH_prep:
 		_disable_interrupts();
-		
-		/* set exception prefix high - to the prom */
-		save_flags( flags );
-		restore_flags( flags|MSR_IP );
-		
-		/* make sure bit 0 (reset) is a 0 */
-		outb( inb(0x92) & ~1L , 0x92 );
-		/* signal a reset to system control port A - soft reset */
-		outb( inb(0x92) | 1 , 0x92 );
-		
-		while ( i != 0 ) i++;
-		panic("restart failed\n");
-		break;
+                /* set exception prefix high - to the prom */
+                save_flags( flags );
+                restore_flags( flags|MSR_IP );
+                
+                /* make sure bit 0 (reset) is a 0 */
+                outb( inb(0x92) & ~1L , 0x92 );
+                /* signal a reset to system control port A - soft reset */
+                outb( inb(0x92) | 1 , 0x92 );
+                
+                while ( 1 ) ;
+                break;
+		/*
+		 * Not reached
+		 */
 	case _MACH_apus:
 		cli();
 
@@ -182,8 +191,16 @@
 		break;
 	}
 #else /* CONFIG_MBX */
-	extern void MBX_gorom(void);
-	MBX_gorom();
+        extern void __clear_msr_me(void);
+        __volatile__ unsigned char dummy;
+	
+        cli();
+        ((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080;
+        __clear_msr_me();
+        dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0];
+ 
+	printk("Restart failed\n");
+	while(1);
 #endif /* CONFIG_MBX */
 }
 
@@ -226,7 +243,7 @@
 	}
 	for (;;);
 #else /* CONFIG_MBX */
-	machine_restart(NULL);
+	machine_halt();
 #endif /* CONFIG_MBX */
 }
 
@@ -238,7 +255,6 @@
 	}
 	else /* prep, chrp or apus */
 		machine_restart(NULL);
-
 }
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
@@ -432,14 +448,14 @@
 	{
 		len += sprintf(buffer+len,"zero pages\t: total %lu (%luKb) "
 			       "current: %lu (%luKb) hits: %lu/%lu (%lu%%)\n",
-			       quicklists.zerototal,
-			       (quicklists.zerototal*PAGE_SIZE)>>10,
-			       quicklists.zero_sz,
-			       (quicklists.zero_sz*PAGE_SIZE)>>10,
-			       quicklists.zeropage_hits,quicklists.zeropage_calls,
+			       zero_cache_total,
+			       (zero_cache_total*PAGE_SIZE)>>10,
+			       zero_cache_sz,
+			       (zero_cache_sz*PAGE_SIZE)>>10,
+			       zero_cache_hits,zero_cache_calls,
 			       /* : 1 below is so we don't div by zero */
-			       (quicklists.zeropage_hits*100) /
-			            ((quicklists.zeropage_calls)?quicklists.zeropage_calls:1));
+			       (zero_cache_hits*100) /
+			            ((zero_cache_calls)?zero_cache_calls:1));
 	}
 
 #ifndef CONFIG_MBX
@@ -590,7 +606,6 @@
 		if ( r3 )
 			memcpy((void *)res,(void *)(r3+KERNELBASE),
 			       sizeof(RESIDUAL));
-		setup_pci_ptrs();
 		isa_io_base = PREP_ISA_IO_BASE;
 		isa_mem_base = PREP_ISA_MEM_BASE;
 		pci_dram_offset = PREP_PCI_DRAM_OFFSET;
@@ -604,11 +619,21 @@
 		{
 			if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) )
 				_prep_type = _PREP_IBM;
+			else if (!strncmp(res->VitalProductData.PrintableModel,
+					  "Radstone",8))
+			{
+				extern char *Motherboard_map_name;
+
+				_prep_type = _PREP_Radstone;
+				Motherboard_map_name=
+					res->VitalProductData.PrintableModel;
+			}
 			else
 				_prep_type = _PREP_Motorola;
 		}
 		else /* assume motorola if no residual (netboot?) */
 			_prep_type = _PREP_Motorola;
+		setup_pci_ptrs();
 #ifdef CONFIG_BLK_DEV_INITRD
 		/* take care of initrd if we have one */
 		if ( r4 )
@@ -634,9 +659,7 @@
 			initrd_end = r3 + r4 + KERNELBASE;
 		}
 #endif /* CONFIG_BLK_DEV_INITRD */
-		/* isa_io_base set by setup_pci_ptrs() */
-		isa_mem_base = CHRP_ISA_MEM_BASE;
-		pci_dram_offset = CHRP_PCI_DRAM_OFFSET;
+		/* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */
 #if !defined(CONFIG_MACH_SPECIFIC)
 		ISA_DMA_THRESHOLD = ~0L;
 		DMA_MODE_READ = 0x44;
@@ -737,7 +760,7 @@
 	if (strstr(cmd_line, "xmon"))
 		xmon(0);
 #endif /* CONFIG_XMON */
-
+ 
 	/* reboot on panic */	
 	panic_timeout = 180;
 	
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/signal.c linux/arch/ppc/kernel/signal.c
--- v2.2.3/linux/arch/ppc/kernel/signal.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/kernel/signal.c	Fri Mar 19 10:50:03 1999
@@ -1,7 +1,7 @@
 /*
  *  linux/arch/ppc/kernel/signal.c
  *
- *  $Id: signal.c,v 1.21 1998/10/22 19:37:49 paulus Exp $
+ *  $Id: signal.c,v 1.23 1999/03/01 16:51:53 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -374,7 +374,7 @@
 	if (!oldset)
 		oldset = &current->blocked;
 
-	newsp = frame = regs->gpr[1] - sizeof(struct sigregs);
+	newsp = frame = 0;
 
 	for (;;) {
 		unsigned long signr;
@@ -470,6 +470,13 @@
 				/* NOTREACHED */
 			}
 		}
+
+		if ( (ka->sa.sa_flags & SA_ONSTACK)
+		     && (! on_sig_stack(regs->gpr[1])))
+			newsp = (current->sas_ss_sp + current->sas_ss_size);
+		else
+			newsp = regs->gpr[1];
+		newsp = frame = newsp - sizeof(struct sigregs);
 
 		/* Whee!  Actually deliver the signal.  */
 		handle_signal(signr, ka, &info, oldset, regs, &newsp, frame);
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- v2.2.3/linux/arch/ppc/kernel/smp.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/kernel/smp.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: smp.c,v 1.39 1998/12/28 10:28:51 paulus Exp $
+ * $Id: smp.c,v 1.48 1999/03/16 10:40:32 cort Exp $
  *
  * Smp support for ppc.
  *
@@ -18,6 +18,7 @@
 #define __KERNEL_SYSCALLS__
 #include <linux/unistd.h>
 #include <linux/init.h>
+#include <linux/openpic.h>
 
 #include <asm/ptrace.h>
 #include <asm/atomic.h>
@@ -29,9 +30,10 @@
 #include <asm/softirq.h>
 #include <asm/init.h>
 #include <asm/io.h>
+#include <asm/prom.h>
 
 #include "time.h"
-
+int first_cpu_booted = 0;
 int smp_threads_ready = 0;
 volatile int smp_commenced = 0;
 int smp_num_cpus = 1;
@@ -42,7 +44,6 @@
 spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;
 unsigned int prof_multiplier[NR_CPUS];
 unsigned int prof_counter[NR_CPUS];
-int first_cpu_booted = 0;
 cycles_t cacheflush_time;
 
 /* all cpu mappings are 1-1 -- Cort */
@@ -51,6 +52,10 @@
 
 int start_secondary(void *);
 extern int cpu_idle(void *unused);
+u_int openpic_read(volatile u_int *addr);
+
+/* register for interrupting the secondary processor on the powersurge */
+#define PSURGE_INTR	((volatile unsigned *)0xf80000c0)
 
 void smp_local_timer_interrupt(struct pt_regs * regs)
 {
@@ -99,29 +104,33 @@
 
 /*
  * Dirty hack to get smp message passing working.
- * Right now it only works for stop cpu's but will be setup
- * later for more general message passing.
  *
  * As it is now, if we're sending two message at the same time
- * we have race conditions.  I avoided doing locks here since
- * all that works right now is the stop cpu message.
+ * we have race conditions.  The PowerSurge doesn't easily
+ * allow us to send IPI messages so we put the messages in
+ * smp_message[].
  *
+ * This is because don't have several IPI's on the PowerSurge even though
+ * we do on the chrp.  It would be nice to use the actual IPI's on the chrp
+ * rather than this but having two methods of doing IPI isn't a good idea
+ * right now.
  *  -- Cort
  */
 int smp_message[NR_CPUS];
 void smp_message_recv(void)
 {
 	int msg = smp_message[smp_processor_id()];
-	
-	/* clear interrupt */
-	*(volatile unsigned long *)(0xf80000c0) = ~0L;
-	eieio();
+
+	if ( _machine == _MACH_Pmac )
+	{
+		/* clear interrupt */
+		out_be32(PSURGE_INTR, ~0);
+	}
 	
 	/* make sure msg is for us */
 	if ( msg == -1 ) return;
 
 	ipi_count++;
-	/*printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);*/
 	
 	switch( msg )
 	{
@@ -158,12 +167,17 @@
 spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED;
 void smp_message_pass(int target, int msg, unsigned long data, int wait)
 {
-	if ( _machine != _MACH_Pmac )
+	int i;
+	if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) )
 		return;
-printk("SMP %d: sending smp message %x\n", current->processor, msg);
-if (smp_processor_id() ) printk("pass from cpu 1\n");
+
 	spin_lock(&mesg_pass_lock);
-#define OTHER (~smp_processor_id() & 1)
+
+	/*
+	 * We assume here that the msg is not -1.  If it is,
+	 * the recipient won't know the message was destined
+	 * for it. -- Cort
+	 */
 	
 	switch( target )
 	{
@@ -171,105 +185,179 @@
 		smp_message[smp_processor_id()] = msg;
 		/* fall through */
 	case MSG_ALL_BUT_SELF:
-		smp_message[OTHER] = msg;
+		for ( i = 0 ; i < smp_num_cpus ; i++ )
+			if ( i != smp_processor_id () )
+				smp_message[i] = msg;
 		break;
 	default:
 		smp_message[target] = msg;
 		break;
 	}
-	/* interrupt secondary processor */
-	*(volatile unsigned long *)(0xf80000c0) = ~0L;
-	eieio();
-	*(volatile unsigned long *)(0xf80000c0) = 0L;
-	eieio();
-	/* interrupt primary */
-	/**(volatile unsigned long *)(0xf3019000);*/
-	spin_unlock(&mesg_pass_lock);	
+	
+	if ( _machine == _MACH_Pmac )
+	{
+		/* interrupt secondary processor */
+		out_be32(PSURGE_INTR, ~0);
+		out_be32(PSURGE_INTR, 0);
+		/*
+		 * Assume for now that the secondary doesn't send
+		 * IPI's -- Cort
+		 */
+		/* interrupt primary */
+		/**(volatile unsigned long *)(0xf3019000);*/
+	}
+	
+	if ( _machine == _MACH_chrp )
+	{
+		/*
+		 * There has to be some way of doing this better -
+		 * perhaps a sent-to-all or send-to-all-but-self
+		 * in the openpic.  This gets us going for now, though.
+		 * -- Cort
+		 */
+		switch ( target )
+		{
+		case MSG_ALL:
+			for ( i = 0 ; i < smp_num_cpus ; i++ )
+				openpic_cause_IPI(i, 0, 0xffffffff );
+			break;
+		case MSG_ALL_BUT_SELF:
+			for ( i = 0 ; i < smp_num_cpus ; i++ )
+				if ( i != smp_processor_id () )
+					openpic_cause_IPI(i, 0,
+			  0xffffffff & ~(1 << smp_processor_id()));
+			break;
+		default:
+			openpic_cause_IPI(target, 0, 1U << target);
+			break;
+		}
+	}
+	
+	spin_unlock(&mesg_pass_lock);
 }
 
 void __init smp_boot_cpus(void)
 {
 	extern struct task_struct *current_set[NR_CPUS];
-	extern void __secondary_start(void);
+	extern void __secondary_start_psurge(void);
 	int i;
 	struct task_struct *p;
 	unsigned long a;
 
         printk("Entering SMP Mode...\n");
-	
+	/* let other processors know to not do certain initialization */
 	first_cpu_booted = 1;
-	/*dcbf(&first_cpu_booted);*/
+	
+	/*
+	 * assume for now that the first cpu booted is
+	 * cpu 0, the master -- Cort
+	 */
+	cpu_callin_map[0] = 1;
+        smp_store_cpu_info(0);
+        active_kernel_processor = 0;
+	current->processor = 0;
 
 	for (i = 0; i < NR_CPUS; i++) {
 		prof_counter[i] = 1;
 		prof_multiplier[i] = 1;
 	}
 
-	cpu_callin_map[0] = 1;
-        smp_store_cpu_info(0);
-        active_kernel_processor = 0;
-	current->processor = 0;
-
 	/*
 	 * XXX very rough, assumes 20 bus cycles to read a cache line,
 	 * timebase increments every 4 bus cycles, 32kB L1 data cache.
 	 */
 	cacheflush_time = 5 * 1024;
 
-	if ( _machine != _MACH_Pmac )
+	if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) )
 	{
 		printk("SMP not supported on this machine.\n");
 		return;
 	}
 	
-	/* create a process for second processor */
-        kernel_thread(start_secondary, NULL, CLONE_PID);
-	p = task[1];
-	if ( !p )
-		panic("No idle task for secondary processor\n");
-	p->processor = 1;
-	p->has_cpu = 1;
-	current_set[1] = p;
-
-	/* need to flush here since secondary bat's aren't setup */
-	/* XXX ??? */
-	for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32)
-		asm volatile("dcbf 0,%0" : : "r" (a) : "memory");
-	asm volatile("sync");
-
-	/*dcbf((void *)&current_set[1]);*/
-	/* setup entry point of secondary processor */
-	*(volatile unsigned long *)(0xf2800000) =
-		(unsigned long)__secondary_start-KERNELBASE;
-	eieio();
-	/* interrupt secondary to begin executing code */
-	*(volatile unsigned long *)(0xf80000c0) = ~0L;
-	eieio();
-	*(volatile unsigned long *)(0xf80000c0) = 0L;
-	eieio();
+	switch ( _machine )
+	{
+	case _MACH_Pmac:
+		/* assume powersurge board - 2 processors -- Cort */
+		smp_num_cpus = 2; 
+		break;
+	case _MACH_chrp:
+		smp_num_cpus = ((openpic_read(&OpenPIC->Global.Feature_Reporting0)
+				 & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
+				OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1;
+		/* get our processor # - we may not be cpu 0 */
+		printk("SMP %d processors, boot CPU is %d (should be 0)\n",
+		       smp_num_cpus,
+		       10/*openpic_read(&OpenPIC->Processor[0]._Who_Am_I)*/);
+		break;
+	}
+
 	/*
-	 * wait to see if the secondary made a callin (is actually up).
-	 * udelay() isn't accurate here since we haven't yet called
-	 * calibrate_delay() so use this value that I found through
-	 * experimentation.  -- Cort
+	 * only check for cpus we know exist.  We keep the callin map
+	 * with cpus at the bottom -- Cort
 	 */
-	for ( i = 1000; i && !cpu_callin_map[1] ; i-- )
-		udelay(100);
+	for ( i = 1 ; i < smp_num_cpus; i++ )
+	{
+		int c;
+		
+		/* create a process for the processor */
+		kernel_thread(start_secondary, NULL, CLONE_PID);
+		p = task[i];
+		if ( !p )
+			panic("No idle task for secondary processor\n");
+		p->processor = i;
+		p->has_cpu = 1;
+		current_set[i] = p;
+
+		/* need to flush here since secondary bats aren't setup */
+		for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32)
+			asm volatile("dcbf 0,%0" : : "r" (a) : "memory");
+		asm volatile("sync");
+
+		/* wake up cpus */
+		switch ( _machine )
+		{
+		case _MACH_Pmac:
+			/* setup entry point of secondary processor */
+			*(volatile unsigned long *)(0xf2800000) =
+				(unsigned long)__secondary_start_psurge-KERNELBASE;
+			eieio();
+			/* interrupt secondary to begin executing code */
+			out_be32(PSURGE_INTR, ~0);
+			out_be32(PSURGE_INTR, 0);
+			break;
+		case _MACH_chrp:
+			*(unsigned long *)KERNELBASE = i;
+			asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory");
+			break;
+		}
+		
+		/*
+		 * wait to see if the cpu made a callin (is actually up).
+		 * use this value that I found through experimentation.
+		 * -- Cort
+		 */
+		for ( c = 1000; c && !cpu_callin_map[i] ; c-- )
+			udelay(100);
+		
+		if ( cpu_callin_map[i] )
+		{
+			printk("Processor %d found.\n", i);
+			/* this sync's the decr's -- Cort */
+			if ( _machine == _MACH_Pmac )
+				set_dec(decrementer_count);
+		} else {
+			printk("Processor %d is stuck.\n", i);
+		}
+	}
 	
-	if(cpu_callin_map[1]) {
-		printk("Processor %d found.\n", smp_num_cpus);
-		smp_num_cpus++;
-#if 1 /* this sync's the decr's, but we don't want this now -- Cort */
-		set_dec(decrementer_count);
-#endif
-	} else {
-		printk("Processor %d is stuck. \n", smp_num_cpus);
+	if ( _machine == _MACH_Pmac )
+	{
+		/* reset the entry point so if we get another intr we won't
+		 * try to startup again */
+		*(volatile unsigned long *)(0xf2800000) = 0x100;
+		/* send interrupt to other processors to start decr's on all cpus */
+		smp_message_pass(1,0xf0f0, 0, 0);
 	}
-	/* reset the entry point so if we get another intr we won't
-	 * try to startup again */
-	*(volatile unsigned long *)(0xf2800000) = 0x100;
-	/* send interrupt to other processors to start decr's on all cpus */
-	smp_message_pass(1,0xf0f0, 0, 0);
 }
 
 void __init smp_commence(void)
@@ -308,7 +396,6 @@
 	while(!smp_commenced)
 		barrier();
 	__sti();
-	printk("SMP %d: smp_callin done\n", current->processor);
 }
 
 void __init smp_setup(char *str, int *ints)
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/softemu8xx.c linux/arch/ppc/kernel/softemu8xx.c
--- v2.2.3/linux/arch/ppc/kernel/softemu8xx.c	Fri May  8 23:14:45 1998
+++ linux/arch/ppc/kernel/softemu8xx.c	Wed Mar 10 21:30:32 1999
@@ -38,6 +38,7 @@
 #define LFDU	51
 #define STFD	54
 #define STFDU	55
+#define FMR	63
 
 /*
  * We return 0 on success, 1 on unimplemented instruction, and EFAULT
@@ -49,6 +50,7 @@
 	uint	inst, instword;
 	uint	flreg, idxreg, disp;
 	uint	retval;
+	signed short sdisp;
 	uint	*ea, *ip;
 
 	retval = 0;
@@ -66,6 +68,11 @@
 	switch ( inst )
 	{
 	case LFD:
+		/* this is a 16 bit quantity that is sign extended
+		 * so use a signed short here -- Cort
+		 */
+		sdisp = (instword & 0xffff);
+		ea = (uint *)(regs->gpr[idxreg] + sdisp);
 		if (copy_from_user(ip, ea, sizeof(double)))
 			retval = EFAULT;
 		break;
@@ -77,6 +84,11 @@
 			regs->gpr[idxreg] = (uint)ea;
 		break;
 	case STFD:
+		/* this is a 16 bit quantity that is sign extended
+		 * so use a signed short here -- Cort
+		 */
+		sdisp = (instword & 0xffff);
+		ea = (uint *)(regs->gpr[idxreg] + sdisp);
 		if (copy_to_user(ea, ip, sizeof(double)))
 			retval = EFAULT;
 		break;
@@ -87,6 +99,11 @@
 		else
 			regs->gpr[idxreg] = (uint)ea;
 		break;
+	case FMR:
+		/* assume this is a fp move -- Cort */
+		memcpy( ip, &current->tss.fpr[(instword>>11)&0x1f],
+			sizeof(double) );
+		break;
 	default:
 		retval = 1;
 		printk("Bad emulation %s/%d\n"
@@ -98,7 +115,7 @@
 		       (instword>>16)&0x1f,
 		       (instword>>11)&0x1f,
 		       (instword>>6)&0x1f,
-		       (instword>>1)&0x1f,
+		       (instword>>1)&0x3ff,
 		       instword&1);
 		{
 			int pa;
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.2.3/linux/arch/ppc/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/ppc/kernel/time.c	Fri Mar 19 10:50:03 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: time.c,v 1.39 1998/12/28 10:28:51 paulus Exp $
+ * $Id: time.c,v 1.45 1999/03/03 15:09:59 cort Exp $
  * Common time routines among all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -18,8 +18,8 @@
  * Since it is not possible to get a nice 100 Hz clock out of this, without
  * creating a software PLL, I have set HZ to 128.  -- Dan
  *
- * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
- *		"A Kernel Model for Precision Timekeeping" by Dave Mills
+ * 1997-09-10  Updated NTP code according to technical memorandum Jan '96
+ *             "A Kernel Model for Precision Timekeeping" by Dave Mills
  */
 
 #include <linux/config.h>
@@ -76,9 +76,6 @@
 {
 	int dval, d;
 	unsigned long cpu = smp_processor_id();
-	/* save the HID0 in case dcache was off - see idle.c
-	 * this hack should leave for a better solution -- Cort */
-	unsigned dcache_locked = unlock_dcache();
 	
 	hardirq_enter(cpu);
 #ifdef __SMP__
@@ -136,9 +133,6 @@
 	}
 #endif
 	hardirq_exit(cpu);
-	/* restore the HID0 in case dcache was off - see idle.c
-	 * this hack should leave for a better solution -- Cort */
-	lock_dcache(dcache_locked);
 }
 
 #ifdef CONFIG_MBX
@@ -198,9 +192,9 @@
 	xtime.tv_sec = tv->tv_sec;
 	xtime.tv_usec = tv->tv_usec - frac_tick;
 	set_dec(frac_tick * count_period_den / count_period_num);
-	time_adjust = 0;		/* stop active adjtime() */
+	time_adjust = 0;                /* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
+	time_state = TIME_ERROR;        /* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	restore_flags(flags);
@@ -303,6 +297,7 @@
 __initfunc(void prep_calibrate_decr(void))
 {
 	unsigned long flags;
+	unsigned long freq, divisor;
 
 	/* the Powerstack II's have trouble with the timer so
 	 * we use a default value -- Cort
@@ -310,7 +305,6 @@
 	if ( (_prep_type == _PREP_Motorola) &&
 	     ((inb(0x800) & 0xF0) & 0x40) )
 	{
-		unsigned long freq, divisor;
 		static unsigned long t2 = 0;
 		
 		t2 = 998700000/60;
@@ -323,7 +317,16 @@
 		count_period_den = freq / 1000000;
 		return;
 	}
-	
+	if ( _prep_type == _PREP_Radstone )
+	{
+		freq = res->VitalProductData.ProcessorBusHz;
+		divisor = 4;
+		printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+		decrementer_count = freq / HZ / divisor;
+		count_period_num = divisor;
+		count_period_den = freq / 1000000;
+		return;
+	}
 	
 	save_flags(flags);
 
@@ -439,6 +442,49 @@
 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 };
 
+/*
+ * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
+ */
+void GregorianDay(struct rtc_time * tm)
+{
+	int leapsToDate;
+	int lastYear;
+	int day;
+	int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+	lastYear=tm->tm_year-1;
+
+	/*
+	 * Number of leap corrections to apply up to end of last year
+	 */
+	leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
+
+	/*
+	 * This year is a leap year if it is divisible by 4 except when it is
+	 * divisible by 100 unless it is divisible by 400
+	 *
+	 * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
+	 */
+	if((tm->tm_year%4==0) &&
+	   ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
+	   (tm->tm_mon>2))
+	{
+		/*
+		 * We are past Feb. 29 in a leap year
+		 */
+		day=1;
+	}
+	else
+	{
+		day=0;
+	}
+
+	day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] +
+		   tm->tm_mday;
+
+	tm->tm_wday=day%7;
+}
+
 void to_tm(int tim, struct rtc_time * tm)
 {
 	register int    i;
@@ -467,6 +513,11 @@
 
 	/* Days are what is left over (+1) from all that. */
 	tm->tm_mday = day + 1;
+
+	/*
+	 * Determine the day of week
+	 */
+	GregorianDay(tm);
 }
 
 
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/lib/locks.c linux/arch/ppc/lib/locks.c
--- v2.2.3/linux/arch/ppc/lib/locks.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/lib/locks.c	Wed Mar 10 21:30:32 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: locks.c,v 1.21 1998/12/28 10:28:53 paulus Exp $
+ * $Id: locks.c,v 1.23 1999/02/12 07:06:32 cort Exp $
  *
  * Locks for smp ppc 
  * 
@@ -26,24 +26,18 @@
 #ifdef DEBUG_LOCKS
 	unsigned int stuck = INIT_STUCK;
 #endif /* DEBUG_LOCKS */
-	/* try expensive atomic load/store to get lock */
-	while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) {
-		/* try cheap load until it's free */
-		while(lock->lock) {
+	while (__spin_trylock(&lock->lock)) {
 #ifdef DEBUG_LOCKS
-			if(!--stuck)
-			{
-				printk("_spin_lock(%p) CPU#%d NIP %p"
-				       " holder: cpu %ld pc %08lX\n",
-				       lock, cpu, __builtin_return_address(0),
-				       lock->owner_cpu,lock->owner_pc);
-				stuck = INIT_STUCK;
-				/* steal the lock */
-				/*xchg_u32((void *)&lock->lock,0);*/
-			}
-#endif /* DEBUG_LOCKS */
-			barrier();
+		if(!--stuck) {
+			printk("_spin_lock(%p) CPU#%d NIP %p"
+			       " holder: cpu %ld pc %08lX\n",
+			       lock, cpu, __builtin_return_address(0),
+			       lock->owner_cpu,lock->owner_pc);
+			stuck = INIT_STUCK;
+			/* steal the lock */
+			/*xchg_u32((void *)&lock->lock,0);*/
 		}
+#endif /* DEBUG_LOCKS */
 	}
 	lock->owner_pc = (unsigned long)__builtin_return_address(0);
 	lock->owner_cpu = cpu;
@@ -51,15 +45,11 @@
 
 int spin_trylock(spinlock_t *lock)
 {
-	unsigned long result;
-
-	result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff);
-	if ( !result ) 
-	{ 
-		lock->owner_cpu = smp_processor_id(); 
-		lock->owner_pc = (unsigned long)__builtin_return_address(0);
-	} 
-	return (result == 0);
+	if (__spin_trylock(&lock->lock))
+		return 0;
+	lock->owner_cpu = smp_processor_id(); 
+	lock->owner_pc = (unsigned long)__builtin_return_address(0);
+	return 1;
 }
 
 
@@ -76,11 +66,11 @@
 		      lp->owner_pc,lp->lock);
 #endif /* DEBUG_LOCKS */
 	lp->owner_pc = lp->owner_cpu = 0;
-	eieio();	/* actually I believe eieio only orders */
-	lp->lock = 0;	/* non-cacheable accesses (on 604 at least) */
-	eieio();	/*  - paulus. */
+	wmb();
+	lp->lock = 0;
+	wmb();
 }
-		
+
 /*
  * Just like x86, implement read-write locks as a 32-bit counter
  * with the high bit (sign) being the "write" bit.
@@ -95,6 +85,7 @@
 
 again:	
 	/* get our read lock in there */
+	wmb();
 	atomic_inc((atomic_t *) &(rw)->lock);
 	if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */
 	{
@@ -114,6 +105,7 @@
 		/* try to get the read lock again */
 		goto again;
 	}
+	wmb();
 }
 
 void _read_unlock(rwlock_t *rw)
@@ -124,7 +116,9 @@
 		       current->comm,current->pid,current->tss.regs->nip,
 		      rw->lock);
 #endif /* DEBUG_LOCKS */
+	wmb();
 	atomic_dec((atomic_t *) &(rw)->lock);
+	wmb();
 }
 
 void _write_lock(rwlock_t *rw)
@@ -134,7 +128,8 @@
 	int cpu = smp_processor_id();
 #endif /* DEBUG_LOCKS */		  
 
-again:	
+again:
+	wmb();
 	if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */
 	{
 		while ( (rw)->lock & (1<<31) ) /* wait for write lock */
@@ -170,6 +165,7 @@
 		}
 		goto again;
 	}
+	wmb();
 }
 
 void _write_unlock(rwlock_t *rw)
@@ -180,7 +176,9 @@
 		      current->comm,current->pid,current->tss.regs->nip,
 		      rw->lock);
 #endif /* DEBUG_LOCKS */
+	wmb();
 	clear_bit(31,&(rw)->lock);
+	wmb();
 }
 
 void __lock_kernel(struct task_struct *task)
@@ -196,24 +194,18 @@
 	}
 #endif /* DEBUG_LOCKS */
 
-	if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 )
+	if (atomic_inc_return((atomic_t *) &task->lock_depth) != 1)
 		return;
 	/* mine! */
-	while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) )
-	{
-		/* try cheap load until it's free */
-		while(klock_info.kernel_flag) {
+	while (__spin_trylock(&klock_info.kernel_flag)) {
 #ifdef DEBUG_LOCKS
-			if(!--stuck)
-			{
-				printk("_lock_kernel() CPU#%d NIP %p\n",
-				       smp_processor_id(),
-				       __builtin_return_address(0));
-				stuck = INIT_STUCK;
-			}
-#endif /* DEBUG_LOCKS */
-			barrier();
+		if(!--stuck) {
+			printk("_lock_kernel() CPU#%d NIP %p\n",
+			       smp_processor_id(),
+			       __builtin_return_address(0));
+			stuck = INIT_STUCK;
 		}
+#endif /* DEBUG_LOCKS */
 	}
 	
 	klock_info.akp = smp_processor_id();
@@ -223,7 +215,7 @@
 void __unlock_kernel(struct task_struct *task)
 {
 #ifdef DEBUG_LOCKS
- 	if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) )
+ 	if ((task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD))
 	{
 		printk("__unlock_kernel(): %s/%d (nip %08lX) "
 		       "lock depth %x flags %lx\n",
@@ -234,10 +226,13 @@
 		return;
 	}
 #endif /* DEBUG_LOCKS */
-	if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) )
+	if (atomic_dec_and_test((atomic_t *) &task->lock_depth))
 	{
+		wmb();
 		klock_info.akp = NO_PROC_ID;
+		wmb();
 		klock_info.kernel_flag = KLOCK_CLEAR;
+		wmb();
 	}
 }	
 
@@ -249,5 +244,5 @@
 		__lock_kernel(task);
 		task->lock_depth = depth;
 		__sti();
-       }
+	}
 }
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/lib/string.S linux/arch/ppc/lib/string.S
--- v2.2.3/linux/arch/ppc/lib/string.S	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/lib/string.S	Wed Mar 10 21:30:32 1999
@@ -321,8 +321,6 @@
 	.long	75b,76b
 .text
 
-#undef CLEAR_USE_DCBZ 1
-#undef CLEAR_NO_CACHE 1
 	.globl	__clear_user
 __clear_user:
 	addi	r6,r3,-4
@@ -333,15 +331,6 @@
 	/* clear a single word */
 11:	stwu	r5,4(r6)
 	beqlr
-#if defined(CLEAR_NO_CACHE) && defined (CONFIG_6xx)
-	/* 
-	 * no reason to turn off the cache for a single word 
-	 * or a few bytes -- Cort 
-	 */
-	mfspr	r7,HID0
-	ori	r8,r7,HID0_DLOCK
-	mtspr	HID0,r8
-#endif /* CLEAR_NO_CACHE */
 	/* clear word sized chunks */
 	andi.	r0,r6,3
 	add	r4,r0,r4
@@ -353,10 +342,6 @@
 1:	stwu	r5,4(r6)
 	bdnz	1b
 6:	andi.	r4,r4,3
-#if defined(CLEAR_NO_CACHE) && defined (CONFIG_6xx)
-	/* restore the original state of HID0 in case cache was off -- Cort */
-	mtspr	HID0,r7
-#endif /* CLEAR_NO_CACHE */	
 	/* clear byte sized chunks */
 7:	cmpwi	0,r4,0
 	beqlr
@@ -371,9 +356,6 @@
 	.align	2
 	.long	11b,99b
 	.long	1b,99b
-#ifdef CLEAR_USE_DCBZ
-	/*.long	66b,99b*/
-#endif		
 	.long	8b,99b
 .text
 
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbx_defconfig linux/arch/ppc/mbx_defconfig
--- v2.2.3/linux/arch/ppc/mbx_defconfig	Wed Mar 10 15:29:45 1999
+++ linux/arch/ppc/mbx_defconfig	Wed Mar 10 21:30:32 1999
@@ -14,8 +14,8 @@
 # CONFIG_ALL_PPC is not set
 # CONFIG_APUS is not set
 CONFIG_MBX=y
-CONFIG_SMP=n
 CONFIG_MACH_SPECIFIC=y
+# CONFIG_SMP is not set
 CONFIG_SERIAL_CONSOLE=y
 
 #
@@ -58,8 +58,26 @@
 # Block devices
 #
 # CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-# CONFIG_BLK_DEV_HD_ONLY is not set
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
 # CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
@@ -90,7 +108,7 @@
 # CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_RARP is not set
 CONFIG_IP_NOSR=y
-# CONFIG_SKB_LARGE is not set
+CONFIG_SKB_LARGE=y
 # CONFIG_IPV6 is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
@@ -102,7 +120,11 @@
 # CONFIG_WAN_ROUTER is not set
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
-# CONFIG_CPU_IS_SLOW is not set
+CONFIG_CPU_IS_SLOW=y
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -126,6 +148,7 @@
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_RTL8139 is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
@@ -138,6 +161,8 @@
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 
 #
 # Amateur Radio support
@@ -161,7 +186,8 @@
 #
 # Character devices
 #
-# CONFIG_VT is not set
+CONFIG_VT=y
+# CONFIG_VT_CONSOLE is not set
 CONFIG_SERIAL=y
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
@@ -170,9 +196,17 @@
 # CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
 # CONFIG_VIDEO_DEV is not set
-# CONFIG_NVRAM is not set
+
+#
+# Joystick support
+#
 # CONFIG_JOYSTICK is not set
 
 #
@@ -184,35 +218,46 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_EXT2_FS is not set
-# CONFIG_ISO9660_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
 # CONFIG_FAT_FS is not set
 # CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
 CONFIG_ROOT_NFS=y
 # CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_UFS_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
 # CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MAC_PARTITION is not set
 # CONFIG_SMD_DISKLABEL is not set
 # CONFIG_SOLARIS_X86_PARTITION is not set
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_MAC_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
 # CONFIG_NLS is not set
 
 #
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/Makefile linux/arch/ppc/mbxboot/Makefile
--- v2.2.3/linux/arch/ppc/mbxboot/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbxboot/Makefile	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,101 @@
+#
+# arch/ppc/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1994 by Linus Torvalds
+# Adapted for PowerPC by Gary Thomas
+# modified by Cort (cort@cs.nmt.edu)
+#
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS)  -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $<
+.S.s:
+	$(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $<
+.S.o:
+	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
+
+ZOFF = 0
+ZSZ = 0
+IOFF = 0
+ISZ = 0
+
+TFTPIMAGE=/tftpboot/zImage.mbx
+ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000
+GZIP_FLAGS = -v9
+
+OBJECTS := head.o misc.o ../coffboot/zlib.o mbxtty.o
+CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include -DCONFIG_MBX
+OBJCOPY = $(CROSS_COMPILE)objcopy
+OBJCOPY_ARGS = -O elf32-powerpc
+
+all:	zImage
+
+zvmlinux.initrd: zvmlinux
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+		--add-section=initrd=ramdisk.image.gz \
+		--add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.initrd.tmp zvmlinux.initrd
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd initrd` \
+		-DINITRD_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd initrd` \
+		-DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux.initrd image` \
+		-DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux.initrd image` \
+		-DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+		--add-section=initrd=ramdisk.image.gz \
+		--add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.initrd.tmp $@
+	rm zvmlinux.initrd.tmp
+
+zImage: zvmlinux
+	ln -sf zvmlinux zImage
+
+zImage.initrd: zvmlinux.initrd
+	ln -sf zvmlinux.initrd zImage.initrd
+
+zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
+#
+# build the boot loader image and then compute the offset into it
+# for the kernel image
+#
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.tmp $@
+#
+# then with the offset rebuild the bootloader so we know where the kernel is
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=`sh offset $(OBJDUMP) zvmlinux image` \
+		-DZIMAGE_SIZE=`sh size $(OBJDUMP) zvmlinux image` -DKERNELBASE=$(KERNELBASE) \
+		-c -o misc.o misc.c
+	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \
+		zvmlinux.tmp $@
+	rm zvmlinux.tmp
+
+znetboot : zImage
+	cp zImage $(TFTPIMAGE)
+
+znetboot.initrd : zImage.initrd
+	cp zImage.initrd $(TFTPIMAGE)
+
+clean:
+	rm -f vmlinux* zvmlinux* zImage*
+
+fastdep:
+	$(TOPDIR)/scripts/mkdep *.[Sch] > .depend
+
+dep:
+	$(CPP) -M *.S *.c > .depend
+
+# just here to match coffboot/Makefile
+vmlinux.coff:
+
+vmlinux.coff.initrd:
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/head.S linux/arch/ppc/mbxboot/head.S
--- v2.2.3/linux/arch/ppc/mbxboot/head.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbxboot/head.S	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,201 @@
+#include <linux/config.h>
+#include "../kernel/ppc_defs.h"
+#include "../kernel/ppc_asm.tmpl"
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+	.text
+
+/*
+ * $Id: head.S,v 1.2 1999/02/17 06:29:41 cort Exp $
+ *	
+ * This code is loaded by the ROM loader at some arbitrary location.
+ * Move it to high memory so that it can load the kernel at 0x0000.
+ *
+ * The MBX EPPC-Bug understands ELF, so it loads us into the location
+ * specified in the header.  This is a two step process.  First, EPPC-Bug
+ * loads the file into the intermediate buffer memory location specified
+ * by the environment parameters.  When it discovers this is an ELF
+ * binary, it relocates to the link address for us.  Unfortunately, the
+ * header does not move with the file, so we have to find the
+ * intermediate load location and read the header from there.  From
+ * information provided by Motorola (thank you), we know this intermediate
+ * location can be found from the NVRAM environment.
+ * All of these addresses must be somewhat carefully chosen to make sure
+ * we don't overlap the regions.  I chose to load the kernel at 0, the
+ * compressed image loads at 0x00100000, and the MBX intermediate buffer
+ * was set to 0x00200000.  Provided the loaded kernel image never grows
+ * over one megabyte (which I am going to ensure never happens :-), these
+ * will work fine.  When we get called from EPPC-Bug, registers are:
+ *              R1 - Stack pointer at a high memory address.
+ *              R3 - Pointer to Board Information Block.
+ *              R4 - Pointer to argument string.
+ *              Interrupts masked, cache and MMU disabled.
+ */
+
+	.globl	start
+start:
+	bl	start_
+start_:
+	mr	r11,r3		/* Save pointer to residual/board data */
+/* Clear all of BSS */
+	lis	r3,edata@h
+	ori	r3,r3,edata@l
+	lis	r4,end@h
+	ori	r4,r4,end@l
+	subi	r3,r3,4
+	subi	r4,r4,4
+	li	r0,0
+50:	stwu	r0,4(r3)
+	cmp	0,r3,r4
+	bne	50b
+90:	mr	r9,r1			/* Save old stack pointer (in case it matters) */
+	lis	r1,.stack@h
+	ori	r1,r1,.stack@l
+	addi	r1,r1,4096*2
+	subi	r1,r1,256
+	li	r2,0x000F		/* Mask pointer to 16-byte boundary */
+	andc	r1,r1,r2
+/* Run loader */
+	mr	r3, r11
+        mr      r21, r11
+	bl      serial_init		/* Init MBX serial port */
+
+	lis	r8, 0xfa200000@h	/* Disable Ethernet SCC */
+	li	r0, 0
+	stw	r0, 0x0a00(r8)
+
+	mr      r11, r21
+	lis	r8,start@h
+	ori	r8,r8,start@l
+	li	r9,end@h
+	ori	r9,r9,end@l
+	sub	r7,r8,r9
+	srwi	r7,r7,2
+#define ILAP_ADDRESS    0xfa000020
+        lis     r8, ILAP_ADDRESS@h
+        lwz     r8, ILAP_ADDRESS@l(r8)
+        addis   r8, r8, 1               /* Add 64K */
+	mr	r3,r8			/* Load point */
+	mr	r4,r7			/* Program length */
+	mr	r5,r6			/* Checksum */
+	mr	r6,r11			/* Residual data */
+	bl	decompress_kernel
+	
+	/* changed to use r3 (as firmware does) for kernel
+	   as ptr to residual -- Cort*/
+	lis	r6,cmd_line@h
+	ori	r6,r6,cmd_line@l
+	lwz	r6, 0(r6)
+	subi	r7,r6,1
+00:	lbzu	r2,1(r7)
+	cmpi	0,r2,0
+	bne	00b
+
+	/* r4,r5 have initrd_start, size */
+	lis	r2,initrd_start@h
+	ori	r2,r2,initrd_start@l
+	lwz	r4,0(r2)
+	lis	r2,initrd_end@h
+	ori	r2,r2,initrd_end@l
+	lwz	r5,0(r2)
+	
+	/* tell kernel we're prep */
+	/* 
+	 * get start address of kernel code which is stored as a coff
+	 * entry.  see boot/head.S -- Cort 
+	 */
+	li	r9,0x0
+	lwz	r9,0(r9)
+	mtlr	r9
+	blr
+hang:
+	b	hang	
+
+/*
+ * Delay for a number of microseconds
+ * -- Use the BUS timer (assumes 66MHz)
+ */
+	.globl	udelay
+udelay:		
+	mfspr	r4,PVR
+	srwi	r4,r4,16
+	cmpi	0,r4,1		/* 601 ? */
+	bne	.udelay_not_601
+00:	li	r0,86	/* Instructions / microsecond? */
+	mtctr	r0
+10:	addi	r0,r0,0 /* NOP */
+	bdnz	10b
+	subic.	r3,r3,1
+	bne	00b
+	blr
+
+.udelay_not_601:		
+	mulli	r4,r3,1000	/* nanoseconds */
+	addi	r4,r4,59
+	li	r5,60
+	divw	r4,r4,r5	/* BUS ticks */
+1:	mftbu	r5
+	mftb	r6
+	mftbu	r7
+	cmp	0,r5,r7
+	bne	1b		/* Get [synced] base time */
+	addc	r9,r6,r4	/* Compute end time */
+	addze	r8,r5
+2:	mftbu	r5
+	cmp	0,r5,r8
+	blt	2b
+	bgt	3f
+	mftb	r6
+	cmp	0,r6,r9
+	blt	2b
+3:	blr		
+
+.globl _get_HID0
+_get_HID0:		
+	mfspr	r3,HID0
+	blr
+
+.globl _put_HID0
+_put_HID0:		
+	mtspr	HID0,r3
+	blr
+		
+.globl _get_MSR
+_get_MSR:		
+	mfmsr	r3
+	blr
+	
+.globl _put_MSR
+_put_MSR:		
+	mtmsr	r3
+	blr
+
+/*
+ * Flush instruction cache
+ * *** I'm really paranoid here!
+ */
+_GLOBAL(flush_instruction_cache)
+	mflr	r5
+	bl	flush_data_cache
+	mtlr	r5
+	blr
+	
+#define NUM_CACHE_LINES 128*8
+#define CACHE_LINE_SIZE 32 
+#define cache_flush_buffer 0x1000
+
+/*
+ * Flush data cache
+ * *** I'm really paranoid here!
+ */
+_GLOBAL(flush_data_cache)
+	lis	r3,cache_flush_buffer@h
+	ori	r3,r3,cache_flush_buffer@l
+	li	r4,NUM_CACHE_LINES
+	mtctr	r4
+00:	lwz	r4,0(r3)
+	addi	r3,r3,CACHE_LINE_SIZE	/* Next line, please */
+	bdnz	00b	
+10:	blr
+	.comm	.stack,4096*2,4
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/mbxtty.c linux/arch/ppc/mbxboot/mbxtty.c
--- v2.2.3/linux/arch/ppc/mbxboot/mbxtty.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbxboot/mbxtty.c	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,201 @@
+
+
+/* Minimal serial functions needed to send messages out the serial
+ * port on the MBX console.
+ *
+ * The MBX uxes SMC1 for the serial port.  We reset the port and use
+ * only the first BD that EPPC-Bug set up as a character FIFO.
+ *
+ * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug
+ * use COM1 instead of SMC1 as the console port.  This kinda sucks
+ * for the rest of the kernel, so here we force the use of SMC1 again.
+ * I f**ked around for a day trying to figure out how to make EPPC-Bug
+ * use SMC1, but gave up and decided to fix it here.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+#ifdef CONFIG_FADS
+#include <asm/fads.h>
+#endif
+#include "../8xx_io/commproc.h"
+
+#ifdef CONFIG_MBX
+#define MBX_CSR1	((volatile u_char *)0xfa100000)
+#define CSR1_COMEN	(u_char)0x02
+#endif
+
+static cpm8xx_t	*cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm);
+
+void
+serial_init(bd_t *bd)
+{
+	volatile smc_t		*sp;
+	volatile smc_uart_t	*up;
+	volatile cbd_t	*tbdf, *rbdf;
+	volatile cpm8xx_t	*cp;
+	uint	dpaddr, memaddr;
+
+	cp = cpmp;
+	sp = (smc_t*)&(cp->cp_smc[0]);
+	up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1];
+
+	/* Disable transmitter/receiver.
+	*/
+	sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+
+#ifdef CONFIG_MBX
+	if (*MBX_CSR1 & CSR1_COMEN) {
+		/* COM1 is enabled.  Initialize SMC1 and use it for
+		 * the console port.
+		 */
+
+		/* Enable SDMA.
+		*/
+		((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1;
+
+		/* Use Port B for SMCs instead of other functions.
+		*/
+		cp->cp_pbpar |= 0x00000cc0;
+		cp->cp_pbdir &= ~0x00000cc0;
+		cp->cp_pbodr &= ~0x00000cc0;
+
+		/* Allocate space for two buffer descriptors in the DP ram.
+		 * For now, this address seems OK, but it may have to
+		 * change with newer versions of the firmware.
+		 */
+		dpaddr = 0x0800;
+
+		/* Grab a few bytes from the top of memory.  EPPC-Bug isn't
+		 * running any more, so we can do this.
+		 */
+		memaddr = (bd->bi_memsize - 32) & ~15;
+
+		/* Set the physical address of the host memory buffers in
+		 * the buffer descriptors.
+		 */
+		rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr];
+		rbdf->cbd_bufaddr = memaddr;
+		rbdf->cbd_sc = 0;
+		tbdf = rbdf + 1;
+		tbdf->cbd_bufaddr = memaddr+4;
+		tbdf->cbd_sc = 0;
+
+		/* Set up the uart parameters in the parameter ram.
+		*/
+		up->smc_rbase = dpaddr;
+		up->smc_tbase = dpaddr+sizeof(cbd_t);
+		up->smc_rfcr = SMC_EB;
+		up->smc_tfcr = SMC_EB;
+
+		/* Set UART mode, 8 bit, no parity, one stop.
+		 * Enable receive and transmit.
+		 */
+		sp->smc_smcmr = smcr_mk_clen(9) |  SMCMR_SM_UART;
+
+		/* Mask all interrupts and remove anything pending.
+		*/
+		sp->smc_smcm = 0;
+		sp->smc_smce = 0xff;
+
+		/* Set up the baud rate generator.
+		 * See 8xx_io/commproc.c for details.
+		 */
+		cp->cp_simode = 0x10000000;
+		cp->cp_brgc1 =
+			((((bd->bi_intfreq * 1000000)/16) / 9600) << 1) | CPM_BRG_EN;
+
+		/* Enable SMC1 for console output.
+		*/
+		*MBX_CSR1 &= ~CSR1_COMEN;
+	}
+	else {
+#endif
+		/* SMC1 is used as console port.
+		*/
+		tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase];
+		rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase];
+
+		/* Issue a stop transmit, and wait for it.
+		*/
+		cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1,
+					CPM_CR_STOP_TX) | CPM_CR_FLG;
+		while (cp->cp_cpcr & CPM_CR_FLG);
+#ifdef CONFIG_MBX
+	}
+#endif
+
+	/* Make the first buffer the only buffer.
+	*/
+	tbdf->cbd_sc |= BD_SC_WRAP;
+	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
+
+	/* Single character receive.
+	*/
+	up->smc_mrblr = 1;
+	up->smc_maxidl = 0;
+
+	/* Initialize Tx/Rx parameters.
+	*/
+	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+	while (cp->cp_cpcr & CPM_CR_FLG);
+
+	/* Enable transmitter/receiver.
+	*/
+	sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
+}
+
+void
+serial_putchar(const char c)
+{
+	volatile cbd_t		*tbdf;
+	volatile char		*buf;
+	volatile smc_uart_t	*up;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
+
+	/* Wait for last character to go.
+	*/
+	buf = (char *)tbdf->cbd_bufaddr;
+	while (tbdf->cbd_sc & BD_SC_READY);
+
+	*buf = c;
+	tbdf->cbd_datlen = 1;
+	tbdf->cbd_sc |= BD_SC_READY;
+}
+
+char
+serial_getc()
+{
+	volatile cbd_t		*rbdf;
+	volatile char		*buf;
+	volatile smc_uart_t	*up;
+	char			c;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+	/* Wait for character to show up.
+	*/
+	buf = (char *)rbdf->cbd_bufaddr;
+	while (rbdf->cbd_sc & BD_SC_EMPTY);
+	c = *buf;
+	rbdf->cbd_sc |= BD_SC_EMPTY;
+
+	return(c);
+}
+
+int
+serial_tstc()
+{
+	volatile cbd_t		*rbdf;
+	volatile smc_uart_t	*up;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+	return(!(rbdf->cbd_sc & BD_SC_EMPTY));
+}
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/misc.c linux/arch/ppc/mbxboot/misc.c
--- v2.2.3/linux/arch/ppc/mbxboot/misc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbxboot/misc.c	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,637 @@
+/*
+ * misc.c
+ *
+ * $Id: misc.c,v 1.1 1999/02/17 05:00:06 cort Exp $
+ * 
+ * Adapted for PowerPC by Gary Thomas
+ *
+ * Rewritten by Cort Dougan (cort@cs.nmt.edu)
+ * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
+ */
+
+#include <linux/types.h>
+#include "../coffboot/zlib.h"
+#include "asm/residual.h"
+#include <linux/elf.h>
+#include <linux/config.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+#ifdef CONFIG_FADS
+#include <asm/fads.h>
+#endif
+
+/*
+ * Please send me load/board info and such data for hardware not
+ * listed here so I can keep track since things are getting tricky
+ * with the different load addrs with different firmware.  This will
+ * help to avoid breaking the load/boot process.
+ * -- Cort
+ */
+char *avail_ram;
+char *end_avail;
+
+/* Because of the limited amount of memory on the MBX, it presents
+ * loading problems.  The biggest is that we load this boot program
+ * into a relatively low memory address, and the Linux kernel Bss often
+ * extends into this space when it get loaded.  When the kernel starts
+ * and zeros the BSS space, it also writes over the information we
+ * save here and pass to the kernel (command line and board info).
+ * On the MBX we grab some known memory holes to hold this information.
+ */
+#if defined(CONFIG_SERIAL_CONSOLE)
+char cmd_preset[] = "console=ttyS0,9600n8";
+#else
+char cmd_preset[] = "";
+#endif
+char	cmd_buf[256];
+char	*cmd_line = cmd_buf;
+
+char	*root_string = "root=/dev/nfs";
+char	*nfsaddrs_string = "nfsaddrs=";
+char	*nfsroot_string = "nfsroot=";
+char	*defroot_string = "/sys/mbxroot";
+int	do_ipaddrs(char **cmd_cp, int echo);
+void	do_nfsroot(char **cmd_cp, char *dp);
+int	strncmp(const char * cs,const char * ct,size_t count);
+char	*strrchr(const char * s, int c);
+
+RESIDUAL hold_resid_buf;
+RESIDUAL *hold_residual = &hold_resid_buf;
+unsigned long initrd_start = 0, initrd_end = 0;
+char *zimage_start;
+int zimage_size;
+
+char *vidmem = (char *)0xC00B8000;
+int lines, cols;
+int orig_x, orig_y;
+
+void puts(const char *);
+void putc(const char c);
+void puthex(unsigned long val);
+void _bcopy(char *src, char *dst, int len);
+void * memcpy(void * __dest, __const void * __src,
+			    int __n);
+void gunzip(void *, int, unsigned char *, int *);
+
+void pause()
+{
+	puts("pause\n");
+}
+
+void exit()
+{
+	puts("exit\n");
+	while(1); 
+}
+
+/* The MBX is just the serial port.
+*/
+tstc(void)
+{
+        return (serial_tstc());
+}
+
+getc(void)
+{
+        while (1) {
+                if (serial_tstc()) return (serial_getc());
+        }
+}
+
+void 
+putc(const char c)
+{
+        serial_putchar(c);
+}
+
+void puts(const char *s)
+{
+        char c;
+
+        while ( ( c = *s++ ) != '\0' ) {
+                serial_putchar(c);
+                if ( c == '\n' )
+                        serial_putchar('\r');
+        }
+}
+
+
+void * memcpy(void * __dest, __const void * __src,
+			    int __n)
+{
+	int i;
+	char *d = (char *)__dest, *s = (char *)__src;
+
+	for (i=0;i<__n;i++) d[i] = s[i];
+}
+
+int memcmp(__const void * __dest, __const void * __src,
+			    int __n)
+{
+	int i;
+	char *d = (char *)__dest, *s = (char *)__src;
+
+	for (i=0;i<__n;i++, d++, s++)
+	{
+		if (*d != *s)
+		{
+			return (*s - *d);
+		}
+	}
+	return (0);
+}
+
+void error(char *x)
+{
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted");
+
+	while(1);	/* Halt */
+}
+
+void *zalloc(void *x, unsigned items, unsigned size)
+{
+	void *p = avail_ram;
+	
+	size *= items;
+	size = (size + 7) & -8;
+	avail_ram += size;
+	if (avail_ram > end_avail) {
+		puts("oops... out of memory\n");
+		pause();
+	}
+	return p;
+}
+
+void zfree(void *x, void *addr, unsigned nb)
+{
+}
+
+#define HEAD_CRC	2
+#define EXTRA_FIELD	4
+#define ORIG_NAME	8
+#define COMMENT		0x10
+#define RESERVED	0xe0
+
+#define DEFLATED	8
+
+
+void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
+{
+	z_stream s;
+	int r, i, flags;
+	
+	/* skip header */
+	i = 10;
+	flags = src[3];
+	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+		puts("bad gzipped data\n");
+		exit();
+	}
+	if ((flags & EXTRA_FIELD) != 0)
+		i = 12 + src[10] + (src[11] << 8);
+	if ((flags & ORIG_NAME) != 0)
+		while (src[i++] != 0)
+			;
+	if ((flags & COMMENT) != 0)
+		while (src[i++] != 0)
+			;
+	if ((flags & HEAD_CRC) != 0)
+		i += 2;
+	if (i >= *lenp) {
+		puts("gunzip: ran out of data in header\n");
+		exit();
+	}
+	
+	s.zalloc = zalloc;
+	s.zfree = zfree;
+	r = inflateInit2(&s, -MAX_WBITS);
+	if (r != Z_OK) {
+		puts("inflateInit2 returned %d\n");
+		exit();
+	}
+	s.next_in = src + i;
+	s.avail_in = *lenp - i;
+	s.next_out = dst;
+	s.avail_out = dstlen;
+	r = inflate(&s, Z_FINISH);
+	if (r != Z_OK && r != Z_STREAM_END) {
+		puts("inflate returned %d\n");
+		exit();
+	}
+	*lenp = s.next_out - (unsigned char *) dst;
+	inflateEnd(&s);
+}
+
+unsigned char sanity[0x2000];
+
+unsigned long
+decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual)
+{
+	int timer;
+	extern unsigned long start;
+	char *cp, ch;
+	unsigned long i, motorola_id = 0;
+	char needs_reloc = 0;
+	BATU *u;
+	BATL *l;
+	char	*dp;
+
+	lines = 25;
+	cols = 80;
+	orig_x = 0;
+	orig_y = 24;
+
+	/* Grab some space for the command line and board info.  Since
+	 * we no longer use the ELF header, but it was loaded, grab
+	 * that space.
+	 */
+	cmd_line = (char *)(load_addr - 0x10000);
+	hold_residual = (RESIDUAL *)(cmd_line + sizeof(cmd_buf));
+	/* copy board data */
+	if (residual)
+		memcpy(hold_residual,residual,sizeof(bd_t));
+
+	/* MBX/prep sometimes put the residual/board info at the end of mem 
+	 * assume 16M for now  -- Cort
+	 * To boot on standard MBX boards with 4M, we can't use initrd,
+	 * and we have to assume less memory.  -- Dan
+	 */
+	if ( INITRD_OFFSET )
+		end_avail = (char *)0x01000000;
+	else
+		end_avail = (char *)0x00400000;
+
+	/* let residual data tell us it's higher */
+	if ( (unsigned long)residual > 0x00800000 )
+		end_avail = (char *)PAGE_ALIGN((unsigned long)residual);
+
+	puts("loaded at:     "); puthex(load_addr);
+	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
+	if ( (unsigned long)load_addr != (unsigned long)&start )
+	{
+		puts("relocated to:  "); puthex((unsigned long)&start);
+		puts(" ");
+		puthex((unsigned long)((unsigned long)&start + (4*num_words)));
+		puts("\n");
+	}
+
+	if ( residual )
+	{
+		puts("board data at: "); puthex((unsigned long)residual);
+		puts(" ");
+		puthex((unsigned long)((unsigned long)residual + sizeof(bd_t)));
+		puts("\n");
+		puts("relocated to:  ");
+		puthex((unsigned long)hold_residual);
+		puts(" ");
+		puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t)));
+		puts("\n");
+	}
+
+	/* we have to subtract 0x10000 here to correct for objdump including the
+	   size of the elf header which we strip -- Cort */
+	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
+	zimage_size = ZIMAGE_SIZE;
+
+	if ( INITRD_OFFSET )
+		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
+	else
+		initrd_start = 0;
+	initrd_end = INITRD_SIZE + initrd_start;
+
+	/*
+	 * setup avail_ram - this is the first part of ram usable
+	 * by the uncompress code. -- Cort
+	 */
+	avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_start+zimage_size);
+	if ( ((load_addr+(num_words*4)) > (unsigned long) avail_ram)
+		&& (load_addr <= 0x01000000) )
+		avail_ram = (char *)(load_addr+(num_words*4));
+	if ( (((unsigned long)&start+(num_words*4)) > (unsigned long) avail_ram)
+		&& (load_addr <= 0x01000000) )
+		avail_ram = (char *)((unsigned long)&start+(num_words*4));
+	
+	/* relocate zimage */
+	puts("zimage at:     "); puthex((unsigned long)zimage_start);
+	puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
+	/*
+	 * don't relocate the zimage if it was loaded above 16M since
+	 * things get weird if we try to relocate -- Cort
+	 * We don't relocate zimage on a base MBX board because of
+	 * insufficient memory.  In this case we don't have initrd either,
+	 * so use that as an indicator.  -- Dan
+	 */
+	
+	/* Determine if we have a Motorola board */
+	needs_reloc = 0;
+	if ( (( (unsigned long)zimage_start <= 0x01000000 ) && initrd_start)
+		|| needs_reloc)
+	{
+		memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size),
+			(void *)zimage_start, zimage_size );	
+		zimage_start = (char *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-zimage_size);
+		end_avail = (char *)zimage_start;
+		puts("relocated to:  "); puthex((unsigned long)zimage_start);
+		puts(" ");
+		puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
+		puts("\n");
+	}
+
+	/* relocate initrd */
+	if ( initrd_start )
+	{
+		puts("initrd at:     "); puthex(initrd_start);
+		puts(" "); puthex(initrd_end); puts("\n");
+		/*
+		 * Memory is really tight on the MBX (we can assume 4M)
+		 * so put the initrd at the TOP of ram, and set end_avail
+		 * to right after that.
+		 *
+		 * I should do something like this for prep, too and keep
+		 * a variable end_of_DRAM to keep track of what we think the
+		 * max ram is.
+		 * -- Cort
+		 */
+		if (needs_reloc)
+		{
+			memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+
+				(unsigned long)end_avail-INITRD_SIZE),
+				(void *)initrd_start,
+				INITRD_SIZE );
+			initrd_start = PAGE_ALIGN(-PAGE_SIZE+
+				(unsigned long)end_avail-INITRD_SIZE);
+			initrd_end = initrd_start + INITRD_SIZE;
+			end_avail = (char *)initrd_start;
+			puts("relocated to:  "); puthex(initrd_start);
+			puts(" "); puthex(initrd_end); puts("\n");
+		}
+	}
+
+	puts("avail ram:     "); puthex((unsigned long)avail_ram); puts(" ");
+	puthex((unsigned long)end_avail); puts("\n");
+
+	puts("\nLinux/PPC load: ");
+	timer = 0;
+	cp = cmd_line;
+	memcpy (cmd_line, cmd_preset, sizeof(cmd_preset));
+	while ( *cp ) putc(*cp++);
+	while (timer++ < 5*1000) {
+		if (tstc()) {
+			while ((ch = getc()) != '\n' && ch != '\r') {
+				if (ch == '\b') {
+					if (cp != cmd_line) {
+						cp--;
+						puts("\b \b");
+					}
+				  } else if (ch == '?') {
+					if (!do_ipaddrs(&cp, 1)) {
+						  *cp++ = ch;
+						  putc(ch);
+					}
+				} else {
+					*cp++ = ch;
+					putc(ch);
+				}
+			}
+			break;  /* Exit 'timer' loop */
+		}
+		udelay(1000);  /* 1 msec */
+	}
+	*cp = 0;
+	/* The MBX does not currently have any default boot strategy.
+	 * If the command line is not filled in, we will automatically
+	 * create the default network boot.
+	 */
+	if (cmd_line[0] == 0) {
+		dp = root_string;
+		while (*dp != 0)
+			*cp++ = *dp++;
+		*cp++ = ' ';
+
+		dp = nfsaddrs_string;
+		while (*dp != 0)
+			*cp++ = *dp++;
+		dp = cp;
+		do_ipaddrs(&cp, 0);
+		*cp++ = ' ';
+
+		/* Add the server address to the root file system path.
+		*/
+		dp = strrchr(dp, ':');
+		dp++;
+		do_nfsroot(&cp, dp);
+		*cp = 0;
+	}
+	puts("\n");
+
+	/* mappings on early boot can only handle 16M */
+	if ( (int)(cmd_line[0]) > (16<<20))
+		puts("cmd_line located > 16M\n");
+	if ( (int)hold_residual > (16<<20))
+		puts("hold_residual located > 16M\n");
+	if ( initrd_start > (16<<20))
+		puts("initrd_start located > 16M\n");
+       
+	puts("Uncompressing Linux...");
+
+	gunzip(0, 0x400000, zimage_start, &zimage_size);
+	puts("done.\n");
+	puts("Now booting the kernel\n");
+	return (unsigned long)hold_residual;
+}
+
+int
+do_ipaddrs(char **cmd_cp, int echo)
+{
+	char	*cp, *ip, ch;
+	unsigned char	ipd;
+	int	i, j, retval;
+
+	/* We need to create the string:
+	 *	<my_ip>:<serv_ip>
+	 */
+	cp = *cmd_cp;
+	retval = 0;
+
+	if ((cp - 9) >= cmd_line) {
+		if (strncmp(cp - 9, "nfsaddrs=", 9) == 0) {
+			ip = (char *)0xfa000060;
+			retval = 1;
+			for (j=0; j<2; j++) {
+				for (i=0; i<4; i++) {
+					ipd = *ip++;
+
+					ch = ipd/100;
+					if (ch) {
+						ch += '0';
+						if (echo)
+							putc(ch);
+						*cp++ = ch;
+						ipd -= 100 * (ch - '0');
+					}
+
+					ch = ipd/10;
+					if (ch) {
+						ch += '0';
+						if (echo)
+							putc(ch);
+						*cp++ = ch;
+						ipd -= 10 * (ch - '0');
+					}
+
+					ch = ipd + '0';
+					if (echo)
+						putc(ch);
+					*cp++ = ch;
+
+					ch = '.';
+					if (echo)
+						putc(ch);
+					*cp++ = ch;
+				}
+
+				/* At the end of the string, remove the
+				 * '.' and replace it with a ':'.
+				 */
+				*(cp - 1) = ':';
+				if (echo) {
+					putc('\b'); putc(':');
+				}
+			}
+
+			/* At the end of the second string, remove the
+			 * '.' from both the command line and the
+			 * screen.
+			 */
+			--cp;
+			putc('\b'); putc(' '); putc('\b');
+		}
+	}
+	*cmd_cp = cp;
+	return(retval);
+}
+
+void
+do_nfsroot(char **cmd_cp, char *dp)
+{
+	char	*cp, *rp, *ep;
+
+	/* The boot argument (i.e /sys/mbxroot/zImage) is stored
+	 * at offset 0x0078 in NVRAM.  We use this path name to
+	 * construct the root file system path.
+	 */
+	cp = *cmd_cp;
+
+	/* build command string.
+	*/
+	rp = nfsroot_string;
+	while (*rp != 0)
+		*cp++ = *rp++;
+
+	/* Add the server address to the path.
+	*/
+	while (*dp != ' ')
+		*cp++ = *dp++;
+	*cp++ = ':';
+
+	rp = (char *)0xfa000078;
+	ep = strrchr(rp, '/');
+
+	if (ep != 0) {
+		while (rp < ep)
+			*cp++ = *rp++;
+	}
+	else {
+		rp = defroot_string;
+		while (*rp != 0)
+			*cp++ = *rp++;
+	}
+
+	*cmd_cp = cp;
+}
+
+size_t strlen(const char * s)
+{
+	const char *sc;
+
+	for (sc = s; *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+	register signed char __res = 0;
+
+	while (count) {
+		if ((__res = *cs - *ct++) != 0 || !*cs++)
+			break;
+		count--;
+	}
+
+	return __res;
+}
+
+char * strrchr(const char * s, int c)
+{
+       const char *p = s + strlen(s);
+       do {
+           if (*p == (char)c)
+               return (char *)p;
+       } while (--p >= s);
+       return NULL;
+}
+
+void puthex(unsigned long val)
+{
+	unsigned char buf[10];
+	int i;
+	for (i = 7;  i >= 0;  i--)
+	{
+		buf[i] = "0123456789ABCDEF"[val & 0x0F];
+		val >>= 4;
+	}
+	buf[8] = '\0';
+	puts(buf);
+}
+
+/*
+ * PCI/ISA I/O support
+ */
+
+volatile unsigned char *ISA_io  = (unsigned char *)0x80000000;
+volatile unsigned char *ISA_mem = (unsigned char *)0xC0000000;
+
+void
+outb(int port, char val)
+{
+	/* Ensure I/O operations complete */
+	__asm__ volatile("eieio");
+	ISA_io[port] = val;
+}
+
+unsigned char
+inb(int port)
+{
+	/* Ensure I/O operations complete */
+	__asm__ volatile("eieio");
+	return (ISA_io[port]);
+}
+
+unsigned long
+local_to_PCI(unsigned long addr)
+{
+	return ((addr & 0x7FFFFFFF) | 0x80000000);
+}
+
+void
+_bcopy(char *src, char *dst, int len)
+{
+	while (len--) *dst++ = *src++;
+}
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/offset linux/arch/ppc/mbxboot/offset
--- v2.2.3/linux/arch/ppc/mbxboot/offset	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbxboot/offset	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux| awk '{print $6}'`
+echo "0x"$OFFSET
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mbxboot/size linux/arch/ppc/mbxboot/size
--- v2.2.3/linux/arch/ppc/mbxboot/size	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/mbxboot/size	Fri Mar 19 10:50:03 1999
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux | awk '{print $3}'`
+echo "0x"$OFFSET
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v2.2.3/linux/arch/ppc/mm/init.c	Fri Jan  8 22:36:03 1999
+++ linux/arch/ppc/mm/init.c	Fri Mar 19 10:50:04 1999
@@ -1,5 +1,5 @@
-/*
- *  $Id: init.c,v 1.139 1998/12/29 19:53:49 cort Exp $
+ /*
+ *  $Id: init.c,v 1.150 1999/03/10 08:16:33 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -34,6 +34,7 @@
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/openpic.h>
 #ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/blk.h>		/* for initrd_* */
 #endif
@@ -70,10 +71,12 @@
 unsigned long ioremap_base;
 unsigned long ioremap_bot;
 unsigned long avail_start;
-struct pgtable_cache_struct quicklists;
 extern int num_memory;
 extern struct mem_info memory[NUM_MEMINFO];
 extern boot_infos_t *boot_infos;
+#ifndef __SMP__
+struct pgtable_cache_struct quicklists;
+#endif
 
 void MMU_init(void);
 static void *MMU_get_page(void);
@@ -883,7 +886,8 @@
 	}	    
 }
 
-__initfunc(static void *MMU_get_page(void))
+/* This can get called from ioremap, so don't make it an initfunc, OK? */
+static void *MMU_get_page(void)
 {
 	void *p;
 
@@ -926,14 +930,16 @@
 		break;
 	case _MACH_prep:
 		FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
-		FREESEC(__openfirmware_begin,__openfirmware_end,num_openfirmware_pages);
 		break;
 	case _MACH_mbx:
 		FREESEC(__pmac_begin,__pmac_end,num_pmac_pages);
-		FREESEC(__openfirmware_begin,__openfirmware_end,num_openfirmware_pages);
 		FREESEC(__prep_begin,__prep_end,num_prep_pages);
 		break;
 	}
+
+	if ( !have_of )
+		FREESEC( __openfirmware_begin, __openfirmware_end,
+			 num_openfirmware_pages );
 	
 	printk ("Freeing unused kernel memory: %ldk init",
 		(num_freed_pages * PAGE_SIZE) >> 10);
@@ -955,7 +961,6 @@
  */
 __initfunc(void MMU_init(void))
 {
-
 #ifdef __SMP__
 	if ( first_cpu_booted ) return;
 #endif /* __SMP__ */
@@ -989,6 +994,7 @@
 		break;
 	case _MACH_chrp:
 		setbat(0, 0xf8000000, 0xf8000000, 0x08000000, IO_PAGE);
+		setbat(1, 0x80000000, 0x80000000, 0x10000000, IO_PAGE);
 		break;
 	case _MACH_Pmac:
 		{
@@ -1029,6 +1035,7 @@
         ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE);
 	/* ide needs to be able to get at PCI space -- Cort */
         ioremap(0x80000000, 0x4000);
+        ioremap(0x81000000, 0x4000);
 #endif /* CONFIG_8xx */
 }
 
@@ -1236,6 +1243,9 @@
 	unsigned long a, total;
 	unsigned long kstart, ksize;
 	int i;
+	
+	/* max amount of RAM we allow -- Cort */
+#define RAM_LIMIT (768<<20)	
 
 	memory_node = find_devices("memory");
 	if (memory_node == NULL) {
@@ -1260,7 +1270,18 @@
 	a = phys_mem.regions[0].address;
 	if (a != 0)
 		panic("RAM doesn't start at physical address 0");
+	/*
+	 * XXX:
+	 * Make sure ram mappings don't stomp on IO space
+	 * This is a temporary hack to keep this from happening
+	 * until we move the KERNELBASE and can allocate RAM up
+	 * to our nearest IO area.
+	 * -- Cort
+	 */
+	if ( phys_mem.regions[0].size >= RAM_LIMIT )
+		phys_mem.regions[0].size = RAM_LIMIT;
 	total = phys_mem.regions[0].size;
+	
 	if (phys_mem.n_regions > 1) {
 		printk("RAM starting at 0x%x is not contiguous\n",
 		       phys_mem.regions[1].address);
@@ -1277,8 +1298,15 @@
 	}
 	prom_mem = phys_mem;
 	for (i = 0; i < phys_avail.n_regions; ++i)
+	{
+		if ( phys_avail.regions[i].address >= RAM_LIMIT )
+			continue;
+		if ( (phys_avail.regions[i].address+phys_avail.regions[i].size)
+		     >= RAM_LIMIT )
+			phys_avail.regions[i].size = RAM_LIMIT - phys_avail.regions[i].address;
 		remove_mem_piece(&prom_mem, phys_avail.regions[i].address,
 				 phys_avail.regions[i].size, 1);
+	}
 
 	/*
 	 * phys_avail records memory we can use now.
@@ -1292,7 +1320,7 @@
 	remove_mem_piece(&prom_mem, kstart, ksize, 0);
 	remove_mem_piece(&phys_avail, 0, 0x4000, 0);
 	remove_mem_piece(&prom_mem, 0, 0x4000, 0);
-
+#undef RAM_LIMIT
 	return __va(total);
 }
 
diff -u --recursive --new-file v2.2.3/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig
--- v2.2.3/linux/arch/ppc/pmac_defconfig	Wed Mar 10 15:29:45 1999
+++ linux/arch/ppc/pmac_defconfig	Wed Mar 10 21:30:32 1999
@@ -262,6 +262,7 @@
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
 # CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 
 #
 # Amateur Radio support
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/boot/piggyback.c linux/arch/sparc/boot/piggyback.c
--- v2.2.3/linux/arch/sparc/boot/piggyback.c	Wed Jul 16 19:22:50 1997
+++ linux/arch/sparc/boot/piggyback.c	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/* $Id: piggyback.c,v 1.1 1997/07/11 11:05:17 jj Exp $
+/* $Id: piggyback.c,v 1.2 1998/12/15 12:24:43 jj Exp $
    Simple utility to make a single-image install kernel with initial ramdisk
    for Sparc tftpbooting without need to set up nfs.
    
@@ -46,16 +46,21 @@
 	struct stat s;
 	int image, tail;
 	
+	start = end = 0;
 	if (stat (argv[3], &s) < 0) die (argv[3]);
 	map = fopen (argv[2], "r");
 	if (!map) die(argv[2]);
 	while (fgets (buffer, 1024, map)) {
-		if (!strcmp (buffer + 11, "start\n"))
+		if (!strcmp (buffer + 8, " T start\n") || !strcmp (buffer + 16, " T start\n"))
 			start = strtoul (buffer, NULL, 16);
-		else if (!strcmp (buffer + 11, "end\n"))
+		else if (!strcmp (buffer + 8, " A end\n") || !strcmp (buffer + 16, " A end\n"))
 			end = strtoul (buffer, NULL, 16);
 	}
 	fclose (map);
+	if (!start || !end) {
+		fprintf (stderr, "Could not determine start and end from System.map\n");
+		exit(1);
+	}
 	if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]);
 	if (read(image,buffer,512) != 512) die(argv[1]);
 	if (!memcmp (buffer, "\177ELF", 4)) {
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.2.3/linux/arch/sparc/config.in	Tue Jan 19 11:32:51 1999
+++ linux/arch/sparc/config.in	Mon Mar 15 16:10:43 1999
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.63 1998/09/21 05:05:56 jj Exp $
+# $Id: config.in,v 1.68 1999/03/14 03:12:42 anton Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -25,7 +25,7 @@
 define_bool CONFIG_VT_CONSOLE y
 
 bool 'Support for AP1000 multicomputer' CONFIG_AP1000
-bool 'Symmetric multi-processing support' CONFIG_SMP
+bool 'Symmetric multi-processing support (does not work on sun4/sun4c)' CONFIG_SMP
 
 if [ "$CONFIG_AP1000" = "y" ]; then
 	define_bool CONFIG_NO_KEYBOARD y
@@ -165,6 +165,9 @@
 		fi
 		tristate 'Sun LANCE support' CONFIG_SUNLANCE
 		tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
+		if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+		  tristate 'Sun BigMAC 10/100baseT support' CONFIG_SUNBMAC
+		fi
 		tristate 'Sun QuadEthernet support' CONFIG_SUNQE
 		tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS
 #		bool 'FDDI driver support' CONFIG_FDDI
@@ -173,6 +176,15 @@
 	fi
 	endmenu
 fi
+
+# This one must be before the filesystem configs. -DaveM
+mainmenu_option next_comment
+comment 'Unix98 PTY support'
+bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
+if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
+	int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
+fi
+endmenu
 
 source fs/Config.in
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.2.3/linux/arch/sparc/defconfig	Wed Mar 10 15:29:45 1999
+++ linux/arch/sparc/defconfig	Sun Mar 21 07:23:38 1999
@@ -117,7 +117,6 @@
 # (it is safe to leave these untouched)
 #
 CONFIG_INET_RARP=m
-CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 CONFIG_IPV6=m
 # CONFIG_IPV6_EUI64 is not set
@@ -138,6 +137,10 @@
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -180,11 +183,13 @@
 # FC4 drivers
 #
 CONFIG_FC4_SOC=m
+CONFIG_FC4_SOCAL=m
 
 #
 # FC4 targets
 #
 CONFIG_SCSI_PLUTO=m
+CONFIG_SCSI_FCAL=m
 
 #
 # Network device support
@@ -202,28 +207,51 @@
 # CONFIG_SLIP_MODE_SLIP6 is not set
 CONFIG_SUNLANCE=y
 CONFIG_HAPPYMEAL=m
+CONFIG_SUNBMAC=m
 CONFIG_SUNQE=m
 CONFIG_MYRI_SBUS=m
 
 #
+# Unix98 PTY support
+#
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-CONFIG_MINIX_FS=m
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=m
-# CONFIG_JOLIET is not set
+CONFIG_AUTOFS_FS=m
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+# CONFIG_HFS_FS is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
 CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+# CONFIG_JOLIET is not set
+CONFIG_MINIX_FS=m
+# CONFIG_NTFS_FS is not set
+CONFIG_HPFS_FS=m
 CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=m
+CONFIG_EXT2_FS=y
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_UFS_FS_WRITE=y
+
+#
+# Network File Systems
+#
+CONFIG_CODA_FS=m
 CONFIG_NFS_FS=y
 CONFIG_NFSD=m
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-CONFIG_CODA_FS=m
 CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
@@ -233,21 +261,16 @@
 # CONFIG_NCPFS_NFS_NS is not set
 # CONFIG_NCPFS_OS2_NS is not set
 # CONFIG_NCPFS_MOUNT_SUBDIR is not set
-CONFIG_HPFS_FS=m
-# CONFIG_NTFS_FS is not set
-CONFIG_SYSV_FS=m
-CONFIG_AFFS_FS=m
-# CONFIG_HFS_FS is not set
-CONFIG_ROMFS_FS=m
-CONFIG_AUTOFS_FS=m
-CONFIG_AMIGA_PARTITION=y
-CONFIG_UFS_FS=m
+
+#
+# Partition Types
+#
 CONFIG_BSD_DISKLABEL=y
+# CONFIG_MAC_PARTITION is not set
 CONFIG_SMD_DISKLABEL=y
 CONFIG_SOLARIS_X86_PARTITION=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_MAC_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+CONFIG_AMIGA_PARTITION=y
 CONFIG_NLS=y
 
 #
@@ -278,6 +301,7 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
 #
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile
--- v2.2.3/linux/arch/sparc/kernel/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc/kernel/Makefile	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.48 1998/09/21 05:04:46 jj Exp $
+# $Id: Makefile,v 1.49 1999/01/02 16:45:37 davem Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c
--- v2.2.3/linux/arch/sparc/kernel/devices.c	Sun Nov  8 14:02:44 1998
+++ linux/arch/sparc/kernel/devices.c	Wed Mar 10 16:53:36 1999
@@ -29,24 +29,25 @@
 
 	prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
 
+	prom_printf("Booting Linux...\n");
 	if(strcmp(node_str, "cpu") == 0) {
 		linux_num_cpus++;
 	} else {
 		int scan;
 		scan = prom_getchild(prom_root_node);
-		prom_printf("root child is %08lx\n", (unsigned long) scan);
+		/* One can look it up in PROM instead */
+		/* prom_printf("root child is %08lx\n", (unsigned long) scan); */
 		while((scan = prom_getsibling(scan)) != 0) {
 			prom_getstring(scan, "device_type", node_str, sizeof(node_str));
 			if(strcmp(node_str, "cpu") == 0) {
 				linux_cpus[linux_num_cpus].prom_node = scan;
 				prom_getproperty(scan, "mid", (char *) &thismid, sizeof(thismid));
 				linux_cpus[linux_num_cpus].mid = thismid;
-				prom_printf("Found CPU %d <node=%08lx,mid=%d>\n",
-					    linux_num_cpus, (unsigned long) scan,
-					    thismid);
+				/* prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", linux_num_cpus, (unsigned long) scan, thismid); */
+				printk("Found CPU %d <node=%08lx,mid=%d>\n", linux_num_cpus, (unsigned long) scan, thismid);
 				linux_num_cpus++;
 			}
-		};
+		}
 		if(linux_num_cpus == 0) {
 			if (sparc_cpu_model == sun4d) {
 				scan = prom_getchild(prom_root_node);
@@ -59,9 +60,10 @@
 						prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid));
 						linux_cpus[linux_num_cpus].prom_node = node;
 						linux_cpus[linux_num_cpus].mid = thismid;
-						prom_printf("Found CPU %d <node=%08lx,mid=%d>\n",
-							    linux_num_cpus, (unsigned long) node,
-							    thismid);
+						/* prom_printf("Found CPU %d <node=%08lx,mid=%d>\n", 
+							       linux_num_cpus, (unsigned long) node, thismid); */
+						printk("Found CPU %d <node=%08lx,mid=%d>\n", 
+						       linux_num_cpus, (unsigned long) node, thismid);
 						linux_num_cpus++;
 					}
 				}
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- v2.2.3/linux/arch/sparc/kernel/entry.S	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc/kernel/entry.S	Wed Mar 10 16:53:36 1999
@@ -1,10 +1,10 @@
-/* $Id: entry.S,v 1.153 1998/11/11 15:12:33 jj Exp $
+/* $Id: entry.S,v 1.157 1999/01/19 07:54:32 davem Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1996,1998 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996-1999 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
  */
 
@@ -1380,11 +1380,13 @@
 
 	/* Now that we have a real sys_clone, sys_fork() is
 	 * implemented in terms of it.  Our _real_ implementation
-	 * of SunOS vfork() will use sys_clone() instead.
+	 * of SunOS vfork() will use sys_vfork().
+	 *
+	 * XXX These three should be consolidated into mostly shared
+	 * XXX code just like on sparc64... -DaveM
 	 */
 	.align	4
-	.globl	C_LABEL(sys_fork), C_LABEL(sys_vfork), flush_patch_two
-C_LABEL(sys_vfork):
+	.globl	C_LABEL(sys_fork), flush_patch_two
 C_LABEL(sys_fork):
 	mov	%o7, %l5
 flush_patch_two:
@@ -1422,6 +1424,23 @@
 	call	C_LABEL(do_fork)
 	 mov	%l5, %o7
 
+	/* Whee, real vfork! */
+	.globl	C_LABEL(sys_vfork), flush_patch_four
+C_LABEL(sys_vfork):
+flush_patch_four:
+	FLUSH_ALL_KERNEL_WINDOWS;
+	rd	%psr, %g4
+	WRITE_PAUSE
+	rd	%wim, %g5
+	WRITE_PAUSE
+	std	%g4, [%curptr + AOFF_task_tss + AOFF_thread_fork_kpsr]
+	sethi	%hi(0x4000 | 0x0100 | SIGCHLD), %o0
+	mov	%fp, %o1
+	or	%o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0
+	sethi	%hi(C_LABEL(do_fork)), %l1
+	jmpl	%l1 + %lo(C_LABEL(do_fork)), %g0
+	 add	%sp, REGWIN_SZ, %o2
+
         .align  4
 linux_sparc_ni_syscall:
 	sethi   %hi(C_LABEL(sys_ni_syscall)), %l7
@@ -1454,11 +1473,10 @@
 #ifdef __SMP__
 	.globl	C_LABEL(ret_from_smpfork)
 C_LABEL(ret_from_smpfork):
-	/* Nowadays all we need to do is drop the scheduler lock. */
-	sethi	%hi(C_LABEL(scheduler_lock)), %o4
-	stb	%g0, [%o4 + %lo(C_LABEL(scheduler_lock))]
 	wr	%l0, PSR_ET, %psr
 	WRITE_PAUSE
+	call	schedule_tail
+	 nop
 	b	C_LABEL(ret_sys_call)
 	 ld	[%sp + REGWIN_SZ + PT_I0], %o0
 #endif
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.2.3/linux/arch/sparc/kernel/head.S	Thu Aug  6 14:06:30 1998
+++ linux/arch/sparc/kernel/head.S	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.92 1998/06/10 07:21:55 davem Exp $
+/* $Id: head.S,v 1.93 1999/01/10 06:03:14 jj Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1108,6 +1108,9 @@
 		st	%g4, [%g5 + 0x18]
 		st	%g4, [%g5 + 0x1c]
 		set	flush_patch_three, %g5
+		st	%g4, [%g5 + 0x18]
+		st	%g4, [%g5 + 0x1c]
+		set	flush_patch_four, %g5
 		st	%g4, [%g5 + 0x18]
 		st	%g4, [%g5 + 0x1c]
 		set	flush_patch_exception, %g5
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/pcic.c linux/arch/sparc/kernel/pcic.c
--- v2.2.3/linux/arch/sparc/kernel/pcic.c	Sun Nov  8 14:02:45 1998
+++ linux/arch/sparc/kernel/pcic.c	Tue Mar 16 21:52:05 1999
@@ -1,4 +1,4 @@
-/* $Id: pcic.c,v 1.3 1998/10/07 11:34:56 jj Exp $
+/* $Id: pcic.c,v 1.5 1999/03/16 00:15:20 davem Exp $
  * pcic.c: Sparc/PCI controller support
  *
  * Copyright (C) 1998 V. Roganov and G. Raiko
@@ -498,9 +498,10 @@
 		tv->tv_sec--;
 	}
 	xtime = *tv;
-	time_state = TIME_BAD;
-	time_maxerror = 0x70000000;
-	time_esterror = 0x70000000;
+	time_adjust = 0;		/* stop active adjtime() */
+	time_status |= STA_UNSYNC;
+	time_maxerror = NTP_PHASE_LIMIT;
+	time_esterror = NTP_PHASE_LIMIT;
 	sti();
 }
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.2.3/linux/arch/sparc/kernel/process.c	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/kernel/process.c	Sun Mar 21 18:37:56 1999
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.126 1998/09/21 05:05:18 jj Exp $
+/*  $Id: process.c,v 1.132 1999/03/22 02:12:13 davem Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -62,7 +62,7 @@
 
 	/* endless idle loop with no priority at all */
 	current->priority = 0;
-	current->counter = 0;
+	current->counter = -100;
 	for (;;) {
 		if (ARCH_SUN4C_SUN4) {
 			static int count = HZ;
@@ -108,13 +108,15 @@
 /* This is being executed in task 0 'user space'. */
 int cpu_idle(void *unused)
 {
+	/* endless idle loop with no priority at all */
 	current->priority = 0;
+	current->counter = -100;
 	while(1) {
-		check_pgt_cache();
- 		run_task_queue(&tq_scheduler);
- 		/* endless idle loop with no priority at all */
-		current->counter = 0;
-		schedule();
+		if(current->need_resched) {
+			schedule();
+			check_pgt_cache();
+		}
+		barrier(); /* or else gcc optimizes... */
 	}
 }
 
@@ -440,10 +442,17 @@
 	size = ((unsigned long)src->fp) - ((unsigned long)src);
 	sp = (struct sparc_stackf *)(((unsigned long)dst) - size); 
 
+	/* do_fork() grabs the parent semaphore, we must release it
+	 * temporarily so we can build the child clone stack frame
+	 * without deadlocking.
+	 */
+	up(&current->mm->mmap_sem);
 	if (copy_to_user(sp, src, size))
-		return 0;
-	if (put_user(dst, &sp->fp))
-		return 0;
+		sp = (struct sparc_stackf *) 0;
+	else if (put_user(dst, &sp->fp))
+		sp = (struct sparc_stackf *) 0;
+	down(&current->mm->mmap_sem);
+
 	return sp;
 }
 
@@ -657,4 +666,38 @@
 out:
 	unlock_kernel();
 	return error;
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+	long retval;
+
+	__asm__ __volatile("mov %4, %%g2\n\t"    /* Set aside fn ptr... */
+			   "mov %5, %%g3\n\t"    /* and arg. */
+			   "mov %1, %%g1\n\t"
+			   "mov %2, %%o0\n\t"    /* Clone flags. */
+			   "mov 0, %%o1\n\t"     /* usp arg == 0 */
+			   "t 0x10\n\t"          /* Linux/Sparc clone(). */
+			   "cmp %%o1, 0\n\t"
+			   "be 1f\n\t"           /* The parent, just return. */
+			   " nop\n\t"            /* Delay slot. */
+			   "jmpl %%g2, %%o7\n\t" /* Call the function. */
+			   " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
+			   "mov %3, %%g1\n\t"
+			   "t 0x10\n\t"          /* Linux/Sparc exit(). */
+			   /* Notreached by child. */
+			   "1: mov %%o0, %0\n\t" :
+			   "=r" (retval) :
+			   "i" (__NR_clone), "r" (flags | CLONE_VM),
+			   "i" (__NR_exit),  "r" (fn), "r" (arg) :
+			   "g1", "g2", "g3", "o0", "o1", "memory", "cc");
+	return retval;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.2.3/linux/arch/sparc/kernel/ptrace.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/sparc/kernel/ptrace.c	Mon Mar 15 16:10:43 1999
@@ -528,6 +528,8 @@
 
 	if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH))
 	    || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) {
+		unsigned long flags;
+
 		if(child == current) {
 			/* Try this under SunOS/Solaris, bwa haha
 			 * You'll never be able to kill the process. ;-)
@@ -539,8 +541,9 @@
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
 		    (current->gid != child->egid) ||
-		    (current->gid != child->gid)) && 
-		   !capable(CAP_SYS_PTRACE)) {
+		    (current->gid != child->sgid) || 
+	 	    (cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+		    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) {
 			pt_error_return(regs, EPERM);
 			goto out;
 		}
@@ -550,14 +553,13 @@
 			goto out;
 		}
 		child->flags |= PF_PTRACED;
+		write_lock_irqsave(&tasklist_lock, flags);
 		if(child->p_pptr != current) {
-			unsigned long flags;
-			write_lock_irqsave(&tasklist_lock, flags);
 			REMOVE_LINKS(child);
 			child->p_pptr = current;
 			SET_LINKS(child);
-			write_unlock_irqrestore(&tasklist_lock, flags);
 		}
+		write_unlock_irqrestore(&tasklist_lock, flags);
 		send_sig(SIGSTOP, child, 1);
 		pt_succ_return(regs, 0);
 		goto out;
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- v2.2.3/linux/arch/sparc/kernel/signal.c	Sun Nov  8 14:02:45 1998
+++ linux/arch/sparc/kernel/signal.c	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.90 1998/10/18 03:31:05 davem Exp $
+/*  $Id: signal.c,v 1.91 1999/01/26 11:00:44 jj Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -38,6 +38,8 @@
 
 /* This turned off for production... */
 /* #define DEBUG_SIGNALS 1 */
+/* #define DEBUG_SIGNALS_TRACE 1 */
+/* #define DEBUG_SIGNALS_MAPS 1 */
 
 /* Signal frames: the original one (compatible with SunOS):
  *
@@ -1004,6 +1006,59 @@
 	}
 }
 
+#ifdef DEBUG_SIGNALS_MAPS
+
+#define MAPS_LINE_FORMAT	  "%08lx-%08lx %s %08lx %s %lu "
+
+static inline void read_maps (void)
+{
+	struct vm_area_struct * map, * next;
+	char * buffer;
+	ssize_t i;
+
+	buffer = (char*)__get_free_page(GFP_KERNEL);
+	if (!buffer)
+		return;
+
+	for (map = current->mm->mmap ; map ; map = next ) {
+		/* produce the next line */
+		char *line;
+		char str[5], *cp = str;
+		int flags;
+		kdev_t dev;
+		unsigned long ino;
+
+		/*
+		 * Get the next vma now (but it won't be used if we sleep).
+		 */
+		next = map->vm_next;
+		flags = map->vm_flags;
+
+		*cp++ = flags & VM_READ ? 'r' : '-';
+		*cp++ = flags & VM_WRITE ? 'w' : '-';
+		*cp++ = flags & VM_EXEC ? 'x' : '-';
+		*cp++ = flags & VM_MAYSHARE ? 's' : 'p';
+		*cp++ = 0;
+
+		dev = 0;
+		ino = 0;
+		if (map->vm_file != NULL) {
+			dev = map->vm_file->f_dentry->d_inode->i_dev;
+			ino = map->vm_file->f_dentry->d_inode->i_ino;
+			line = d_path(map->vm_file->f_dentry, buffer, PAGE_SIZE);
+		}
+		printk(MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_offset,
+			      kdevname(dev), ino);
+		if (map->vm_file != NULL)
+			printk("%s\n", line);
+		else
+			printk("\n");
+	}
+	free_page((unsigned long)buffer);
+	return;
+}
+#endif
+
 /* Note that 'init' is a special process: it doesn't get signals it doesn't
  * want to handle. Thus you cannot kill init even with a SIGKILL even by
  * mistake.
@@ -1115,8 +1170,25 @@
 				}
 #ifdef DEBUG_SIGNALS
 				/* Very useful to debug dynamic linker problems */
-				printk ("Sig ILL going...\n");
+				printk ("Sig %ld going for %s[%d]...\n", signr, current->comm, current->pid);
 				show_regs (regs);
+#ifdef DEBUG_SIGNALS_TRACE
+				{
+					struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP];
+					unsigned int ins[8];
+
+					while(rw &&
+					      !(((unsigned long) rw) & 0x3)) {
+						copy_from_user(ins, &rw->ins[0], sizeof(ins));
+						printk("Caller[%08x](%08x,%08x,%08x,%08x,%08x,%08x)\n", ins[7], ins[0], ins[1], ins[2], ins[3], ins[4], ins[5]);
+						rw = (struct reg_window *)(unsigned long)ins[6];
+					}
+				}
+#endif
+#ifdef DEBUG_SIGNALS_MAPS
+				printk("Maps:\n");
+				read_maps();
+#endif
 #endif
 				/* fall through */
 			default:
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
--- v2.2.3/linux/arch/sparc/kernel/smp.c	Sun Nov  8 14:02:45 1998
+++ linux/arch/sparc/kernel/smp.c	Wed Mar 10 16:53:36 1999
@@ -52,6 +52,7 @@
 int smp_activated = 0;
 volatile int cpu_number_map[NR_CPUS];
 volatile int __cpu_logical_map[NR_CPUS];
+cycles_t cacheflush_time = 0; /* XXX */
 
 /* The only guaranteed locking primitive available on all Sparc
  * processors is 'ldstub [%reg + immediate], %dest_reg' which atomically
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.2.3/linux/arch/sparc/kernel/sparc_ksyms.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.73 1998/11/06 13:49:54 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.77 1999/03/21 06:37:43 davem Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -66,6 +66,7 @@
 
 extern void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
+extern int __lshrdi3(int, int);
 
 extern void dump_thread(struct pt_regs *, struct user *);
 
@@ -91,6 +92,7 @@
 /* used by various drivers */
 EXPORT_SYMBOL(sparc_cpu_model);
 EXPORT_SYMBOL_PRIVATE(_spinlock_waitfor);
+EXPORT_SYMBOL(kernel_thread);
 #ifdef SPIN_LOCK_DEBUG
 EXPORT_SYMBOL(_do_spin_lock);
 EXPORT_SYMBOL(_do_spin_unlock);
@@ -118,6 +120,7 @@
 #endif
 
 EXPORT_SYMBOL(page_offset);
+EXPORT_SYMBOL(sparc_valid_addr_bitmap);
 
 #ifndef CONFIG_SUN4
 EXPORT_SYMBOL(stack_top);
@@ -211,6 +214,7 @@
 EXPORT_SYMBOL(prom_apply_obio_ranges);
 EXPORT_SYMBOL(prom_getname);
 EXPORT_SYMBOL(prom_feval);
+EXPORT_SYMBOL(prom_getbool);
 EXPORT_SYMBOL(prom_getstring);
 EXPORT_SYMBOL(prom_apply_sbus_ranges);
 EXPORT_SYMBOL(prom_getint);
@@ -268,6 +272,7 @@
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
 EXPORT_SYMBOL_NOVERS(__ashrdi3);
+EXPORT_SYMBOL_NOVERS(__lshrdi3);
 
 EXPORT_SYMBOL_DOT(rem);
 EXPORT_SYMBOL_DOT(urem);
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c
--- v2.2.3/linux/arch/sparc/kernel/sys_sparc.c	Sun Nov  8 14:02:45 1998
+++ linux/arch/sparc/kernel/sys_sparc.c	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.49 1998/10/11 06:57:53 davem Exp $
+/* $Id: sys_sparc.c,v 1.51 1999/03/20 22:02:00 davem Exp $
  * linux/arch/sparc/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -25,6 +25,8 @@
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
 
+/* #define DEBUG_UNIMP_SYSCALL */
+
 /* XXX Make this per-binary type, this way we can detect the type of
  * XXX a binary.  Every Sparc executable calls this very early on.
  */
@@ -189,6 +191,7 @@
 			goto out;
 	}
 	retval = -ENOMEM;
+	len = PAGE_ALIGN(len);
 	if(!(flags & MAP_FIXED) && !addr) {
 		addr = get_unmapped_area(addr, len);
 		if(!addr)
@@ -202,6 +205,7 @@
 
 	if(ARCH_SUN4C_SUN4) {
 		if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
+			/* VM hole */
 			retval = current->mm->brk;
 			goto out_putf;
 		}
@@ -223,9 +227,14 @@
 asmlinkage unsigned long
 c_sys_nis_syscall (struct pt_regs *regs)
 {
+	static int count = 0;
+	
+	if (count++ > 5) return -ENOSYS;
 	lock_kernel();
 	printk ("Unimplemented SPARC system call %d\n",(int)regs->u_regs[1]);
+#ifdef DEBUG_UNIMP_SYSCALL	
 	show_regs (regs);
+#endif
 	unlock_kernel();
 	return -ENOSYS;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.2.3/linux/arch/sparc/kernel/systbls.S	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/kernel/systbls.S	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.80 1998/09/21 05:04:59 jj Exp $
+/* $Id: systbls.S,v 1.82 1999/03/20 22:01:59 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/time.c linux/arch/sparc/kernel/time.c
--- v2.2.3/linux/arch/sparc/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/sparc/kernel/time.c	Mon Mar 15 16:10:43 1999
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.39 1998/09/29 09:46:15 davem Exp $
+/* $Id: time.c,v 1.43 1999/03/15 22:13:31 davem Exp $
  * linux/arch/sparc/kernel/time.c
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -39,6 +39,8 @@
 #include <asm/sun4paddr.h>
 #include <asm/page.h>
 
+extern rwlock_t xtime_lock;
+
 enum sparc_clock_type sp_clock_typ;
 struct mostek48t02 *mstk48t02_regs = 0;
 struct mostek48t08 *mstk48t08_regs = 0;
@@ -80,7 +82,7 @@
 
 #ifdef CONFIG_SUN4
 	if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) ||
-   (idprom->id_machtype == (SM_SUN4 | SM_4_110))) {
+	   (idprom->id_machtype == (SM_SUN4 | SM_4_110))) {
 		int temp;
         	intersil_read_intr(intersil_clock, temp);
 		/* re-enable the irq */
@@ -89,6 +91,8 @@
 #endif
 	clear_clock_irq();
 
+	write_lock(&xtime_lock);
+
 	do_timer(regs);
 
 	/* Determine when to update the Mostek clock. */
@@ -101,6 +105,7 @@
 	  else
 	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	}
+	write_unlock(&xtime_lock);
 }
 
 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
@@ -436,6 +441,9 @@
 	return offset + count;
 }
 
+/* This need not obtain the xtime_lock as it is coded in
+ * an implicitly SMP safe way already.
+ */
 void do_gettimeofday(struct timeval *tv)
 {
 #if CONFIG_AP1000
@@ -485,12 +493,13 @@
 
 void do_settimeofday(struct timeval *tv)
 {
+	write_lock_irq(&xtime_lock);
 	bus_do_settimeofday(tv);
+	write_unlock_irq(&xtime_lock);
 }
 
 static void sbus_do_settimeofday(struct timeval *tv)
 {
-	cli();
 #if !CONFIG_AP1000
 	tv->tv_usec -= do_gettimeoffset();
 	if(tv->tv_usec < 0) {
@@ -501,10 +510,8 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti();
 }
 
 /*
@@ -544,7 +551,7 @@
 			} else {
 				printk(KERN_WARNING
 			       "set_rtc_mmss: can't update from %d to %d\n",
-				       cmos_minutes, real_minutes);
+				       mostek_minutes, real_minutes);
 				return -1;
 			}
 			
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/kernel/traps.c linux/arch/sparc/kernel/traps.c
--- v2.2.3/linux/arch/sparc/kernel/traps.c	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/kernel/traps.c	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.57 1998/09/17 11:04:51 jj Exp $
+/* $Id: traps.c,v 1.59 1999/03/06 12:07:31 anton Exp $
  * arch/sparc/kernel/traps.c
  *
  * Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -82,8 +82,13 @@
 	printk("\n");
 }
 
+#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
+#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
+
 void die_if_kernel(char *str, struct pt_regs *regs)
 {
+	int count = 0;
+
 	/* Amuse the user. */
 	printk(
 "              \\|/ ____ \\|/\n"
@@ -93,6 +98,27 @@
 
 	printk("%s(%d): %s\n", current->comm, current->pid, str);
 	show_regs(regs);
+
+	__SAVE; __SAVE; __SAVE; __SAVE;
+	__SAVE; __SAVE; __SAVE; __SAVE;
+	__RESTORE; __RESTORE; __RESTORE; __RESTORE;
+	__RESTORE; __RESTORE; __RESTORE; __RESTORE;
+
+	{
+		struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP];
+
+		/* Stop the back trace when we hit userland or we
+		 * find some badly aligned kernel stack. Set an upper
+		 * bound in case our stack is trashed and we loop.
+		 */
+		while(rw					&&
+		      count++ < 30				&&
+                      (((unsigned long) rw) >= PAGE_OFFSET)	&&
+		      !(((unsigned long) rw) & 0x7)) {
+			printk("Caller[%08lx]\n", rw->ins[7]);
+			rw = (struct reg_window *)rw->ins[6];
+		}
+	}
 	printk("Instruction DUMP:");
 	instruction_dump ((unsigned long *) regs->pc);
 	if(regs->psr & PSR_PS)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/Makefile linux/arch/sparc/lib/Makefile
--- v2.2.3/linux/arch/sparc/lib/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc/lib/Makefile	Sun Mar 21 07:23:38 1999
@@ -1,11 +1,11 @@
-# $Id: Makefile,v 1.26 1998/07/26 03:02:43 davem Exp $
+# $Id: Makefile,v 1.28 1999/03/21 06:37:44 davem Exp $
 # Makefile for Sparc library files..
 #
 
 OBJS  = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
         strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
 	strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
-	copy_user.o locks.o atomic.o bitops.o debuglocks.o
+	copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o
 
 ifdef CONFIG_SMP
 OBJS += irqlock.o
@@ -88,6 +88,9 @@
 
 ashrdi3.o: ashrdi3.S
 	$(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S
+
+lshrdi3.o: lshrdi3.S
+	$(CC) -D__ASSEMBLY__ -c -o lshrdi3.o lshrdi3.S
 
 dep:
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/atomic.S linux/arch/sparc/lib/atomic.S
--- v2.2.3/linux/arch/sparc/lib/atomic.S	Thu Apr 23 20:21:30 1998
+++ linux/arch/sparc/lib/atomic.S	Wed Mar 10 16:53:36 1999
@@ -51,12 +51,16 @@
 1:	ldstub	[%g1 + 3], %g7		! Spin on the byte lock for SMP.
 	orcc	%g7, 0x0, %g0		! Did we get it?
 	bne	1b			! Nope...
-#endif
 	 ld	[%g1], %g7		! Load locked atomic_t
 	sra	%g7, 8, %g7		! Get signed 24-bit integer
 	add	%g7, %g2, %g2		! Add in argument
 	sll	%g2, 8, %g7		! Transpose back to atomic_t
 	st	%g7, [%g1]		! Clever: This releases the lock as well.
+#else
+	ld	[%g1], %g7		! Load locked atomic_t
+	add	%g7, %g2, %g2		! Add in argument
+	st	%g2, [%g1]		! Store it back
+#endif
 	wr	%g3, 0x0, %psr		! Restore original PSR_PIL
 	nop; nop; nop;			! Let the bits set
 	jmpl	%o7, %g0		! NOTE: not + 8, see callers in atomic.h
@@ -72,12 +76,16 @@
 1:	ldstub	[%g1 + 3], %g7		! Spin on the byte lock for SMP.
 	orcc	%g7, 0x0, %g0		! Did we get it?
 	bne	1b			! Nope...
-#endif
 	 ld	[%g1], %g7		! Load locked atomic_t
 	sra	%g7, 8, %g7		! Get signed 24-bit integer
 	sub	%g7, %g2, %g2		! Subtract argument
 	sll	%g2, 8, %g7		! Transpose back to atomic_t
 	st	%g7, [%g1]		! Clever: This releases the lock as well
+#else
+	ld	[%g1], %g7		! Load locked atomic_t
+	sub	%g7, %g2, %g2		! Subtract argument
+	st	%g2, [%g1]		! Store it back
+#endif
 	wr	%g3, 0x0, %psr		! Restore original PSR_PIL
 	nop; nop; nop;			! Let the bits set
 	jmpl	%o7, %g0		! NOTE: not + 8, see callers in atomic.h
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/debuglocks.c linux/arch/sparc/lib/debuglocks.c
--- v2.2.3/linux/arch/sparc/lib/debuglocks.c	Sun Nov  8 14:02:45 1998
+++ linux/arch/sparc/lib/debuglocks.c	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/* $Id: debuglocks.c,v 1.5 1998/10/14 09:19:04 jj Exp $
+/* $Id: debuglocks.c,v 1.6 1999/02/23 13:23:55 jj Exp $
  * debuglocks.c: Debugging versions of SMP locking primitives.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -28,9 +28,11 @@
 static inline void show(char *str, spinlock_t *lock, unsigned long caller)
 {
 	int cpu = smp_processor_id();
+	extern spinlock_t console_lock;
 
-	printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str,
-		lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
+	if (lock != &console_lock)
+		printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str,
+			lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3);
 }
 
 static inline void show_read(char *str, rwlock_t *lock, unsigned long caller)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/lib/lshrdi3.S linux/arch/sparc/lib/lshrdi3.S
--- v2.2.3/linux/arch/sparc/lib/lshrdi3.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/lib/lshrdi3.S	Sun Mar 21 07:23:38 1999
@@ -0,0 +1,29 @@
+/* $Id: lshrdi3.S,v 1.1 1999/03/21 06:37:45 davem Exp $ */
+
+#include <asm/cprefix.h>
+
+	.globl	C_LABEL(__lshrdi3)
+C_LABEL(__lshrdi3):
+	cmp	%o2, 0
+	be	3f
+	 mov	0x20, %g2
+
+	sub	%g2, %o2, %g2
+	cmp	%g2, 0
+	bg	1f
+	 srl	%o0, %o2, %o4
+
+	clr	%o4
+	neg	%g2
+	b	2f
+	 srl	%o0, %g2, %o5
+1:
+	sll  %o0, %g2, %g3
+	srl  %o1, %o2, %g2
+	or  %g2, %g3, %o5
+2:
+	mov  %o4, %o0
+	mov  %o5, %o1
+3:
+	retl 
+	 nop 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fabss.c linux/arch/sparc/math-emu/fabss.c
--- v2.2.3/linux/arch/sparc/math-emu/fabss.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fabss.c	Wed Mar 10 16:53:36 1999
@@ -2,5 +2,5 @@
 {
 	/* Clear the sign bit (high bit of word 0) */
 	rd[0] = rs2[0] & 0x7fffffffUL;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpd.c linux/arch/sparc/math-emu/fcmpd.c
--- v2.2.3/linux/arch/sparc/math-emu/fcmpd.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fcmpd.c	Wed Mar 10 16:53:36 1999
@@ -14,5 +14,5 @@
 		ret = 2;
 
 	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmped.c linux/arch/sparc/math-emu/fcmped.c
--- v2.2.3/linux/arch/sparc/math-emu/fcmped.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fcmped.c	Wed Mar 10 16:53:36 1999
@@ -14,5 +14,5 @@
 		ret = 2;
 
 	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpeq.c linux/arch/sparc/math-emu/fcmpeq.c
--- v2.2.3/linux/arch/sparc/math-emu/fcmpeq.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fcmpeq.c	Wed Mar 10 16:53:36 1999
@@ -14,5 +14,5 @@
 	fsr = *(unsigned long *)rd;
 	fsr &= ~0xc00; fsr |= (ret << 10);
 	*(unsigned long *)rd = fsr;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpes.c linux/arch/sparc/math-emu/fcmpes.c
--- v2.2.3/linux/arch/sparc/math-emu/fcmpes.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fcmpes.c	Wed Mar 10 16:53:36 1999
@@ -14,5 +14,5 @@
 		ret = 2;
 
 	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmpq.c linux/arch/sparc/math-emu/fcmpq.c
--- v2.2.3/linux/arch/sparc/math-emu/fcmpq.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fcmpq.c	Wed Mar 10 16:53:36 1999
@@ -14,5 +14,5 @@
 	fsr = *(unsigned long *)rd;
 	fsr &= ~0xc00; fsr |= (ret << 10);
 	*(unsigned long *)rd = fsr;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fcmps.c linux/arch/sparc/math-emu/fcmps.c
--- v2.2.3/linux/arch/sparc/math-emu/fcmps.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fcmps.c	Wed Mar 10 16:53:36 1999
@@ -14,5 +14,5 @@
 		ret = 2;
 
 	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fdmulq.c linux/arch/sparc/math-emu/fdmulq.c
--- v2.2.3/linux/arch/sparc/math-emu/fdmulq.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fdmulq.c	Wed Mar 10 16:53:36 1999
@@ -11,6 +11,5 @@
 	__FP_UNPACK_D(IN, rs2);
 	FP_CONV(Q,D,4,2,B,IN);
 	FP_MUL_Q(R, A, B);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fdtoq.c linux/arch/sparc/math-emu/fdtoq.c
--- v2.2.3/linux/arch/sparc/math-emu/fdtoq.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fdtoq.c	Wed Mar 10 16:53:36 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_D(A, rs2);
 	FP_CONV(Q,D,4,2,R,A);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fdtos.c linux/arch/sparc/math-emu/fdtos.c
--- v2.2.3/linux/arch/sparc/math-emu/fdtos.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fdtos.c	Wed Mar 10 16:53:36 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_D(A, rs2);
 	FP_CONV(S,D,1,2,R,A);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fnegs.c linux/arch/sparc/math-emu/fnegs.c
--- v2.2.3/linux/arch/sparc/math-emu/fnegs.c	Fri May  8 23:14:45 1998
+++ linux/arch/sparc/math-emu/fnegs.c	Wed Mar 10 16:53:36 1999
@@ -2,5 +2,5 @@
 {
  	/* just change the sign bit */
 	rd[0] = rs2[0] ^ 0x80000000UL;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fqtod.c linux/arch/sparc/math-emu/fqtod.c
--- v2.2.3/linux/arch/sparc/math-emu/fqtod.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fqtod.c	Wed Mar 10 16:53:36 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_Q(A, rs2);
 	FP_CONV(D,Q,2,4,R,A);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fqtos.c linux/arch/sparc/math-emu/fqtos.c
--- v2.2.3/linux/arch/sparc/math-emu/fqtos.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fqtos.c	Wed Mar 10 16:53:36 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_Q(A, rs2);
 	FP_CONV(S,Q,1,4,R,A);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fsmuld.c linux/arch/sparc/math-emu/fsmuld.c
--- v2.2.3/linux/arch/sparc/math-emu/fsmuld.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fsmuld.c	Wed Mar 10 16:53:36 1999
@@ -11,6 +11,5 @@
 	__FP_UNPACK_S(IN, rs2);
 	FP_CONV(D,S,2,1,B,IN);
 	FP_MUL_D(R, A, B);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fstod.c linux/arch/sparc/math-emu/fstod.c
--- v2.2.3/linux/arch/sparc/math-emu/fstod.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fstod.c	Wed Mar 10 16:53:36 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_S(A, rs2);
 	FP_CONV(D,S,2,1,R,A);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/fstoq.c linux/arch/sparc/math-emu/fstoq.c
--- v2.2.3/linux/arch/sparc/math-emu/fstoq.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/fstoq.c	Wed Mar 10 16:53:36 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_S(A, rs2);
 	FP_CONV(Q,S,4,1,R,A);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/math.c linux/arch/sparc/math-emu/math.c
--- v2.2.3/linux/arch/sparc/math-emu/math.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/math.c	Wed Mar 10 16:53:36 1999
@@ -124,6 +124,7 @@
 #include <linux/mm.h>
 #include <asm/uaccess.h>
 
+#include "soft-fp.h"
 
 #define FLOATFUNC(x) extern int x(void *,void *,void *)
 
@@ -189,6 +190,13 @@
 FLOATFUNC(FITOS);                                 /* v6 */
 FLOATFUNC(FITOD);                                 /* v6 */
 
+#define FSR_TEM_SHIFT	23UL
+#define FSR_TEM_MASK	(0x1fUL << FSR_TEM_SHIFT)
+#define FSR_AEXC_SHIFT	5UL
+#define FSR_AEXC_MASK	(0x1fUL << FSR_AEXC_SHIFT)
+#define FSR_CEXC_SHIFT	0UL
+#define FSR_CEXC_MASK	(0x1fUL << FSR_CEXC_SHIFT)
+
 static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);   
 
 /* Unlike the Sparc64 version (which has a struct fpustate), we
@@ -254,12 +262,85 @@
          break;
    }
    /* Now empty the queue and clear the queue_not_empty flag */
-   fpt->tss.fsr &= ~0x3000;
+   if(retcode)
+	   fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK);
+   else
+	   fpt->tss.fsr &= ~0x3000;
    fpt->tss.fpqdepth = 0;
    
    return retcode;
 }
 
+/* All routines returning an exception to raise should detect
+ * such exceptions _before_ rounding to be consistant with
+ * the behavior of the hardware in the implemented cases
+ * (and thus with the recommendations in the V9 architecture
+ * manual).
+ *
+ * We return 0 if a SIGFPE should be sent, 1 otherwise.
+ */
+static int record_exception(unsigned long *pfsr, int eflag)
+{
+	unsigned long fsr = *pfsr;
+	int would_trap;
+
+	/* Determine if this exception would have generated a trap. */
+	would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
+
+	/* If trapping, we only want to signal one bit. */
+	if(would_trap != 0) {
+		eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
+		if((eflag & (eflag - 1)) != 0) {
+			if(eflag & EFLAG_INVALID)
+				eflag = EFLAG_INVALID;
+			else if(eflag & EFLAG_DIVZERO)
+				eflag = EFLAG_DIVZERO;
+			else if(eflag & EFLAG_INEXACT)
+				eflag = EFLAG_INEXACT;
+		}
+	}
+
+	/* Set CEXC, here are the rules:
+	 *
+	 * 1) In general all FPU ops will set one and only one
+	 *    bit in the CEXC field, this is always the case
+	 *    when the IEEE exception trap is enabled in TEM.
+	 *
+	 * 2) As a special case, if an overflow or underflow
+	 *    is being signalled, AND the trap is not enabled
+	 *    in TEM, then the inexact field shall also be set.
+	 */
+	fsr &= ~(FSR_CEXC_MASK);
+	if(would_trap ||
+	   (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) {
+		fsr |= ((long)eflag << FSR_CEXC_SHIFT);
+	} else {
+		fsr |= (((long)eflag << FSR_CEXC_SHIFT) |
+			(EFLAG_INEXACT << FSR_CEXC_SHIFT));
+	}
+
+	/* Set the AEXC field, rules are:
+	 *
+	 * 1) If a trap would not be generated, the
+	 *    CEXC just generated is OR'd into the
+	 *    existing value of AEXC.
+	 *
+	 * 2) When a trap is generated, AEXC is cleared.
+	 */
+	if(would_trap == 0)
+		fsr |= ((long)eflag << FSR_AEXC_SHIFT);
+	else
+		fsr &= ~(FSR_AEXC_MASK);
+
+	/* If trapping, indicate fault trap type IEEE. */
+	if(would_trap != 0)
+		fsr |= (1UL << 14);
+
+	*pfsr = fsr;
+
+	return (would_trap ? 0 : 1);
+}
+
 static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs)
 {
    /* Emulate the given insn, updating fsr and fregs appropriately. */
@@ -270,7 +351,7 @@
     * (this field not used on sparc32 code, as we can't 
     * extract trap type info for ops on the FP queue) 
     */
-   int freg;
+   int freg, eflag;
    int (*func)(void *,void *,void *) = NULL;
    void *rs1 = NULL, *rs2 = NULL, *rd = NULL;   
 
@@ -411,6 +492,8 @@
 #ifdef DEBUG_MATHEMU   
    printk("executing insn...\n");
 #endif   
-   func(rd, rs2, rs1);                            /* do the Right Thing */
-   return 1;                                      /* success! */
+   eflag = func(rd, rs2, rs1);                   /* do the Right Thing */
+   if(eflag == 0)
+	   return 1;                             /* success! */
+   return record_exception(fsr, eflag);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/math-emu/sfp-machine.h linux/arch/sparc/math-emu/sfp-machine.h
--- v2.2.3/linux/arch/sparc/math-emu/sfp-machine.h	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/math-emu/sfp-machine.h	Wed Mar 10 16:53:36 1999
@@ -115,16 +115,6 @@
     X##_s = _flo->bits.sign;					\
   } while (0)
 
-#define __FP_PACK_RAW_1(fs, val, X)				\
-  do {								\
-    union _FP_UNION_##fs *_flo =				\
-    	(union _FP_UNION_##fs *)val;				\
-								\
-    _flo->bits.frac = X##_f;					\
-    _flo->bits.exp  = X##_e;					\
-    _flo->bits.sign = X##_s;					\
-  } while (0)
-  
 #define __FP_UNPACK_RAW_2(fs, X, val)			\
   do {							\
     union _FP_UNION_##fs *_flo =			\
@@ -136,17 +126,6 @@
     X##_s  = _flo->bits.sign;				\
   } while (0)
 
-#define __FP_PACK_RAW_2(fs, val, X)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    _flo->bits.frac0 = X##_f0;				\
-    _flo->bits.frac1 = X##_f1;				\
-    _flo->bits.exp   = X##_e;				\
-    _flo->bits.sign  = X##_s;				\
-  } while (0)
-
 #define __FP_UNPACK_RAW_4(fs, X, val)			\
   do {							\
     union _FP_UNION_##fs *_flo =			\
@@ -160,55 +139,103 @@
     X##_s  = _flo->bits.sign;				\
   } while (0)
 
-#define __FP_PACK_RAW_4(fs, val, X)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    _flo->bits.frac0 = X##_f[0];			\
-    _flo->bits.frac1 = X##_f[1];			\
-    _flo->bits.frac2 = X##_f[2];			\
-    _flo->bits.frac3 = X##_f[3];			\
-    _flo->bits.exp   = X##_e;				\
-    _flo->bits.sign  = X##_s;				\
-  } while (0)
-
 #define __FP_UNPACK_S(X,val)		\
   do {					\
     __FP_UNPACK_RAW_1(S,X,val);		\
     _FP_UNPACK_CANONICAL(S,1,X);	\
   } while (0)
 
-#define __FP_PACK_S(val,X)		\
-  do {					\
-    _FP_PACK_CANONICAL(S,1,X);		\
-    __FP_PACK_RAW_1(S,val,X);		\
-  } while (0)
-
 #define __FP_UNPACK_D(X,val)		\
   do {					\
     __FP_UNPACK_RAW_2(D,X,val);		\
     _FP_UNPACK_CANONICAL(D,2,X);	\
   } while (0)
 
-#define __FP_PACK_D(val,X)		\
-  do {					\
-    _FP_PACK_CANONICAL(D,2,X);		\
-    __FP_PACK_RAW_2(D,val,X);		\
-  } while (0)
-
 #define __FP_UNPACK_Q(X,val)		\
   do {					\
     __FP_UNPACK_RAW_4(Q,X,val);		\
     _FP_UNPACK_CANONICAL(Q,4,X);	\
   } while (0)
 
-#define __FP_PACK_Q(val,X)		\
-  do {					\
-    _FP_PACK_CANONICAL(Q,4,X);		\
-    __FP_PACK_RAW_4(Q,val,X);		\
+#define __FP_PACK_RAW_1(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+    	(union _FP_UNION_##fs *)val;				\
+								\
+    _flo->bits.frac = X##_f;					\
+    _flo->bits.exp  = X##_e;					\
+    _flo->bits.sign = X##_s;					\
+  } while (0)
+  
+#define __FP_PACK_RAW_2(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    _flo->bits.frac0 = X##_f0;				\
+    _flo->bits.frac1 = X##_f1;				\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
+  } while (0)
+
+#define __FP_PACK_RAW_4(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    _flo->bits.frac0 = X##_f[0];			\
+    _flo->bits.frac1 = X##_f[1];			\
+    _flo->bits.frac2 = X##_f[2];			\
+    _flo->bits.frac3 = X##_f[3];			\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
   } while (0)
 
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+/* We only actually write to the destination register
+ * if exceptions signalled (if any) will not trap.
+ */
+#ifdef __SMP__
+#define __FPU_TEM \
+	(((current->tss.fsr)>>23)&0x1f)
+#else
+extern struct task_struct *last_task_used_math;
+#define __FPU_TEM \
+	(((last_task_used_math->tss.fsr)>>23)&0x1f)
+#endif
+#define __FPU_TRAP_P(bits) \
+	((__FPU_TEM & (bits)) != 0)
+
+#define __FP_PACK_S(val,X)			\
+({  int __exc = _FP_PACK_CANONICAL(S,1,X);	\
+    if(!__exc || !__FPU_TRAP_P(__exc))		\
+        __FP_PACK_RAW_1(S,val,X);		\
+    __exc;					\
+})
+
+#define __FP_PACK_D(val,X)			\
+({  int __exc = _FP_PACK_CANONICAL(D,2,X);	\
+    if(!__exc || !__FPU_TRAP_P(__exc))		\
+        __FP_PACK_RAW_2(D,val,X);		\
+    __exc;					\
+})
+
+#define __FP_PACK_Q(val,X)			\
+({  int __exc = _FP_PACK_CANONICAL(Q,4,X);	\
+    if(!__exc || !__FPU_TRAP_P(__exc))		\
+        __FP_PACK_RAW_4(Q,val,X);		\
+    __exc;					\
+})
+
+/* Obtain the current rounding mode. */
+#ifdef __SMP__
+#define FP_ROUNDMODE	((current->tss.fsr >> 30) & 0x3)
+#else
+#define FP_ROUNDMODE	((last_task_used_math->tss.fsr >> 30) & 0x3)
+#endif
+
 /* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */
 
 #include <linux/types.h>
@@ -361,3 +388,9 @@
 #define __BYTE_ORDER __LITTLE_ENDIAN
 #endif
 
+/* Exception flags. */
+#define EFLAG_INVALID		(1 << 4)
+#define EFLAG_OVERFLOW		(1 << 3)
+#define EFLAG_UNDERFLOW		(1 << 2)
+#define EFLAG_DIVZERO		(1 << 1)
+#define EFLAG_INEXACT		(1 << 0)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/Makefile linux/arch/sparc/mm/Makefile
--- v2.2.3/linux/arch/sparc/mm/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc/mm/Makefile	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.32 1998/08/16 16:02:25 ecd Exp $
+# $Id: Makefile,v 1.33 1999/01/02 16:45:47 davem Exp $
 # Makefile for the linux Sparc-specific parts of the memory manager.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
--- v2.2.3/linux/arch/sparc/mm/fault.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc/mm/fault.c	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.96 1998/11/08 11:13:56 davem Exp $
+/* $Id: fault.c,v 1.101 1999/01/04 06:24:52 jj Exp $
  * fault.c:  Page fault handlers for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -13,11 +13,13 @@
 #include <linux/ptrace.h>
 #include <linux/mman.h>
 #include <linux/tasks.h>
+#include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/signal.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/interrupt.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -149,9 +151,7 @@
 	       (unsigned long) tsk->mm->context);
 	printk(KERN_ALERT "tsk->mm->pgd = %08lx\n",
 	       (unsigned long) tsk->mm->pgd);
-	lock_kernel();
 	die_if_kernel("Oops", regs);
-	unlock_kernel();
 }
 
 asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, 
@@ -201,6 +201,13 @@
 
 	if(text_fault)
 		address = regs->pc;
+
+	/*
+	 * If we're in an interrupt or have no user
+	 * context, we must not take the fault..
+	 */
+        if (in_interrupt() || mm == &init_mm)
+                goto do_kernel_fault;
 
 	down(&mm->mmap_sem);
 	/* The kernel referencing a bad kernel pointer can lock up
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/init.c linux/arch/sparc/mm/init.c
--- v2.2.3/linux/arch/sparc/mm/init.c	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/mm/init.c	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.60 1998/09/13 04:30:31 davem Exp $
+/*  $Id: init.c,v 1.63 1999/03/20 22:02:01 davem Exp $
  *  linux/arch/sparc/mm/init.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -37,6 +37,8 @@
 
 extern void show_net_buffers(void);
 
+unsigned long *sparc_valid_addr_bitmap;
+
 struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 unsigned long sparc_unmapped_base;
 
@@ -215,16 +217,20 @@
 				unsigned long limit = base + sp_banks[tmp2].num_bytes;
 
 				if((phys_addr >= base) && (phys_addr < limit) &&
-				   ((phys_addr + PAGE_SIZE) < limit))
+				   ((phys_addr + PAGE_SIZE) < limit)) {
 					mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+					set_bit(MAP_NR(addr) >> 8, sparc_valid_addr_bitmap);
+				}
 			}
 		}
 	} else {
 		if((sparc_cpu_model == sun4m) || (sparc_cpu_model == sun4d)) {
 			srmmu_frob_mem_map(start_mem);
 		} else {
-			for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE)
+			for(addr = start_mem; addr < end_mem; addr += PAGE_SIZE) {
 				mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+				set_bit(MAP_NR(addr) >> 8, sparc_valid_addr_bitmap);
+			}
 		}
 	}
 }
@@ -234,6 +240,7 @@
 	int codepages = 0;
 	int datapages = 0;
 	int initpages = 0; 
+	int i;
 	unsigned long addr;
 	struct page *page, *end;
 
@@ -243,6 +250,12 @@
 	end_mem &= PAGE_MASK;
 	max_mapnr = MAP_NR(end_mem);
 	high_memory = (void *) end_mem;
+	
+	sparc_valid_addr_bitmap = (unsigned long *)start_mem;
+	i = max_mapnr >> (8 + 5);
+	i += 1;
+	memset(sparc_valid_addr_bitmap, 0, i << 2);
+	start_mem += i << 2;
 
 	start_mem = PAGE_ALIGN(start_mem);
 	num_physpages = 0;
@@ -255,6 +268,7 @@
 		else
 #endif	
 			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+		set_bit(MAP_NR(addr) >> 8, sparc_valid_addr_bitmap);
 		addr += PAGE_SIZE;
 	}
 
@@ -266,6 +280,9 @@
 		if (PageSkip(page)) {
 			unsigned long low, high;
 
+			/* See srmmu_frob_mem_map() for why this is done.  -DaveM */
+			page++;
+
 			low = PAGE_ALIGN((unsigned long)(page+1));
 			if (page->next_hash < page)
 				high = ((unsigned long)end) & PAGE_MASK;
@@ -313,11 +330,18 @@
 	       initpages << (PAGE_SHIFT-10),
 	       (unsigned long)PAGE_OFFSET, end_mem);
 
-	freepages.min = nr_free_pages >> 7;
-	if(freepages.min < 16)
-		freepages.min = 16;
-	freepages.low = freepages.min + (freepages.min >> 1);
-	freepages.high = freepages.min + freepages.min;
+	/* NOTE NOTE NOTE NOTE
+	 * Please keep track of things and make sure this
+	 * always matches the code in mm/page_alloc.c -DaveM
+	 */
+	i = nr_free_pages >> 7;
+	if (i < 48)
+		i = 48;
+	if (i > 256)
+		i = 256;
+	freepages.min = i;
+	freepages.low = i << 1;
+	freepages.high = freepages.low + i;
 }
 
 void free_initmem (void)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.2.3/linux/arch/sparc/mm/srmmu.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/sparc/mm/srmmu.c	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.175 1998/08/28 18:57:31 zaitcev Exp $
+/* $Id: srmmu.c,v 1.184 1999/03/20 22:02:03 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -12,6 +12,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/vmalloc.h>
+#include <linux/pagemap.h>
 #include <linux/init.h>
 
 #include <asm/page.h>
@@ -216,24 +217,36 @@
 	mem_map[MAP_NR(pg1)].flags &= ~(1<<PG_reserved);
 	mem_map[MAP_NR(pg2)].flags &= ~(1<<PG_reserved);
 	mem_map[MAP_NR(pg3)].flags &= ~(1<<PG_reserved);
-
+	
 	start_mem = PAGE_ALIGN(start_mem);
 	for(i = 0; srmmu_map[i].size; i++) {
 		bank_start = srmmu_map[i].vbase;
 		
-		if (i && bank_start - bank_end > 2 * PAGE_SIZE) {
+		/* Making a one or two pages PG_skip holes
+		 * is not necessary.  We add one more because
+		 * we must set the PG_skip flag on the first
+		 * two mem_map[] entries for the hole.  Go and
+		 * see the mm/filemap.c:shrink_mmap() loop for
+		 * details. -DaveM
+		 */
+		if (i && bank_start - bank_end > 3 * PAGE_SIZE) {
 			mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
 			mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(bank_start);
+			mem_map[MAP_NR(bank_end)+1UL].flags |= (1<<PG_skip);
+			mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map + MAP_NR(bank_start);
 			PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(bank_start));
 			if (bank_end > KERNBASE && bank_start < KERNBASE) {
 				mem_map[0].flags |= (1<<PG_skip);
 				mem_map[0].next_hash = mem_map + MAP_NR(bank_start);
+				mem_map[1].flags |= (1<<PG_skip);
+				mem_map[1].next_hash = mem_map + MAP_NR(bank_start);
 				PGSKIP_DEBUG(0, MAP_NR(bank_start));
 			}
 		}
 		
 		bank_end = bank_start + srmmu_map[i].size;
 		while(bank_start < bank_end) {
+			set_bit(MAP_NR(bank_start) >> 8, sparc_valid_addr_bitmap);
 			if((bank_start >= KERNBASE) &&
 			   (bank_start < start_mem)) {
 				bank_start += PAGE_SIZE;
@@ -250,14 +263,19 @@
 	if (bank_end < KERNBASE) {
 		mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
 		mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(KERNBASE);
+		mem_map[MAP_NR(bank_end)+1UL].flags |= (1<<PG_skip);
+		mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map + MAP_NR(KERNBASE);
 		PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(KERNBASE));
 	} else if (MAP_NR(bank_end) < max_mapnr) {
 		mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+		mem_map[MAP_NR(bank_end)+1UL].flags |= (1<<PG_skip);
 		if (mem_map[0].flags & (1 << PG_skip)) {
 			mem_map[MAP_NR(bank_end)].next_hash = mem_map[0].next_hash;
+			mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map[0].next_hash;
 			PGSKIP_DEBUG(MAP_NR(bank_end), mem_map[0].next_hash - mem_map);
 		} else {
 			mem_map[MAP_NR(bank_end)].next_hash = mem_map;
+			mem_map[MAP_NR(bank_end)+1UL].next_hash = mem_map;
 			PGSKIP_DEBUG(MAP_NR(bank_end), 0);
 		}
 	}
@@ -447,7 +465,8 @@
 /* This must update the context table entry for this process. */
 static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
-	if(tsk->mm->context != NO_CONTEXT) {
+	if(tsk->mm->context != NO_CONTEXT &&
+	   tsk->mm->pgd != pgdp) {
 		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
 		flush_tlb_mm(tsk->mm);
@@ -798,13 +817,19 @@
 
 static void srmmu_switch_to_context(struct task_struct *tsk)
 {
+	int set = 0;
+
 	if(tsk->mm->context == NO_CONTEXT) {
 		alloc_context(tsk->mm);
 		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
 		flush_tlb_mm(tsk->mm);
-	}
-	srmmu_set_context(tsk->mm->context);
+		set = 1;
+	} else if(tsk->mm != current->mm)
+		set = 1;
+
+	if(set != 0)
+		srmmu_set_context(tsk->mm->context);
 }
 
 static void srmmu_init_new_context(struct mm_struct *mm)
@@ -1273,6 +1298,12 @@
 				   unsigned long end);
 extern void viking_flush_tlb_page(struct vm_area_struct *vma,
 				  unsigned long page);
+extern void sun4dsmp_flush_tlb_all(void);
+extern void sun4dsmp_flush_tlb_mm(struct mm_struct *mm);
+extern void sun4dsmp_flush_tlb_range(struct mm_struct *mm, unsigned long start,
+				   unsigned long end);
+extern void sun4dsmp_flush_tlb_page(struct vm_area_struct *vma,
+				  unsigned long page);
 
 /* hypersparc.S */
 extern void hypersparc_flush_cache_all(void);
@@ -1311,7 +1342,8 @@
 	if(pgdp != swapper_pg_dir)
 		hypersparc_flush_page_to_ram(page);
 
-	if(tsk->mm->context != NO_CONTEXT) {
+	if(tsk->mm->context != NO_CONTEXT &&
+	   tsk->mm->pgd != pgdp) {
 		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
 		flush_tlb_mm(tsk->mm);
@@ -1320,11 +1352,13 @@
 
 static void viking_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
-	viking_flush_page((unsigned long)pgdp);
-	if(tsk->mm->context != NO_CONTEXT) {
-		flush_cache_mm(current->mm);
+	if(pgdp != swapper_pg_dir)
+		viking_flush_page((unsigned long)pgdp);
+	if(tsk->mm->context != NO_CONTEXT &&
+	   tsk->mm->pgd != pgdp) {
+		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
-		flush_tlb_mm(current->mm);
+		flush_tlb_mm(tsk->mm);
 	}
 }
 
@@ -1334,6 +1368,9 @@
 	unsigned long page = ((unsigned long) pgdp) & PAGE_MASK;
 	unsigned long line;
 
+	if(pgdp == swapper_pg_dir)
+		goto skip_flush;
+
 	a = 0x20; b = 0x40; c = 0x60; d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
 	page &= PAGE_MASK;
 	line = (page + PAGE_SIZE) - 0x100;
@@ -1354,16 +1391,19 @@
 				     "r" (a), "r" (b), "r" (c), "r" (d),
 				     "r" (e), "r" (f), "r" (g));
 	} while(line != page);
-
-	if(tsk->mm->context != NO_CONTEXT) {
-		flush_cache_mm(current->mm);
+skip_flush:
+	if(tsk->mm->context != NO_CONTEXT &&
+	   tsk->mm->pgd != pgdp) {
+		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
-		flush_tlb_mm(current->mm);
+		flush_tlb_mm(tsk->mm);
 	}
 }
 
 static void hypersparc_switch_to_context(struct task_struct *tsk)
 {
+	int set = 0;
+
 	if(tsk->mm->context == NO_CONTEXT) {
 		ctxd_t *ctxp;
 
@@ -1371,9 +1411,14 @@
 		ctxp = &srmmu_context_table[tsk->mm->context];
 		srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4))));
 		hypersparc_flush_page_to_ram((unsigned long)ctxp);
+		set = 1;
+	} else if(tsk->mm != current->mm)
+		set = 1;
+
+	if(set != 0) {
+		hyper_flush_whole_icache();
+		srmmu_set_context(tsk->mm->context);
 	}
-	hyper_flush_whole_icache();
-	srmmu_set_context(tsk->mm->context);
 }
 
 static void hypersparc_init_new_context(struct mm_struct *mm)
@@ -1386,9 +1431,10 @@
 	srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) mm->pgd) >> 4))));
 	hypersparc_flush_page_to_ram((unsigned long)ctxp);
 
-	hyper_flush_whole_icache();
-	if(mm == current->mm)
+	if(mm == current->mm) {
+		hyper_flush_whole_icache();
 		srmmu_set_context(mm->context);
+	}
 }
 
 static unsigned long mempool;
@@ -1998,6 +2044,11 @@
 static void srmmu_destroy_context(struct mm_struct *mm)
 {
 	if(mm->context != NO_CONTEXT && atomic_read(&mm->count) == 1) {
+		/* XXX This could be drastically improved.
+		 * XXX We are only called from __exit_mm and it just did
+		 * XXX cache/tlb mm flush and right after this will (re-)
+		 * XXX SET_PAGE_DIR to swapper_pg_dir.  -DaveM
+		 */
 		flush_cache_mm(mm);
 		ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
 		flush_tlb_mm(mm);
@@ -2028,8 +2079,11 @@
 		offset = (address & PAGE_MASK) - vma->vm_start;
 		vmaring = inode->i_mmap; 
 		do {
-			vaddr = vmaring->vm_start + offset;
+			/* Do not mistake ourselves as another mapping. */
+			if(vmaring == vma)
+				continue;
 
+			vaddr = vmaring->vm_start + offset;
 			if ((vaddr ^ address) & vac_badbits) {
 				alias_found++;
 				start = vmaring->vm_start;
@@ -2042,7 +2096,7 @@
 					if(!ptep) goto next;
 
 					if((pte_val(*ptep) & SRMMU_ET_MASK) == SRMMU_VALID) {
-#if 1
+#if 0
 						printk("Fixing USER/USER alias [%ld:%08lx]\n",
 						       vmaring->vm_mm->context, start);
 #endif
@@ -2057,11 +2111,12 @@
 			}
 		} while ((vmaring = vmaring->vm_next_share) != NULL);
 
-		if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) {
+		if(alias_found && ((pte_val(pte) & SRMMU_CACHE) != 0)) {
 			pgdp = srmmu_pgd_offset(vma->vm_mm, address);
-			ptep = srmmu_pte_offset((pmd_t *) pgdp, address);
+			pmdp = srmmu_pmd_offset(pgdp, address);
+			ptep = srmmu_pte_offset(pmdp, address);
 			flush_cache_page(vma, address);
-			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE);
+			set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE)));
 			flush_tlb_page(vma, address);
 		}
 	done:
@@ -2652,15 +2707,8 @@
 
 	/* Ahhh, the viking.  SRMMU VLSI abortion number two... */
 	if(mreg & VIKING_MMODE) {
-		unsigned long bpreg;
-
 		srmmu_name = "TI Viking";
 		viking_mxcc_present = 0;
-
-		bpreg = viking_get_bpreg();
-		bpreg &= ~(VIKING_ACTION_MIX);
-		viking_set_bpreg(bpreg);
-
 		msi_set_sync();
 
 		BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_viking, BTFIXUPCALL_NORM);
@@ -2697,10 +2745,20 @@
 	BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP);
 	BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP);
 
-	BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
+#ifdef __SMP__
+	if (sparc_cpu_model == sun4d) {
+		BTFIXUPSET_CALL(flush_tlb_all, sun4dsmp_flush_tlb_all, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, sun4dsmp_flush_tlb_mm, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, sun4dsmp_flush_tlb_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, sun4dsmp_flush_tlb_range, BTFIXUPCALL_NORM);
+	} else
+#endif
+	{
+		BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
+	}
 
 	BTFIXUPSET_CALL(flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP);
 	BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP);
@@ -3027,10 +3085,12 @@
 	BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM);
+	if (sparc_cpu_model != sun4d) {
+		BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM);
+		BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM);
+	}
 	BTFIXUPSET_CALL(flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM);
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c
--- v2.2.3/linux/arch/sparc/mm/sun4c.c	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/mm/sun4c.c	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-/* $Id: sun4c.c,v 1.171 1998/09/21 05:05:41 jj Exp $
+/* $Id: sun4c.c,v 1.173 1999/01/17 02:20:37 davem Exp $
  * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -2688,6 +2688,10 @@
 			unsigned long vaddr = vmaring->vm_start + offset;
 			unsigned long start;
 
+			/* Do not mistake ourselves as another mapping. */
+			if(vmaring == vma)
+				continue;
+
 			if (S4CVAC_BADALIAS(vaddr, address)) {
 				alias_found++;
 				start = vmaring->vm_start;
@@ -2699,8 +2703,8 @@
 
 					if(pte_val(*ptep) & _SUN4C_PAGE_PRESENT) {
 						flush_cache_page(vmaring, start);
-						pte_val(*ptep) = (pte_val(*ptep) |
-								  _SUN4C_PAGE_NOCACHE);
+						*ptep = __pte(pte_val(*ptep) |
+							      _SUN4C_PAGE_NOCACHE);
 						flush_tlb_page(vmaring, start);
 					}
 				next:
@@ -2712,7 +2716,7 @@
 		if(alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) {
 			pgdp = sun4c_pgd_offset(vma->vm_mm, address);
 			ptep = sun4c_pte_offset((pmd_t *) pgdp, address);
-			pte_val(*ptep) = (pte_val(*ptep) | _SUN4C_PAGE_NOCACHE);
+			*ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE);
 			pte = pte_val(*ptep);
 		}
 	}
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/mm/viking.S linux/arch/sparc/mm/viking.S
--- v2.2.3/linux/arch/sparc/mm/viking.S	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc/mm/viking.S	Wed Mar 10 16:53:36 1999
@@ -1,8 +1,9 @@
-/* $Id: viking.S,v 1.11 1998/02/20 18:07:50 jj Exp $
+/* $Id: viking.S,v 1.12 1999/02/23 13:23:50 jj Exp $
  * viking.S: High speed Viking cache/mmu operations
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * Copyright (C) 1997,1998  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998,1999  Jakub Jelinek  (jj@ultra.linux.cz)
+ * Copyright (C) 1999  Pavel Semerad  (semerad@ss1000.ms.mff.cuni.cz)
  */
 
 #include <asm/ptrace.h>
@@ -15,16 +16,12 @@
 #include <asm/cprefix.h>
 #include <asm/btfixup.h>
 
-#define WINDOW_FLUSH(tmp1, tmp2)					\
-	mov	0, tmp1;						\
-98:	ld	[%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2;	\
-	orcc	%g0, tmp2, %g0;						\
-	add	tmp1, 1, tmp1;						\
-	bne	98b;							\
-	 save	%sp, -64, %sp;						\
-99:	subcc	tmp1, 1, tmp1;						\
-	bne	99b;							\
-	 restore %g0, %g0, %g0;
+#ifdef __SMP__
+	.data
+	.align	4
+sun4dsmp_flush_tlb_spin:
+	.word	0
+#endif
 
 	.text
 	.align	4
@@ -176,8 +173,10 @@
 	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+#ifndef __SMP__
 1:	retl
 	 nop
+#endif
 
 viking_flush_tlb_range:
 	mov	SRMMU_CTX_REG, %g1
@@ -198,8 +197,10 @@
 	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+#ifndef __SMP__
 2:	retl
 	 nop
+#endif
 
 viking_flush_tlb_page:
 	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
@@ -215,11 +216,96 @@
 	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
 	retl
 	 sta	%g5, [%g1] ASI_M_MMUREGS
+#ifndef __SMP__
 1:	retl
 	 nop
+#endif
 
 viking_flush_page_to_ram:
 viking_flush_page_for_dma:
 viking_flush_sig_insns:
 	retl
 	 nop
+
+#ifdef __SMP__
+	.globl	sun4dsmp_flush_tlb_all, sun4dsmp_flush_tlb_mm
+	.globl	sun4dsmp_flush_tlb_range, sun4dsmp_flush_tlb_page
+sun4dsmp_flush_tlb_all:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	2f
+	 mov	0x400, %g1
+	sta	%g0, [%g1] ASI_M_FLUSH_PROBE
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+2:	tst	%g5
+	bne,a	2b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+
+sun4dsmp_flush_tlb_mm:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	2f
+	 mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o1
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	mov	0x300, %g2
+	sta	%o1, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%g2] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+2:	tst	%g5
+	bne,a	2b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+
+sun4dsmp_flush_tlb_range:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	3f
+	 mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	sethi	%hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	and	%o1, %o4, %o1
+	add	%o1, 0x200, %o1
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+2:	sub	%o1, %o4, %o1
+	cmp	%o1, %o2
+	blu,a	2b
+	 sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+3:	tst	%g5
+	bne,a	3b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+
+sun4dsmp_flush_tlb_page:
+	sethi	%hi(sun4dsmp_flush_tlb_spin), %g3
+1:	ldstub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	tst	%g5
+	bne	2f
+	 mov	SRMMU_CTX_REG, %g1
+	ld	[%o0 + 0x00], %o0	/* XXX vma->vm_mm GROSS XXX */
+	ld	[%o0 + AOFF_mm_context], %o3
+	lda	[%g1] ASI_M_MMUREGS, %g5
+	and	%o1, PAGE_MASK, %o1
+	sta	%o3, [%g1] ASI_M_MMUREGS
+	sta	%g0, [%o1] ASI_M_FLUSH_PROBE
+	sta	%g5, [%g1] ASI_M_MMUREGS
+	retl
+	 stb	%g0, [%g3 + %lo(sun4dsmp_flush_tlb_spin)]
+2:	tst	%g5
+	bne,a	2b
+	 ldub	[%g3 + %lo(sun4dsmp_flush_tlb_spin)], %g5
+	b,a	1b
+	 nop
+#endif
diff -u --recursive --new-file v2.2.3/linux/arch/sparc/vmlinux.lds linux/arch/sparc/vmlinux.lds
--- v2.2.3/linux/arch/sparc/vmlinux.lds	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/vmlinux.lds	Wed Mar 10 16:53:36 1999
@@ -31,6 +31,10 @@
   __start___ksymtab = .;
   __ksymtab  : { *(__ksymtab) }
   __stop___ksymtab = .;
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
   . = ALIGN(4096);
   __init_begin = .;
   .text.init : { *(.text.init) }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.2.3/linux/arch/sparc64/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc64/Makefile	Wed Mar 10 16:53:36 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.33 1998/10/19 07:04:02 jj Exp $
+# $Id: Makefile,v 1.35 1999/01/02 16:45:50 davem Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -46,6 +46,7 @@
 else
   CFLAGS := $(CFLAGS) -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow \
 	    -ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare
+  AFLAGS += -m64 -mcpu=ultrasparc
 endif
 
 # Uncomment this to get spinlock/rwlock debugging on SMP.
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.2.3/linux/arch/sparc64/config.in	Tue Jan 19 11:32:51 1999
+++ linux/arch/sparc64/config.in	Mon Mar 15 16:10:43 1999
@@ -1,8 +1,8 @@
-# $Id: config.in,v 1.58 1998/11/16 04:47:30 davem Exp $
+# $Id: config.in,v 1.65 1999/03/14 03:12:48 anton Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
-mainmenu_name "Linux/SPARC Kernel Configuration"
+mainmenu_name "Linux/UltraSPARC Kernel Configuration"
 
 mainmenu_option next_comment
 comment 'Code maturity level options'
@@ -24,7 +24,6 @@
 define_bool CONFIG_VT y
 define_bool CONFIG_VT_CONSOLE y
 
-bool 'Support for AP1000 multicomputer' CONFIG_AP1000
 bool 'Symmetric multi-processing support' CONFIG_SMP
 
 mainmenu_option next_comment
@@ -34,28 +33,20 @@
 source drivers/video/Config.in
 endmenu
 
-if [ "$CONFIG_AP1000" = "y" ]; then
-	define_bool CONFIG_NO_KEYBOARD y
-	define_bool CONFIG_APFDDI y
-	define_bool CONFIG_APBLOCK y
-	define_bool CONFIG_APBIF y
-	tristate 'OPIU DDV Driver' CONFIG_DDV
-else
-	# Global things across all Sun machines.
-	define_bool CONFIG_SBUS y
-	define_bool CONFIG_SBUSCHAR y
-	define_bool CONFIG_SUN_MOUSE y
-	define_bool CONFIG_SERIAL y
-	define_bool CONFIG_SUN_SERIAL y
-	define_bool CONFIG_SERIAL_CONSOLE y
-	define_bool CONFIG_SUN_KEYBOARD y
-	define_bool CONFIG_SUN_CONSOLE y
-	define_bool CONFIG_SUN_AUXIO y
-	define_bool CONFIG_SUN_IO y
-	bool 'PCI support' CONFIG_PCI
-	source drivers/sbus/char/Config.in
-	source drivers/sbus/audio/Config.in
-fi
+# Global things across all Sun machines.
+define_bool CONFIG_SBUS y
+define_bool CONFIG_SBUSCHAR y
+define_bool CONFIG_SUN_MOUSE y
+define_bool CONFIG_SERIAL y
+define_bool CONFIG_SUN_SERIAL y
+define_bool CONFIG_SERIAL_CONSOLE y
+define_bool CONFIG_SUN_KEYBOARD y
+define_bool CONFIG_SUN_CONSOLE y
+define_bool CONFIG_SUN_AUXIO y
+define_bool CONFIG_SUN_IO y
+bool 'PCI support' CONFIG_PCI
+source drivers/sbus/char/Config.in
+source drivers/sbus/audio/Config.in
 
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
 if [ "$CONFIG_PCI" = "y" ]; then
@@ -221,14 +212,14 @@
 		fi
 		bool 'Sun LANCE support' CONFIG_SUNLANCE
 		tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
+		if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+		  tristate 'Sun BigMAC 10/100baseT support' CONFIG_SUNBMAC
+		fi
 		tristate 'Sun QuadEthernet support' CONFIG_SUNQE
 		tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS
 		if [ "$CONFIG_PCI" = "y" ]; then
 			tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
-# Turned off until updated 3c59x.c driver
-# gets approved by Linus...  --DAVEM
-#
-#			tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
+			tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
 		fi
 #		bool 'FDDI driver support' CONFIG_FDDI
 #		if [ "$CONFIG_FDDI" = "y" ]; then
@@ -236,6 +227,15 @@
 	fi
 	endmenu
 fi
+
+# This one must be before the filesystem configs. -DaveM
+mainmenu_option next_comment
+comment 'Unix 98 PTY support'
+bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
+if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
+	int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
+fi
+endmenu
 
 source fs/Config.in
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.2.3/linux/arch/sparc64/defconfig	Wed Mar 10 15:29:45 1999
+++ linux/arch/sparc64/defconfig	Sun Mar 21 07:23:38 1999
@@ -19,7 +19,6 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-# CONFIG_AP1000 is not set
 # CONFIG_SMP is not set
 
 #
@@ -28,7 +27,9 @@
 CONFIG_PROM_CONSOLE=y
 CONFIG_FB=y
 CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
 # CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
 CONFIG_FB_SBUS=y
 CONFIG_FB_CREATOR=y
 CONFIG_FB_CGSIX=y
@@ -71,9 +72,9 @@
 #
 # Linux/SPARC audio subsystem (EXPERIMENTAL)
 #
-# CONFIG_SPARCAUDIO is not set
+CONFIG_SPARCAUDIO=y
 # CONFIG_SPARCAUDIO_AMD7930 is not set
-# CONFIG_SPARCAUDIO_CS4231 is not set
+CONFIG_SPARCAUDIO_CS4231=y
 # CONFIG_SPARCAUDIO_DBRI is not set
 # CONFIG_SPARCAUDIO_DUMMY is not set
 CONFIG_SUN_OPENPROMFS=m
@@ -144,7 +145,6 @@
 # (it is safe to leave these untouched)
 #
 CONFIG_INET_RARP=m
-CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 CONFIG_IPV6=m
 # CONFIG_IPV6_EUI64 is not set
@@ -165,6 +165,10 @@
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -214,11 +218,13 @@
 # FC4 drivers
 #
 CONFIG_FC4_SOC=m
+CONFIG_FC4_SOCAL=m
 
 #
 # FC4 targets
 #
 CONFIG_SCSI_PLUTO=m
+CONFIG_SCSI_FCAL=m
 
 #
 # Network device support
@@ -236,29 +242,53 @@
 # CONFIG_SLIP_MODE_SLIP6 is not set
 CONFIG_SUNLANCE=y
 CONFIG_HAPPYMEAL=y
+CONFIG_SUNBMAC=m
 CONFIG_SUNQE=m
 CONFIG_MYRI_SBUS=m
 CONFIG_DE4X5=m
+CONFIG_VORTEX=m
+
+#
+# Unix 98 PTY support
+#
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
 
 #
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-CONFIG_MINIX_FS=m
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=m
-# CONFIG_JOLIET is not set
+CONFIG_AUTOFS_FS=m
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+# CONFIG_HFS_FS is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
 CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+# CONFIG_JOLIET is not set
+CONFIG_MINIX_FS=m
+# CONFIG_NTFS_FS is not set
+CONFIG_HPFS_FS=m
 CONFIG_PROC_FS=y
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=m
+CONFIG_EXT2_FS=y
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_UFS_FS_WRITE=y
+
+#
+# Network File Systems
+#
+CONFIG_CODA_FS=m
 CONFIG_NFS_FS=y
 CONFIG_NFSD=m
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-CONFIG_CODA_FS=m
 CONFIG_SMB_FS=m
 CONFIG_SMB_WIN95=y
 CONFIG_NCP_FS=m
@@ -268,21 +298,16 @@
 # CONFIG_NCPFS_NFS_NS is not set
 # CONFIG_NCPFS_OS2_NS is not set
 # CONFIG_NCPFS_MOUNT_SUBDIR is not set
-CONFIG_HPFS_FS=m
-# CONFIG_NTFS_FS is not set
-CONFIG_SYSV_FS=m
-CONFIG_AFFS_FS=m
-# CONFIG_HFS_FS is not set
-CONFIG_ROMFS_FS=m
-CONFIG_AUTOFS_FS=m
-CONFIG_AMIGA_PARTITION=y
-CONFIG_UFS_FS=m
+
+#
+# Partition Types
+#
 CONFIG_BSD_DISKLABEL=y
+# CONFIG_MAC_PARTITION is not set
 CONFIG_SMD_DISKLABEL=y
 CONFIG_SOLARIS_X86_PARTITION=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_MAC_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+CONFIG_AMIGA_PARTITION=y
 CONFIG_NLS=y
 
 #
@@ -313,6 +338,7 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
 #
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- v2.2.3/linux/arch/sparc64/kernel/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc64/kernel/Makefile	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.41 1998/10/11 06:58:14 davem Exp $
+# $Id: Makefile,v 1.43 1999/01/02 16:45:53 davem Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -20,7 +20,7 @@
 	    traps.o devices.o auxio.o ioport.o \
 	    irq.o ptrace.o time.o sys_sparc.o signal.o \
 	    unaligned.o sys_sunos32.o sunos_ioctl32.o \
-	    central.o psycho.o
+	    central.o psycho.o starfire.o
 OX_OBJS  := sparc64_ksyms.o
 
 ifdef CONFIG_PCI
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c
--- v2.2.3/linux/arch/sparc64/kernel/binfmt_aout32.c	Thu Nov 12 16:21:18 1998
+++ linux/arch/sparc64/kernel/binfmt_aout32.c	Sun Mar 21 07:23:38 1999
@@ -9,7 +9,6 @@
 
 #include <linux/module.h>
 
-#include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
@@ -18,9 +17,10 @@
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
-#include <linux/file.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/malloc.h>
@@ -58,14 +58,13 @@
  * macros to write out all the necessary info.
  */
 #define DUMP_WRITE(addr,nr) \
-while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) \
-	goto close_coredump
+while (file->f_op->write(file,(char *)(addr),(nr),&file->f_pos) != (nr)) goto close_coredump
 
 #define DUMP_SEEK(offset) \
-if (file.f_op->llseek) { \
-	if (file.f_op->llseek(&file,(offset),0) != (offset)) \
+if (file->f_op->llseek) { \
+	if (file->f_op->llseek(file,(offset),0) != (offset)) \
  		goto close_coredump; \
-} else file.f_pos = (offset)
+} else file->f_pos = (offset)
 
 /*
  * Routine writes a core dump image in the current directory.
@@ -82,7 +81,7 @@
 {
 	struct dentry * dentry = NULL;
 	struct inode * inode = NULL;
-	struct file file;
+	struct file * file;
 	mm_segment_t fs;
 	int has_dumped = 0;
 	char corefile[6+sizeof(current->comm)];
@@ -106,29 +105,16 @@
 #else
 	corefile[4] = '\0';
 #endif
-	dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
-	if (IS_ERR(dentry)) {
-		dentry = NULL;
+	file = filp_open(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
+	if (IS_ERR(file))
 		goto end_coredump;
-	}
+	dentry = file->f_dentry;
 	inode = dentry->d_inode;
 	if (!S_ISREG(inode->i_mode))
-		goto end_coredump;
+		goto close_coredump;
 	if (!inode->i_op || !inode->i_op->default_file_ops)
-		goto end_coredump;
-	if (get_write_access(inode))
-		goto end_coredump;
-	file.f_mode = 3;
-	file.f_flags = 0;
-	file.f_count = 1;
-	file.f_dentry = dentry;
-	file.f_pos = 0;
-	file.f_reada = 0;
-	file.f_op = inode->i_op->default_file_ops;
-	if (file.f_op->open)
-		if (file.f_op->open(inode,&file))
-			goto done_coredump;
-	if (!file.f_op->write)
+		goto close_coredump;
+	if (!file->f_op->write)
 		goto close_coredump;
 	has_dumped = 1;
 	current->flags |= PF_DUMPCORE;
@@ -175,13 +161,9 @@
 	set_fs(KERNEL_DS);
 	DUMP_WRITE(current,sizeof(*current));
 close_coredump:
-	if (file.f_op->release)
-		file.f_op->release(inode,&file);
-done_coredump:
-	put_write_access(inode);
+	close_fp(file, NULL);
 end_coredump:
 	set_fs(fs);
-	dput(dentry);
 	return has_dumped;
 }
 
@@ -269,7 +251,6 @@
 		return -ENOEXEC;
 	}
 
-	current->personality = PER_LINUX;
 	fd_offset = N_TXTOFF(ex);
 
 	/* Check initial limits. This avoids letting people circumvent
@@ -288,6 +269,8 @@
 		return retval;
 
 	/* OK, This is the point of no return */
+	current->personality = PER_LINUX;
+
 	current->mm->end_code = ex.a_text +
 		(current->mm->start_code = N_TXTADDR(ex));
 	current->mm->end_data = ex.a_data +
@@ -297,8 +280,7 @@
 
 	current->mm->rss = 0;
 	current->mm->mmap = NULL;
-	current->suid = current->euid = current->fsuid = bprm->e_uid;
-	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+	compute_creds(bprm);
  	current->flags &= ~PF_FORKNOEXEC;
 	if (N_MAGIC(ex) == NMAGIC) {
 		/* Fuck me plenty... */
@@ -404,48 +386,44 @@
 do_load_aout32_library(int fd)
 {
         struct file * file;
-	struct exec ex;
-	struct dentry * dentry;
 	struct inode * inode;
-	unsigned int len;
-	unsigned int bss;
-	unsigned int start_addr;
+	unsigned long bss, start_addr, len;
 	unsigned long error;
+	int retval;
+	loff_t offset = 0;
+	struct exec ex;
 
-	file = fcheck(fd);
-
-	if (!file || !file->f_op)
-		return -EACCES;
-
-	dentry = file->f_dentry;
-	inode = dentry->d_inode;
-
-	/* Seek into the file */
-	if (file->f_op->llseek) {
-		if ((error = file->f_op->llseek(file, 0, 0)) != 0)
-			return -ENOEXEC;
-	} else
-		file->f_pos = 0;
+	retval = -EACCES;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	if (!file->f_op)
+		goto out_putf;
+	inode = file->f_dentry->d_inode;
 
+	retval = -ENOEXEC;
+	/* N.B. Save current fs? */
 	set_fs(KERNEL_DS);
-	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos);
+	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset);
 	set_fs(USER_DS);
 	if (error != sizeof(ex))
-		return -ENOEXEC;
+		goto out_putf;
 
 	/* We come in here for the regular a.out style of shared libraries */
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
 	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
 	    inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
-		return -ENOEXEC;
+		goto out_putf;
 	}
+
 	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
 	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
 		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
-		return -ENOEXEC;
+		goto out_putf;
 	}
 
-	if (N_FLAGS(ex)) return -ENOEXEC;
+	if (N_FLAGS(ex))
+		goto out_putf;
 
 	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
 	   this off to get the starting address for the page */
@@ -457,18 +435,26 @@
 			PROT_READ | PROT_WRITE | PROT_EXEC,
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
 			N_TXTOFF(ex));
+	retval = error;
 	if (error != start_addr)
-		return error;
+		goto out_putf;
+
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
 	bss = ex.a_text + ex.a_data + ex.a_bss;
 	if (bss > len) {
-		error = do_mmap(NULL, start_addr + len, bss-len,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_PRIVATE|MAP_FIXED, 0);
+		error = do_mmap(NULL, start_addr + len, bss - len,
+				PROT_READ | PROT_WRITE | PROT_EXEC,
+				MAP_PRIVATE | MAP_FIXED, 0);
+		retval = error;
 		if (error != start_addr + len)
-			return error;
+			goto out_putf;
 	}
-	return 0;
+	retval = 0;
+
+out_putf:
+	fput(file);
+out:
+	return retval;
 }
 
 static int
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/central.c linux/arch/sparc64/kernel/central.c
--- v2.2.3/linux/arch/sparc64/kernel/central.c	Thu Aug  6 14:06:30 1998
+++ linux/arch/sparc64/kernel/central.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: central.c,v 1.6 1998/05/14 13:35:45 jj Exp $
+/* $Id: central.c,v 1.11 1998/12/14 12:18:16 davem Exp $
  * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -7,11 +7,17 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
 
 #include <asm/page.h>
 #include <asm/fhc.h>
 
 struct linux_central *central_bus = NULL;
+struct linux_fhc *fhc_list = NULL;
+
+#define IS_CENTRAL_FHC(__fhc)	((__fhc) == central_bus->child)
 
 static inline unsigned long long_align(unsigned long addr)
 {
@@ -22,6 +28,156 @@
 extern void prom_central_ranges_init(int cnode, struct linux_central *central);
 extern void prom_fhc_ranges_init(int fnode, struct linux_fhc *fhc);
 
+static unsigned long probe_other_fhcs(unsigned long memory_start)
+{
+	struct linux_prom64_registers fpregs[6];
+	char namebuf[128];
+	int node;
+
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "fhc");
+	if (node == 0) {
+		prom_printf("FHC: Cannot find any toplevel firehose controllers.\n");
+		prom_halt();
+	}
+	while(node) {
+		struct linux_fhc *fhc;
+		int board;
+		u32 tmp;
+
+		fhc = (struct linux_fhc *)memory_start;
+		memory_start += sizeof(struct linux_fhc);
+		memory_start = long_align(memory_start);
+
+		/* Link it into the FHC chain. */
+		fhc->next = fhc_list;
+		fhc_list = fhc;
+
+		/* Toplevel FHCs have no parent. */
+		fhc->parent = NULL;
+		
+		fhc->prom_node = node;
+		prom_getstring(node, "name", namebuf, sizeof(namebuf));
+		strcpy(fhc->prom_name, namebuf);
+		prom_fhc_ranges_init(node, fhc);
+
+		/* Non-central FHC's have 64-bit OBP format registers. */
+		if(prom_getproperty(node, "reg",
+				    (char *)&fpregs[0], sizeof(fpregs)) == -1) {
+			prom_printf("FHC: Fatal error, cannot get fhc regs.\n");
+			prom_halt();
+		}
+
+		/* Only central FHC needs special ranges applied. */
+		fhc->fhc_regs.pregs = (struct fhc_internal_regs *)
+			__va(fpregs[0].phys_addr);
+		fhc->fhc_regs.ireg = (struct fhc_ign_reg *)
+			__va(fpregs[1].phys_addr);
+		fhc->fhc_regs.ffregs = (struct fhc_fanfail_regs *)
+			__va(fpregs[2].phys_addr);
+		fhc->fhc_regs.sregs = (struct fhc_system_regs *)
+			__va(fpregs[3].phys_addr);
+		fhc->fhc_regs.uregs = (struct fhc_uart_regs *)
+			__va(fpregs[4].phys_addr);
+		fhc->fhc_regs.tregs = (struct fhc_tod_regs *)
+			__va(fpregs[5].phys_addr);
+
+		board = prom_getintdefault(node, "board#", -1);
+		fhc->board = board;
+
+		tmp = fhc->fhc_regs.pregs->fhc_jtag_ctrl;
+		if((tmp & FHC_JTAG_CTRL_MENAB) != 0)
+			fhc->jtag_master = 1;
+		else
+			fhc->jtag_master = 0;
+
+		tmp = fhc->fhc_regs.pregs->fhc_id;
+		printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n",
+		       board,
+		       (tmp & FHC_ID_VERS) >> 28,
+		       (tmp & FHC_ID_PARTID) >> 12,
+		       (tmp & FHC_ID_MANUF) >> 1,
+		       (fhc->jtag_master ? "(JTAG Master)" : ""));
+		
+		/* This bit must be set in all non-central FHC's in
+		 * the system.  When it is clear, this identifies
+		 * the central board.
+		 */
+		fhc->fhc_regs.pregs->fhc_control |= FHC_CONTROL_IXIST;
+
+		/* Look for the next FHC. */
+		node = prom_getsibling(node);
+		if(node == 0)
+			break;
+		node = prom_searchsiblings(node, "fhc");
+		if(node == 0)
+			break;
+	}
+
+	return memory_start;
+}
+
+static void probe_clock_board(struct linux_central *central,
+			      struct linux_fhc *fhc,
+			      int cnode, int fnode)
+{
+	struct linux_prom_registers cregs[3];
+	int clknode, nslots, tmp, nregs;
+
+	clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board");
+	if(clknode == 0 || clknode == -1) {
+		prom_printf("Critical error, central lacks clock-board.\n");
+		prom_halt();
+	}
+	nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs));
+	if (nregs == -1) {
+		prom_printf("CENTRAL: Fatal error, cannot map clock-board regs.\n");
+		prom_halt();
+	}
+	nregs /= sizeof(struct linux_prom_registers);
+	prom_apply_fhc_ranges(fhc, &cregs[0], nregs);
+	prom_apply_central_ranges(central, &cregs[0], nregs);
+	central->cfreg = (volatile u8 *)
+		__va((((unsigned long)cregs[0].which_io) << 32) |
+		     (((unsigned long)cregs[0].phys_addr)+0x02));
+	central->clkregs = (struct clock_board_regs *)
+		__va((((unsigned long)cregs[1].which_io) << 32) |
+		     (((unsigned long)cregs[1].phys_addr)));
+	if(nregs == 2)
+		central->clkver = NULL;
+	else
+		central->clkver = (volatile u8 *)
+			__va((((unsigned long)cregs[2].which_io) << 32) |
+			     (((unsigned long)cregs[2].phys_addr)));
+
+	tmp = central->clkregs->stat1;
+	tmp &= 0xc0;
+	switch(tmp) {
+	case 0x40:
+		nslots = 16;
+		break;
+	case 0xc0:
+		nslots = 8;
+		break;
+	case 0x80:
+		if(central->clkver != NULL &&
+		   *(central->clkver) != 0) {
+			if((*(central->clkver) & 0x80) != 0)
+				nslots = 4;
+			else
+				nslots = 5;
+			break;
+		}
+	default:
+		nslots = 4;
+		break;
+	};
+	central->slots = nslots;
+	printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n",
+	       central->slots, *(central->cfreg),
+	       (central->clkver ? *(central->clkver) : 0x00));
+}
+
 unsigned long central_probe(unsigned long memory_start)
 {
 	struct linux_prom_registers fpregs[6];
@@ -30,9 +186,12 @@
 	int cnode, fnode, err;
 
 	cnode = prom_finddevice("/central");
-	if(cnode == 0 || cnode == -1)
+	if(cnode == 0 || cnode == -1) {
+		extern void starfire_check(void);
+
+		starfire_check();
 		return memory_start;
-	printk("CENTRAL: found central PROM node %08x.\n", cnode);
+	}
 
 	/* Ok we got one, grab some memory for software state. */
 	memory_start = long_align(memory_start);
@@ -54,7 +213,9 @@
 	prom_central_ranges_init(cnode, central_bus);
 
 	/* And then central's FHC. */
-	fhc->next = NULL;
+	fhc->next = fhc_list;
+	fhc_list = fhc;
+
 	fhc->parent = central_bus;
 	fnode = prom_searchsiblings(prom_getchild(cnode), "fhc");
 	if(fnode == 0 || fnode == -1) {
@@ -67,9 +228,9 @@
 
 	prom_fhc_ranges_init(fnode, fhc);
 
-	/* Finally, map in FHC register set. */
+	/* Now, map in FHC register set. */
 	if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) {
-		prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n");
+		prom_printf("CENTRAL: Fatal error, cannot get fhc regs.\n");
 		prom_halt();
 	}
 	prom_apply_central_ranges(central_bus, &fpregs[0], 6);
@@ -93,11 +254,144 @@
 		__va((((unsigned long)fpregs[5].which_io)<<32) |
 		     (((unsigned long)fpregs[5].phys_addr)));
 
+	/* Obtain board number from board status register, Central's
+	 * FHC lacks "board#" property.
+	 */
+	err = fhc->fhc_regs.pregs->fhc_bsr;
+	fhc->board = (((err >> 16) & 0x01) |
+		      ((err >> 12) & 0x0e));
+
+	fhc->jtag_master = 0;
+
+	/* Attach the clock board registers for CENTRAL. */
+	probe_clock_board(central_bus, fhc, cnode, fnode);
+
 	err = fhc->fhc_regs.pregs->fhc_id;
-	printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n",
+	printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n",
+	       fhc->board,
 	       ((err & FHC_ID_VERS) >> 28),
 	       ((err & FHC_ID_PARTID) >> 12),
 	       ((err & FHC_ID_MANUF) >> 1));
 
-	return memory_start;
+	return probe_other_fhcs(memory_start);
+}
+
+static __inline__ void fhc_ledblink(struct linux_fhc *fhc, int on)
+{
+	volatile u32 *ctrl = (volatile u32 *)
+		&fhc->fhc_regs.pregs->fhc_control;
+	u32 tmp;
+
+	tmp = *ctrl;
+
+	/* NOTE: reverse logic on this bit */
+	if (on)
+		tmp &= ~(FHC_CONTROL_RLED);
+	else
+		tmp |= FHC_CONTROL_RLED;
+	tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE);
+
+	*ctrl = tmp;
+	tmp = *ctrl;
+}
+
+static __inline__ void central_ledblink(struct linux_central *central, int on)
+{
+	volatile u8 *ctrl = (volatile u8 *) &central->clkregs->control;
+	int tmp;
+
+	tmp = *ctrl;
+
+	/* NOTE: reverse logic on this bit */
+	if(on)
+		tmp &= ~(CLOCK_CTRL_RLED);
+	else
+		tmp |= CLOCK_CTRL_RLED;
+
+	*ctrl = tmp;
+	tmp = *ctrl;
+}
+
+static struct timer_list sftimer;
+static int led_state;
+
+static void sunfire_timer(unsigned long __ignored)
+{
+	struct linux_fhc *fhc;
+
+	central_ledblink(central_bus, led_state);
+	for(fhc = fhc_list; fhc != NULL; fhc = fhc->next)
+		if(! IS_CENTRAL_FHC(fhc))
+			fhc_ledblink(fhc, led_state);
+	led_state = ! led_state;
+	sftimer.expires = jiffies + (HZ >> 1);
+	add_timer(&sftimer);
+}
+
+/* After PCI/SBUS busses have been probed, this is called to perform
+ * final initialization of all FireHose Controllers in the system.
+ */
+void firetruck_init(void)
+{
+	struct linux_central *central = central_bus;
+	struct linux_fhc *fhc;
+
+	/* No central bus, nothing to do. */
+	if (central == NULL)
+		return;
+
+	for(fhc = fhc_list; fhc != NULL; fhc = fhc->next) {
+		volatile u32 *ctrl = (volatile u32 *)
+			&fhc->fhc_regs.pregs->fhc_control;
+		u32 tmp;
+
+		/* Clear all of the interrupt mapping registers
+		 * just in case OBP left them in a foul state.
+		 */
+#define ZAP(REG1, REG2) \
+do {	volatile u32 *__iclr = (volatile u32 *)(&(REG1)); \
+	volatile u32 *__imap = (volatile u32 *)(&(REG2)); \
+	*(__iclr) = 0; \
+	(void) *(__iclr); \
+	*(__imap) &= ~(0x80000000); \
+	(void) *(__imap); \
+} while(0)
+
+		ZAP(fhc->fhc_regs.ffregs->fhc_ff_iclr,
+		    fhc->fhc_regs.ffregs->fhc_ff_imap);
+		ZAP(fhc->fhc_regs.sregs->fhc_sys_iclr,
+		    fhc->fhc_regs.sregs->fhc_sys_imap);
+		ZAP(fhc->fhc_regs.uregs->fhc_uart_iclr,
+		    fhc->fhc_regs.uregs->fhc_uart_imap);
+		ZAP(fhc->fhc_regs.tregs->fhc_tod_iclr,
+		    fhc->fhc_regs.tregs->fhc_tod_imap);
+
+#undef ZAP
+
+		/* Setup FHC control register. */
+		tmp = *ctrl;
+
+		/* All non-central boards have this bit set. */
+		if(! IS_CENTRAL_FHC(fhc))
+			tmp |= FHC_CONTROL_IXIST;
+
+		/* For all FHCs, clear the firmware synchronization
+		 * line and both low power mode enables.
+		 */
+		tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE);
+		*ctrl = tmp;
+		tmp = *ctrl; /* Ensure completion */
+	}
+
+	/* OBP leaves it on, turn it off so clock board timer LED
+	 * is in sync with FHC ones.
+	 */
+	central->clkregs->control &= ~(CLOCK_CTRL_RLED);
+
+	led_state = 0;
+	init_timer(&sftimer);
+	sftimer.data = 0;
+	sftimer.function = &sunfire_timer;
+	sftimer.expires = jiffies + (HZ >> 1);
+	add_timer(&sftimer);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/cpu.c linux/arch/sparc64/kernel/cpu.c
--- v2.2.3/linux/arch/sparc64/kernel/cpu.c	Thu Apr 23 20:21:31 1998
+++ linux/arch/sparc64/kernel/cpu.c	Sun Mar 21 07:11:36 1999
@@ -49,11 +49,11 @@
 #define NSPARCCHIPS  (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
 
 #ifdef __SMP__
-char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" };
-char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" };
+char *sparc_cpu_type[64] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" };
+char *sparc_fpu_type[64] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" };
 #else
-char *sparc_cpu_type[NR_CPUS] = { "cpu-oops", };
-char *sparc_fpu_type[NR_CPUS] = { "fpu-oops", };
+char *sparc_cpu_type[64] = { "cpu-oops", };
+char *sparc_fpu_type[64] = { "fpu-oops", };
 #endif
 
 unsigned int fsr_storage;
@@ -88,7 +88,7 @@
 	if(i==NSPARCCHIPS) {
 		printk("DEBUG: manuf = 0x%x   impl = 0x%x\n", manuf, 
 			    impl);
-		sparc_cpu_type[cpuid] = "Unknow CPU";
+		sparc_cpu_type[cpuid] = "Unknown CPU";
 	}
 
 	for(i = 0; i<NSPARCFPU; i++) {
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/devices.c linux/arch/sparc64/kernel/devices.c
--- v2.2.3/linux/arch/sparc64/kernel/devices.c	Thu Aug  6 14:06:30 1998
+++ linux/arch/sparc64/kernel/devices.c	Mon Mar 15 16:10:43 1999
@@ -13,8 +13,8 @@
 #include <asm/system.h>
 #include <asm/smp.h>
 
-struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } };
-unsigned prom_cpu_nodes[NR_CPUS];
+struct prom_cpuinfo linux_cpus[64] __initdata = { { 0 } };
+unsigned prom_cpu_nodes[64];
 int linux_num_cpus = 0;
 
 extern void cpu_probe(void);
@@ -25,11 +25,12 @@
 {
 	char node_str[128];
 	int nd, prom_node_cpu, thismid;
-	int cpu_nds[NR_CPUS];  /* One node for each cpu */
+	int cpu_nds[64];  /* One node for each cpu */
 	int cpu_ctr = 0;
 
 	prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
 
+	prom_printf("Booting Linux...\n");
 	if(strcmp(node_str, "cpu") == 0) {
 		cpu_nds[0] = prom_root_node;
 		linux_cpus[0].prom_node = prom_root_node;
@@ -38,7 +39,7 @@
 	} else {
 		int scan;
 		scan = prom_getchild(prom_root_node);
-		prom_printf("root child is %08x\n", (unsigned) scan);
+		/* prom_printf("root child is %08x\n", (unsigned) scan); */
 		nd = 0;
 		while((scan = prom_getsibling(scan)) != 0) {
 			prom_getstring(scan, "device_type", node_str, sizeof(node_str));
@@ -49,11 +50,11 @@
 						 (char *) &thismid, sizeof(thismid));
 				linux_cpus[cpu_ctr].mid = thismid;
 #ifdef __SMP__				
-				prom_printf("Found CPU %d (node=%08x,mid=%d)\n",
-					    cpu_ctr, (unsigned) scan,
-					    thismid);
-				printk("Found CPU %d (node=%08x,mid=%d)\n",
-				       cpu_ctr, (unsigned) scan, thismid);
+				/* Don't pollute PROM screen with these messages. If the kernel is screwed enough
+				   that console does not start up, then we don't care how many CPUs have been found,
+				   if it starts up, the user can use console=prom to see it. */
+				/* prom_printf("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid); */
+				printk("Found CPU %d (node=%08x,mid=%d)\n", cpu_ctr, (unsigned) scan, thismid);
 #endif				       
 				cpu_ctr++;
 			}
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/dtlb_backend.S linux/arch/sparc64/kernel/dtlb_backend.S
--- v2.2.3/linux/arch/sparc64/kernel/dtlb_backend.S	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc64/kernel/dtlb_backend.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: dtlb_backend.S,v 1.6 1998/09/24 03:21:32 davem Exp $
+/* $Id: dtlb_backend.S,v 1.7 1998/12/16 04:33:28 davem Exp $
  * dtlb_backend.S: Back end to DTLB miss replacement strategy.
  *                 This is included directly into the trap table.
  *
@@ -37,28 +37,30 @@
 	be,pn		%xcc, sparc64_vpte_nucleus	! Is it from Nucleus?
 	 and		%g1, 0xffe, %g1			! Mask PMD offset bits
 	brnz,pt		%g5, sparc64_vpte_continue	! Yep, go like smoke
-	 nop						! Pipe bubble...
+	 add		%g1, %g1, %g1			! Position PMD offset some more
 	srlx		%g6, (PGD_SHIFT - 2), %g5	! Position PGD offset
 	and		%g5, 0xffc, %g5			! Mask PGD offset
 
 /* TLB1 ** ICACHE line 3: Quick VPTE miss	  	*/
 	lduwa		[%g7 + %g5] ASI_PHYS_USE_EC, %g5! Load PGD
-	brz,pn		%g5, 2f				! Valid?
+	brz,pn		%g5, vpte_noent			! Valid?
+sparc64_kpte_continue:
+	 sllx		%g5, 11, %g5			! Shift into place
 sparc64_vpte_continue:
-	 add		%g1, %g1, %g1			! Position PMD offset once again
 	lduwa		[%g5 + %g1] ASI_PHYS_USE_EC, %g5! Load PMD
-	brz,pn		%g5, 2f				! Valid?
+	sllx		%g5, 11, %g5			! Shift into place
+	brz,pn		%g5, vpte_noent			! Valid?
 	 sllx		%g2, 62, %g1			! Put _PAGE_VALID into %g1
 	or		%g5, VPTE_BITS, %g5		! Prepare VPTE data
-	or		%g5, %g1, %g5			! ...
 
 /* TLB1 ** ICACHE line 4: Quick VPTE miss	  	*/
+	or		%g5, %g1, %g5			! ...
 	mov		TLB_SFSR, %g1			! Restore %g1 value
 	stxa		%g5, [%g0] ASI_DTLB_DATA_IN	! Load VPTE into TLB
-	membar		#Sync				! Synchronize ASI stores
 	stxa		%g4, [%g1 + %g1] ASI_DMMU	! Restore previous TAG_ACCESS
 	retry						! Load PTE once again
-2:	mov		TLB_SFSR, %g1			! Restore %g1 value
+vpte_noent:
+	mov		TLB_SFSR, %g1			! Restore %g1 value
 	stxa		%g4, [%g1 + %g1] ASI_DMMU	! Restore previous TAG_ACCESS
 	done						! Slick trick
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S
--- v2.2.3/linux/arch/sparc64/kernel/dtlb_prot.S	Thu Aug  6 14:06:30 1998
+++ linux/arch/sparc64/kernel/dtlb_prot.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: dtlb_prot.S,v 1.17 1998/05/25 16:59:11 davem Exp $
+/* $Id: dtlb_prot.S,v 1.18 1999/03/02 15:42:14 jj Exp $
  * dtlb_prot.S: DTLB protection trap strategy.
  *              This is included directly into the trap table.
  *
@@ -45,13 +45,13 @@
 	mov		TLB_TAG_ACCESS, %g4		! Prepare reload of vaddr
 	bgu,pn		%xcc, winfix_trampoline		! Yes, perform winfixup
 	 ldxa		[%g4] ASI_DMMU, %g5		! Put tagaccess in %g5
-	sethi		%hi(1f), %g7			! Nope, normal fault
+	ba,pt		%xcc, sparc64_realfault_common	! Nope, normal fault
 
 /* PROT ** ICACHE line 4: More real fault processing */
-	ba,pt		%xcc, etrap			! Save state
-1:	 or		%g7, %lo(1b), %g7		! ...
-	ba,pt		%xcc, sparc64_realfault_continue! Now call the fault handler
-	 mov		1, %o2				! Indicate this was a write
+	 mov		1, %g4				! Indicate this was a write
+	nop
+	nop
+	nop
 	nop
 	nop
 	nop
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c
--- v2.2.3/linux/arch/sparc64/kernel/ebus.c	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc64/kernel/ebus.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.33 1998/09/21 05:06:03 jj Exp $
+/* $Id: ebus.c,v 1.35 1999/01/26 14:34:11 jj Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -263,6 +263,31 @@
 	ebus->next = 0;
 
 	while (ebusnd) {
+		/* SUNW,pci-qfe uses four empty ebuses on it.
+		   I think we should not consider them here,
+		   as they have half of the properties this
+		   code expects and once we do PCI hot-plug,
+		   we'd have to tweak with the ebus_chain
+		   in the runtime after initialization. -jj */
+		if (!prom_getchild (ebusnd)) {
+			pdev = pci_find_device(PCI_VENDOR_ID_SUN, 
+					       PCI_DEVICE_ID_SUN_EBUS, pdev);
+			if (!pdev) {
+				if (ebus == ebus_chain) {
+					ebus_chain = NULL;
+					printk("ebus: No EBus's found.\n");
+#ifdef PROM_DEBUG
+					dprintf("ebus: No EBus's found.\n");
+#endif
+					return;
+				}
+				break;
+			}
+			
+			cookie = pdev->sysdata;
+			ebusnd = cookie->prom_node;
+			continue;
+		}
 		printk("ebus%d:", num_ebus);
 #ifdef PROM_DEBUG
 		dprintf("ebus%d:", num_ebus);
@@ -279,6 +304,12 @@
 		pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
 		pci_command |= PCI_COMMAND_MASTER;
 		pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+
+		/* Set reasonable cache line size and latency timer values. */
+		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+
+		/* NOTE: Cache line size is in 32-bit word units. */
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10);
 
 		len = prom_getproperty(ebusnd, "reg", (void *)regs,
 				       sizeof(regs));
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S
--- v2.2.3/linux/arch/sparc64/kernel/entry.S	Sun Nov  8 14:02:46 1998
+++ linux/arch/sparc64/kernel/entry.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.91 1998/10/07 01:27:08 davem Exp $
+/* $Id: entry.S,v 1.101 1999/01/19 07:54:38 davem Exp $
  * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -29,8 +29,17 @@
 	.text
 	.align		32
 
+	.globl		sparc64_vpte_patchme1
+	.globl		sparc64_vpte_patchme2
+sparc64_vpte_nucleus:
+sparc64_vpte_patchme1:
+	sethi		%hi(0), %g5			! This has to be patched
+sparc64_vpte_patchme2:
+	or		%g5, %lo(0), %g5		! This is patched too
+	ba,pt		%xcc, sparc64_kpte_continue	! Part of dtlb_backend
+	 add		%g1, %g1, %g1			! Finish PMD offset adjustment
+
 	/* This is trivial with the new code... */
-	.align		32
 	.globl		do_fpdis
 do_fpdis:
 	ldub		[%g6 + AOFF_task_tss + AOFF_thread_fpsaved], %g5	! Load	Group
@@ -164,69 +173,57 @@
 	 *
 	 * With this method we can do most of the cross-call tlb/cache
 	 * flushing very quickly.
+	 *
+	 * Current CPU's IRQ worklist table is locked into %g1,
+	 * don't touch.
 	 */
-	.data
-	.align  8
-	.globl	ivec_spurious_cookie
-ivec_spurious_cookie:	.xword	0
-
 	.text
-	.align	32
-	.globl	do_ivec
+	.align		32
+	.globl		do_ivec
 do_ivec:
-	ldxa		[%g0] ASI_INTR_RECEIVE, %g5
-	andcc		%g5, 0x20, %g0
-	be,pn		%xcc, do_ivec_return
-	 mov		0x40, %g2
-
-	/* Load up Interrupt Vector Data 0 register. */
+	wr		%g0, ASI_UDB_INTR_R, %asi
+	ldxa		[%g0 + 0x40] %asi, %g3
 	sethi		%hi(KERNBASE), %g4
-	ldxa		[%g2] ASI_UDB_INTR_R, %g3
 	cmp		%g3, %g4
 	bgeu,pn		%xcc, do_ivec_xcall
-	 nop
-	and		%g3, 0x7ff, %g3
-	sllx		%g3, 3, %g3
-	ldx		[%g1 + %g3], %g2
-	brz,pn		%g2, do_ivec_spurious
-	 sethi		%hi(0x80000000), %g5
+	 srlx		%g3, 32, %g5
+	stxa		%g0, [%g0] ASI_INTR_RECEIVE
+	membar		#Sync
 
-	or		%g2, %g5, %g2
-	stx		%g2, [%g1 + %g3]
+	sethi		%hi(ivector_table), %g2
+	sllx		%g3, 5, %g3
+	or		%g2, %lo(ivector_table), %g2
+	add		%g2, %g3, %g3
+	ldx		[%g3 + 0x08], %g2	/* irq_info */
+	ldub		[%g3 + 0x04], %g4	/* pil */
+	brz,pn		%g2, do_ivec_spurious
+	 mov		1, %g2
 
-	/* No branches, worse case we don't know about this interrupt
-	 * yet, so we would just write a zero into the softint register
-	 * which is completely harmless.
-	 */
+	sllx		%g2, %g4, %g2
+	sllx		%g4, 2, %g4
+	lduw		[%g1 + %g4], %g5	/* g5 = irq_work(cpu, pil) */
+	stw		%g5, [%g3 + 0x00]	/* bucket->irq_chain = g5 */
+	stw		%g3, [%g1 + %g4]	/* irq_work(cpu, pil) = bucket */
 	wr		%g2, 0x0, %set_softint
-do_ivec_return:
-	stxa		%g0, [%g0] ASI_INTR_RECEIVE
-	membar		#Sync
 	retry
 do_ivec_xcall:
-	srlx		%g3, 32, %g5
-	add		%g2, 0x10, %g2
+	ldxa		[%g0 + 0x50] %asi, %g6
+
 	srl		%g3, 0, %g3
-	ldxa		[%g2] ASI_UDB_INTR_R, %g6
-	add		%g2, 0x10, %g2
-	ldxa		[%g2] ASI_UDB_INTR_R, %g7
+	ldxa		[%g0 + 0x60] %asi, %g7
 	stxa		%g0, [%g0] ASI_INTR_RECEIVE
 	membar		#Sync
 	jmpl		%g3, %g0
 	 nop
-
 do_ivec_spurious:
-	srl		%g3, 3, %g3
-	sethi		%hi(ivec_spurious_cookie), %g2
-	stx		%g3, [%g2 + %lo(ivec_spurious_cookie)]
-	stxa		%g0, [%g0] ASI_INTR_RECEIVE
-	membar		#Sync
+	stw		%g3, [%g1 + 0x00]	/* irq_work(cpu, 0) = bucket */
 	rdpr		%pstate, %g5
+
 	wrpr		%g5, PSTATE_IG | PSTATE_AG, %pstate
 	sethi		%hi(109f), %g7
 	ba,pt		%xcc, etrap
 109:	 or		%g7, %lo(109b), %g7
-	call		report_spurious_ivec
+	call		catch_disabled_ivec
 	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 	ba,pt		%xcc, rtrap
 	 clr		%l6
@@ -337,7 +334,7 @@
 	or		%g1, %lo(irq_action), %g1
 	ldx		[%g1 + (11 << 3)], %g3		! irqaction[floppy_irq]
 	ldx		[%g3 + 0x10], %g4		! action->mask == ino_bucket ptr
-	ldx		[%g4 + 0x18], %g4		! bucket->iclr
+	ldx		[%g4 + 0x10], %g4		! bucket->iclr
 	stw		%g0, [%g4]			! SYSIO_ICLR_IDLE
 	membar		#Sync				! probably not needed...
 	retry
@@ -588,12 +585,20 @@
 	/* SunOS's execv() call only specifies the argv argument, the
 	 * environment settings are the same as the calling processes.
 	 */
-	.globl	sunos_execv
+	.globl	sunos_execv, sys_execve, sys32_execve
+sys_execve:
+	sethi		%hi(sparc_execve), %g1
+	ba,pt		%xcc, execve_merge
+	 or		%g1, %lo(sparc_execve), %g1
 sunos_execv:
-	sethi	%hi(sparc32_execve), %g1
-	stx	%g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]
-	jmpl	%g1 + %lo(sparc32_execve), %g0
-	 add	%sp, STACK_BIAS + REGWIN_SZ, %o0
+	stx		%g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]
+sys32_execve:
+	sethi		%hi(sparc32_execve), %g1
+	or		%g1, %lo(sparc32_execve), %g1
+execve_merge:
+	flushw
+	jmpl		%g1, %g0
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
 
 	.globl	sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall
 	.globl	sys_sigsuspend, sys_rt_sigsuspend, sys32_rt_sigsuspend
@@ -612,14 +617,6 @@
 		jmpl		%g1 + %lo(c_sys_nis_syscall), %g0
 		 nop
 
-sys_execve:	sethi		%hi(sparc_execve), %g1
-		add		%sp, STACK_BIAS + REGWIN_SZ, %o0
-		jmpl		%g1 + %lo(sparc_execve), %g0
-		 nop
-sys32_execve:	sethi		%hi(sparc32_execve), %g1
-		add		%sp, STACK_BIAS + REGWIN_SZ, %o0
-		jmpl		%g1 + %lo(sparc32_execve), %g0
-		 nop
 sys_memory_ordering:
 		sethi		%hi(sparc_memory_ordering), %g1
 		add		%sp, STACK_BIAS + REGWIN_SZ, %o1
@@ -719,27 +716,30 @@
 	.globl	sys_fork, sys_vfork, sys_clone, sparc_exit
 	.globl	ret_from_syscall
 	.align	32
-sys_fork:
-sys_vfork:	mov		SIGCHLD, %o0
-		clr		%o1
+sys_vfork:	/* Under Linux, vfork and fork are just special cases of clone. */
+		sethi		%hi(0x4000 | 0x0100 | SIGCHLD), %o0
+		or		%o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0
+		ba,pt		%xcc, sys_clone
+sys_fork:	 clr		%o1
+		mov		SIGCHLD, %o0
 sys_clone:	flushw
-		mov		%o7, %l5
-		add		%sp, STACK_BIAS + REGWIN_SZ, %o2
 		movrz		%o1, %fp, %o1
-		call		do_fork
-		 mov		%l5, %o7
+		nop
+		ba,pt		%xcc, do_fork
+		 add		%sp, STACK_BIAS + REGWIN_SZ, %o2
 ret_from_syscall:
 		/* Clear SPARC_FLAG_NEWCHILD, switch_to leaves tss.flags in
 		 * %o7 for us.  Check performance counter stuff too.
 		 */
-		andn		%o7, 0x100, %o7
-		sth		%o7, [%g6 + AOFF_task_tss + AOFF_thread_flags]
 #ifdef __SMP__
-		sethi		%hi(scheduler_lock), %o4
-		membar		#StoreStore | #LoadStore
-		stb		%g0, [%o4 + %lo(scheduler_lock)]
+		andn		%o7, 0x100, %l0
+		call		schedule_tail
+		 sth		%l0, [%g6 + AOFF_task_tss + AOFF_thread_flags]
+#else
+		andn		%o7, 0x100, %l0
+		sth		%l0, [%g6 + AOFF_task_tss + AOFF_thread_flags]
 #endif
-		andcc		%o7, 0x200, %g0
+		andcc		%l0, 0x200, %g0
 		be,pt		%icc, 1f
 		 nop
 		ldx		[%g6 + AOFF_task_tss + AOFF_thread_pcr_reg], %o7
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- v2.2.3/linux/arch/sparc64/kernel/head.S	Sun Nov  8 14:02:46 1998
+++ linux/arch/sparc64/kernel/head.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.54 1998/10/06 20:48:30 ecd Exp $
+/* $Id: head.S,v 1.59 1999/01/06 01:37:35 davem Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,7 @@
  * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
  */
 
+#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/errno.h>
 #include <asm/asm_offsets.h>
@@ -330,7 +331,7 @@
 /* IMPORTANT NOTE: Whenever making changes here, check
  * trampoline.S as well. -jj */
 	.globl	setup_tba
-setup_tba:
+setup_tba:	/* i0 = is_starfire */
 	save	%sp, -160, %sp
 
 	rdpr	%tba, %g7
@@ -376,9 +377,34 @@
 
 	/* Setup Interrupt globals */
 	wrpr	%o1, (PSTATE_IG|PSTATE_IE), %pstate
-	sethi	%hi(ivector_to_mask), %g5
-	or	%g5, %lo(ivector_to_mask), %g1  /* IVECTOR table */
-	mov	0x40, %g2			/* INTR data 0 register */
+#ifndef __SMP__
+	sethi	%hi(__up_workvec), %g5
+	or	%g5, %lo(__up_workvec), %g1
+#else
+	/* By definition of where we are, this is boot_cpu. */
+	sethi	%hi(cpu_data), %g5
+	or	%g5, %lo(cpu_data), %g5
+
+	brz,pt	%i0, not_starfire
+	 sethi	%hi(0x1fff4000), %g1
+	or	%g1, %lo(0x1fff4000), %g1
+	sllx	%g1, 12, %g1
+	or	%g1, 0xd0, %g1
+	lduwa	[%g1] ASI_PHYS_BYPASS_EC_E, %g1
+	b,pt	%xcc, set_worklist
+	 nop
+
+not_starfire:
+	ldxa	[%g0] ASI_UPA_CONFIG, %g1
+	srlx	%g1, 17, %g1
+	and	%g1, 0x1f, %g1
+
+	/* In theory this is: &(cpu_data[boot_cpu_id].irq_worklists[0]) */
+set_worklist:
+	sllx	%g1, 7, %g1
+	add	%g5, %g1, %g5
+	add	%g5, 64, %g1
+#endif
 
 	/* Kill PROM timer */
 	wr	%g0, 0, %tick_cmpr
@@ -407,6 +433,13 @@
 	.globl	empty_bad_page
 empty_bad_page:
 	.skip	0x2000
+
+#ifdef CONFIG_SBUS
+/* This is just a hack to fool make depend config.h discovering
+   strategy: As the .S files below need config.h, but
+   make depend does not find it for them, we include config.h
+   in head.S */
+#endif
 
 ! 0x0000000000408000
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.2.3/linux/arch/sparc64/kernel/ioctl32.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/kernel/ioctl32.c	Mon Mar 15 16:10:43 1999
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.55 1998/11/17 07:43:17 davem Exp $
+/* $Id: ioctl32.c,v 1.59 1999/03/12 13:30:21 jj Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
@@ -52,6 +52,7 @@
 #include <asm/openpromio.h>
 #include <asm/envctrl.h>
 #include <asm/audioio.h>
+#include <asm/ethtool.h>
 
 #include <linux/soundcard.h>
 
@@ -253,11 +254,23 @@
 	case SIOCGPPPSTATS:
 	case SIOCGPPPCSTATS:
 	case SIOCGPPPVER:
+	case SIOCETHTOOL:
 		if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
 			return -EFAULT;
 		ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL);
 		if (!ifr.ifr_data)
 			return -EAGAIN;
+		if(cmd == SIOCETHTOOL) {
+			u32 data;
+
+			__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
+			if(copy_from_user(ifr.ifr_data,
+					  (char *)A(data),
+					  sizeof(struct ethtool_cmd))) {
+				free_page((unsigned long)ifr.ifr_data);
+				return -EFAULT;
+			}
+		}
 		break;
 	default:
 		if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
@@ -280,17 +293,21 @@
 		case SIOCGIFBRDADDR:
 		case SIOCGIFDSTADDR:
 		case SIOCGIFNETMASK:
+		case SIOCGIFTXQLEN:
 			if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32)))
 				return -EFAULT;
 			break;
 		case SIOCGPPPSTATS:
 		case SIOCGPPPCSTATS:
 		case SIOCGPPPVER:
+		case SIOCETHTOOL:
 		{
 			u32 data;
 			int len;
 
 			__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
+			if(cmd == SIOCETHTOOL)
+				len = sizeof(struct ethtool_cmd);
 			if(cmd == SIOCGPPPVER)
 				len = strlen(PPP_VERSION) + 1;
 			else if(cmd == SIOCGPPPCSTATS)
@@ -298,7 +315,9 @@
 			else
 				len = sizeof(struct ppp_stats);
 
-			if (copy_to_user((char *)A(data), ifr.ifr_data, len))
+			len = copy_to_user((char *)A(data), ifr.ifr_data, len);
+			free_page((unsigned long)ifr.ifr_data);
+			if(len)
 				return -EFAULT;
 			break;
 		}
@@ -1458,6 +1477,9 @@
 	case SIOCGPPPSTATS:
 	case SIOCGPPPCSTATS:
 	case SIOCGPPPVER:
+	case SIOCGIFTXQLEN:
+	case SIOCSIFTXQLEN:
+	case SIOCETHTOOL:
 		error = dev_ifsioc(fd, cmd, arg);
 		goto out;
 		
@@ -1583,7 +1605,7 @@
 	 * compatable types passed or none at all...
 	 */
 
-	/* Bit T */
+	/* Big T */
 	case TCGETA:
 	case TCSETA:
 	case TCSETAW:
@@ -1618,6 +1640,8 @@
 	case TIOCSPGRP:
 	case TIOCGPGRP:
 	case TIOCSCTTY:
+	case TIOCGPTN:
+	case TIOCSPTLCK:
 	
 	/* Big F */
 	case FBIOGTYPE:
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c
--- v2.2.3/linux/arch/sparc64/kernel/irq.c	Sun Nov  8 14:02:46 1998
+++ linux/arch/sparc64/kernel/irq.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.66 1998/10/21 15:02:25 ecd Exp $
+/* $Id: irq.c,v 1.75 1999/01/28 12:37:56 jj Exp $
  * irq.c: UltraSparc IRQ handling/init/registry.
  *
  * Copyright (C) 1997  David S. Miller  (davem@caip.rutgers.edu)
@@ -41,20 +41,30 @@
 #define SA_DMA_SYNC		0x200
 
 #ifdef __SMP__
-void distribute_irqs(void);
-static int irqs_have_been_distributed = 0;
+static void distribute_irqs(void);
 #endif
 
-/* UPA nodes send interrupt packet to UltraSparc with first data reg value
- * low 5 bits holding the IRQ identifier being delivered.  We must translate
- * this into a non-vector IRQ so we can set the softint on this cpu.  To
- * make things even more swift we store the complete mask here.
+/* UPA nodes send interrupt packet to UltraSparc with first data reg
+ * value low 5 (7 on Starfire) bits holding the IRQ identifier being
+ * delivered.  We must translate this into a non-vector IRQ so we can
+ * set the softint on this cpu.
+ *
+ * To make processing these packets efficient and race free we use
+ * an array of irq buckets below.  The interrupt vector handler in
+ * entry.S feeds incoming packets into per-cpu pil-indexed lists.
+ * The IVEC handler does not need to act atomically, the PIL dispatch
+ * code uses CAS to get an atomic snapshot of the list and clear it
+ * at the same time.
  */
 
-#define NUM_HARD_IVECS	2048
-#define NUM_IVECS	(NUM_HARD_IVECS + 64)	/* For SMP IRQ distribution alg. */
+struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (64)));
 
-unsigned long ivector_to_mask[NUM_IVECS];
+#ifndef __SMP__
+unsigned int __up_workvec[16] __attribute__ ((aligned (64)));
+#define irq_work(__cpu, __pil)	&(__up_workvec[(__pil)])
+#else
+#define irq_work(__cpu, __pil)	&(cpu_data[(__cpu)].irq_worklists[(__pil)])
+#endif
 
 /* This is based upon code in the 32-bit Sparc kernel written mostly by
  * David Redman (djhr@tadpole.co.uk).
@@ -63,30 +73,21 @@
 static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
 static int static_irq_count = 0;
 
-/* XXX Must be exported so that fast IRQ handlers can get at it... -DaveM */
+/* This is exported so that fast IRQ handlers can get at it... -DaveM */
 struct irqaction *irq_action[NR_IRQS+1] = {
 	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
 	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
 };
 
-#define IBF_DMA_SYNC	0x01
-#define IBF_PCI		0x02
-#define IBF_ACTIVE	0x04
+/* Only 8-bits are available, be careful.  -DaveM */
+#define IBF_DMA_SYNC	0x01	/* DMA synchronization behind PCI bridge needed. */
+#define IBF_PCI		0x02	/* Indicates PSYCHO/SCHIZO PCI interrupt.	 */
+#define IBF_ACTIVE	0x04	/* This interrupt is active and has a handler.	 */
+#define IBF_MULTI	0x08	/* On PCI, indicates shared bucket.		 */
 
-#define __imap(bucket) ((bucket)->iclr + (bucket)->imap_off)
 #define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq))
 #define __irq(bucket) ((unsigned int)(unsigned long)(bucket))
 
-static struct ino_bucket *bucket_base, *buckets, *endbuckets;
-
-__initfunc(unsigned long irq_init(unsigned long start_mem, unsigned long end_mem))
-{
-	start_mem = (start_mem + 15) & ~15;
-	bucket_base = buckets = (struct ino_bucket *)start_mem;
-	endbuckets = buckets + 2048;
-	return (unsigned long)endbuckets;
-}
-
 int get_irq_list(char *buf)
 {
 	int i, len = 0;
@@ -104,7 +105,7 @@
 #else
 		for (j = 0; j < smp_num_cpus; j++)
 			len += sprintf(buf + len, "%10u ",
-				kstat.irqs[cpu_logical_map(j)][i]);
+				       kstat.irqs[cpu_logical_map(j)][i]);
 #endif
 		len += sprintf(buf + len, "%c %s",
 			       (action->flags & SA_INTERRUPT) ? '+' : ' ',
@@ -224,8 +225,7 @@
 	1, /* Power Management */
 };
 
-/* INO number to IMAP register offset for PSYCHO external IRQ's.
- */
+/* INO number to IMAP register offset for PSYCHO external IRQ's. */
 #define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x)))
 
 #define psycho_imap_offset(ino)						      \
@@ -241,16 +241,27 @@
 /* Now these are always passed a true fully specified sun4u INO. */
 void enable_irq(unsigned int irq)
 {
+	extern int this_is_starfire;
 	struct ino_bucket *bucket = __bucket(irq);
-	unsigned long tid;
 	unsigned int *imap;
+	unsigned long tid;
 
-	imap = __imap(bucket);
-	if (!imap) return;
+	imap = bucket->imap;
+	if (!imap)
+		return;
 
-	/* We send it to our UPA MID, for SMP this will be different. */
-	__asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (tid) : "i" (ASI_UPA_CONFIG));
-	tid = ((tid & UPA_CONFIG_MID) << 9);
+	if(this_is_starfire == 0) {
+		/* We set it to our UPA MID. */
+		__asm__ __volatile__("ldxa [%%g0] %1, %0"
+				     : "=r" (tid)
+				     : "i" (ASI_UPA_CONFIG));
+		tid = ((tid & UPA_CONFIG_MID) << 9);
+	} else {
+		extern unsigned int starfire_translate(unsigned int *imap,
+						       unsigned int upaid);
+
+		tid = (starfire_translate(imap, current->processor) << 26);
+	}
 
 	/* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product
 	 * of this SYSIO's preconfigured IGN in the SYSIO Control
@@ -269,35 +280,83 @@
 	struct ino_bucket *bucket = __bucket(irq);
 	unsigned int *imap;
 
-	imap = __imap(bucket);
-	if (!imap) return;
-
-	/* NOTE: We do not want to futz with the IRQ clear registers
-	 *       and move the state to IDLE, the SCSI code does call
-	 *       disable_irq() to assure atomicity in the queue cmd
-	 *       SCSI adapter driver code.  Thus we'd lose interrupts.
-	 */
-	*imap &= ~(SYSIO_IMAP_VALID);
+	imap = bucket->imap;
+	if (imap != NULL) {
+		/* NOTE: We do not want to futz with the IRQ clear registers
+		 *       and move the state to IDLE, the SCSI code does call
+		 *       disable_irq() to assure atomicity in the queue cmd
+		 *       SCSI adapter driver code.  Thus we'd lose interrupts.
+		 */
+		*imap &= ~(SYSIO_IMAP_VALID);
+	}
 }
 
+/* The timer is the one "weird" interrupt which is generated by
+ * the CPU %tick register and not by some normal vectored interrupt
+ * source.  To handle this special case, we use this dummy INO bucket.
+ */
+static struct ino_bucket pil0_dummy_bucket = {
+	0,	/* irq_chain */
+	0,	/* pil */
+	0,	/* pending */
+	0,	/* flags */
+	0,	/* __unused */
+	NULL,	/* irq_info */
+	NULL,	/* iclr */
+	NULL,	/* imap */
+};
+
 unsigned int build_irq(int pil, int inofixup, unsigned int *iclr, unsigned int *imap)
 {
-	if (buckets == endbuckets)
-		panic("Out of IRQ buckets. Should not happen.\n");
-	buckets->pil = pil;
-	if (pil && (!iclr || !imap)) {
-		prom_printf("Invalid build_irq %d %d %016lx %016lx\n", pil, inofixup, iclr, imap);
+	struct ino_bucket *bucket;
+	unsigned short ino;
+
+	if(pil == 0) {
+		if(iclr != NULL || imap != NULL) {
+			prom_printf("Invalid dummy bucket for PIL0 (%p:%p)\n",
+				    iclr, imap);
+			prom_halt();
+		}
+		return __irq(&pil0_dummy_bucket);
+	}
+
+	/* RULE: Both must be specified in all other cases. */
+	if (iclr == NULL || imap == NULL) {
+		prom_printf("Invalid build_irq %d %d %016lx %016lx\n",
+			    pil, inofixup, iclr, imap);
 		prom_halt();
 	}
-	if (imap)
-		buckets->ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup;
-	else
-		buckets->ino = 0;
-		
-	buckets->iclr = iclr;
-	buckets->flags = 0;
-	buckets->imap_off = imap - iclr;
-	return __irq(buckets++);
+	
+	ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup;
+	if(ino > NUM_IVECS) {
+		prom_printf("Invalid INO %04x (%d:%d:%016lx:%016lx)\n",
+			    ino, pil, inofixup, iclr, imap);
+		prom_halt();
+	}
+
+	/* Ok, looks good, set it up.  Don't touch the irq_chain or
+	 * the pending flag.
+	 */
+	bucket = &ivector_table[ino];
+	if ((bucket->flags & IBF_ACTIVE) ||
+	    (bucket->irq_info != NULL)) {
+		/* This is a gross fatal error if it happens here. */
+		prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n");
+		prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n",
+			    ino, pil, inofixup, iclr, imap);
+		prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n",
+			    bucket->pil, bucket->iclr, bucket->imap);
+		prom_printf("IRQ: Cannot continue, halting...\n");
+		prom_halt();
+	}
+	bucket->imap  = imap;
+	bucket->iclr  = iclr;
+	bucket->pil   = pil;
+	bucket->flags = 0;
+
+	bucket->irq_info = NULL;
+
+	return __irq(bucket);
 }
 
 unsigned int sbus_build_irq(void *buscookie, unsigned int ino)
@@ -382,8 +441,44 @@
 	if(!(ino & 0x20))
 		inofixup = ino & 0x03;
 
-	bucket = __bucket(build_irq(pil, inofixup, iclr, imap));
-	
+	/* First check for sharing. */
+	ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup;
+	if (ino > NUM_IVECS) {
+		prom_printf("PSYCHO: Invalid INO %04x (%d:%d:%016lx:%016lx)\n",
+			    ino, pil, inofixup, iclr, imap);
+		prom_halt();
+	}
+	bucket = &ivector_table[ino];
+	if(bucket->flags & IBF_ACTIVE) {
+		void *old_handler = bucket->irq_info;
+		unsigned long flags;
+
+		if(old_handler == NULL) {
+			prom_printf("PSYCHO: Active bucket, but no handler.\n");
+			prom_halt();
+		}
+		save_and_cli(flags);
+		if((bucket->flags & IBF_MULTI) == 0) {
+			void **vector;
+
+			vector = kmalloc(sizeof(void *) * 4,
+					 GFP_KERNEL);
+
+			/* We might have slept. */
+			if((bucket->flags & IBF_MULTI) != 0) {
+				kfree(vector);
+			} else {
+				vector[0] = old_handler;
+				vector[1] = vector[2] = vector[3] = NULL;
+				bucket->irq_info = vector;
+				bucket->flags |= IBF_MULTI;
+			}
+		}
+		restore_flags(flags);
+	} else {
+		/* Just init the bucket */
+		bucket = __bucket(build_irq(pil, inofixup, iclr, imap));
+	}	
 	if (need_dma_sync)
 		bucket->flags |= IBF_DMA_SYNC;
 		
@@ -392,6 +487,20 @@
 }
 #endif
 
+static void atomic_bucket_insert(struct ino_bucket *bucket)
+{
+	unsigned long pstate;
+	unsigned int *ent;
+
+	__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+	__asm__ __volatile__("wrpr %0, %1, %%pstate"
+			     : : "r" (pstate), "i" (PSTATE_IE));
+	ent = irq_work(smp_processor_id(), bucket->pil);
+	bucket->irq_chain = *ent;
+	*ent = __irq(bucket);
+	__asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
+}
+
 int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 		unsigned long irqflags, const char *name, void *dev_id)
 {
@@ -400,11 +509,16 @@
 	unsigned long flags;
 	int pending = 0;
 
-	if (irq < 0x400000 || (irq & 0x80000000)) {
-		prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler);
-		prom_halt();
-	}
-	
+	if ((bucket != &pil0_dummy_bucket) &&
+	    (bucket < &ivector_table[0] ||
+	     bucket >= &ivector_table[NUM_IVECS])) {
+		unsigned int *caller;
+
+		__asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+		printk(KERN_CRIT "request_irq: Old style IRQ registry attempt "
+		       "from %p, irq %08x.\n", caller, irq);
+		return -EINVAL;
+	}	
 	if(!handler)
 	    return -EINVAL;
 
@@ -429,24 +543,26 @@
 		}
 	}
 
+	save_and_cli(flags);
+
 	action = *(bucket->pil + irq_action);
 	if(action) {
 		if((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ))
 			for (tmp = action; tmp->next; tmp = tmp->next)
 				;
-		else
+		else {
+			restore_flags(flags);
 			return -EBUSY;
-
+		}
 		if((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
 			printk("Attempt to mix fast and slow interrupts on IRQ%d "
 			       "denied\n", bucket->pil);
+			restore_flags(flags);
 			return -EBUSY;
 		}   
 		action = NULL;		/* Or else! */
 	}
 
-	save_and_cli(flags);
-
 	/* If this is flagged as statically allocated then we use our
 	 * private struct which is never freed.
 	 */
@@ -466,12 +582,65 @@
 		return -ENOMEM;
 	}
 
-	if (irqflags & SA_IMAP_MASKED) {
-		pending = ((ivector_to_mask[bucket->ino] & 0x80000000) != 0);
-		ivector_to_mask[bucket->ino] = (1 << bucket->pil);
-		if(pending)
-			ivector_to_mask[bucket->ino] |= 0x80000000;
+	if ((irqflags & SA_IMAP_MASKED) == 0) {
+		bucket->irq_info = action;
 		bucket->flags |= IBF_ACTIVE;
+	} else {
+		if((bucket->flags & IBF_ACTIVE) != 0) {
+			void *orig = bucket->irq_info;
+			void **vector = NULL;
+
+			if((bucket->flags & IBF_PCI) == 0) {
+				printk("IRQ: Trying to share non-PCI bucket.\n");
+				goto free_and_ebusy;
+			}
+			if((bucket->flags & IBF_MULTI) == 0) {
+				vector = kmalloc(sizeof(void *) * 4, GFP_KERNEL);
+				if(vector == NULL)
+					goto free_and_enomem;
+
+				/* We might have slept. */
+				if ((bucket->flags & IBF_MULTI) != 0) {
+					int ent;
+
+					kfree(vector);
+					vector = (void **)bucket->irq_info;
+					for(ent = 0; ent < 4; ent++) {
+						if (vector[ent] == NULL) {
+							vector[ent] = action;
+							break;
+						}
+					}
+					if (ent == 4)
+						goto free_and_ebusy;
+				} else {
+					vector[0] = orig;
+					vector[1] = action;
+					vector[2] = NULL;
+					vector[3] = NULL;
+					bucket->irq_info = vector;
+					bucket->flags |= IBF_MULTI;
+				}
+			} else {
+				int ent;
+
+				vector = (void **)orig;
+				for(ent = 0; ent < 4; ent++) {
+					if(vector[ent] == NULL) {
+						vector[ent] = action;
+						break;
+					}
+				}
+				if (ent == 4)
+					goto free_and_ebusy;
+			}
+		} else {
+			bucket->irq_info = action;
+			bucket->flags |= IBF_ACTIVE;
+		}
+		pending = bucket->pending;
+		if(pending)
+			bucket->pending = 0;
 	}
 
 	action->mask = (unsigned long) bucket;
@@ -489,15 +658,26 @@
 	enable_irq(irq);
 
 	/* We ate the IVEC already, this makes sure it does not get lost. */
-	if(pending)
+	if(pending) {
+		atomic_bucket_insert(bucket);
 		set_softint(1 << bucket->pil);
-
+	}
 	restore_flags(flags);
+
 #ifdef __SMP__
-	if(irqs_have_been_distributed)
-		distribute_irqs();
+	distribute_irqs();
 #endif
 	return 0;
+
+free_and_ebusy:
+	kfree(action);
+	restore_flags(flags);
+	return -EBUSY;
+
+free_and_enomem:
+	kfree(action);
+	restore_flags(flags);
+	return -ENOMEM;
 }
 
 void free_irq(unsigned int irq, void *dev_id)
@@ -507,9 +687,15 @@
 	unsigned long flags;
 	struct ino_bucket *bucket = __bucket(irq), *bp;
 
-	if (irq < 0x400000 || (irq & 0x80000000)) {
-		prom_printf("free_irq with old style irq %08x\n", irq);
-		prom_halt();
+	if ((bucket != &pil0_dummy_bucket) &&
+	    (bucket < &ivector_table[0] ||
+	     bucket >= &ivector_table[NUM_IVECS])) {
+		unsigned int *caller;
+
+		__asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+		printk(KERN_CRIT "free_irq: Old style IRQ removal attempt "
+		       "from %p, irq %08x.\n", caller, irq);
+		return;
 	}
 	
 	action = *(bucket->pil + irq_action);
@@ -545,27 +731,59 @@
 		*(bucket->pil + irq_action) = action->next;
 
 	if(action->flags & SA_IMAP_MASKED) {
-		unsigned int *imap = __imap(bucket);
+		unsigned int *imap = bucket->imap;
+		void **vector, *orig;
+		int ent;
+
+		orig = bucket->irq_info;
+		vector = (void **)orig;
+
+		if ((bucket->flags & IBF_MULTI) != 0) {
+			int other = 0;
+			void *orphan = NULL;
+			for(ent = 0; ent < 4; ent++) {
+				if(vector[ent] == action)
+					vector[ent] = NULL;
+				else if(vector[ent] != NULL) {
+					orphan = vector[ent];
+					other++;
+				}
+			}
 
-		/*
-		 * Only free when no other shared irq uses this bucket.
-		 */
-		tmp = *(bucket->pil + irq_action);
-		for( ; tmp; tmp = tmp->next)
-			if ((struct ino_bucket *)tmp->mask == bucket)
+			/* Only free when no other shared irq
+			 * uses this bucket.
+			 */
+			if(other) {
+				if (other == 1) {
+					/* Convert back to non-shared bucket. */
+					bucket->irq_info = orphan;
+					bucket->flags &= ~(IBF_MULTI);
+					kfree(vector);
+				}
 				goto out;
+			}
+		} else {
+			bucket->irq_info = NULL;
+		}
 
-		ivector_to_mask[bucket->ino] = 0;
-
+		/* This unique interrupt source is now inactive. */
 		bucket->flags &= ~IBF_ACTIVE;
-		for (bp = bucket_base; bp < endbuckets; bp++)
-			if (__imap(bp) == imap && (bp->flags & IBF_ACTIVE))
+
+		/* See if any other buckets share this bucket's IMAP
+		 * and are still active.
+		 */
+		for(ent = 0; ent < NUM_IVECS; ent++) {
+			bp = &ivector_table[ent];
+			if(bp != bucket		&&
+			   bp->imap == imap	&&
+			   (bp->flags & IBF_ACTIVE) != 0)
 				break;
-		/*
-		 * Only disable when no other sub-irq levels of
-		 * the same imap are active.
+		}
+
+		/* Only disable when no other sub-irq levels of
+		 * the same IMAP are active.
 		 */
-		if (bp == endbuckets)
+		if (ent == NUM_IVECS)
 			disable_irq(irq);
 	}
 
@@ -607,10 +825,10 @@
 	int cpu = smp_processor_id();
 
 	printk("\n%s, CPU %d:\n", str, cpu);
-	printk("irq:  %d [%d %d]\n",
+	printk("irq:  %d [%ld %ld]\n",
 	       atomic_read(&global_irq_count),
 	       cpu_data[0].irq_count, cpu_data[1].irq_count);
-	printk("bh:   %d [%d %d]\n",
+	printk("bh:   %d [%ld %ld]\n",
 	       (spin_is_locked(&global_bh_count) ? 1 : 0),
 	       cpu_data[0].bh_count, cpu_data[1].bh_count);
 }
@@ -755,57 +973,56 @@
 
 #endif /* __SMP__ */
 
-void report_spurious_ivec(struct pt_regs *regs)
+void catch_disabled_ivec(struct pt_regs *regs)
 {
-	extern unsigned long ivec_spurious_cookie;
-
-#if 0
-	printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n",
-	       ivec_spurious_cookie, regs->tpc);
-#endif
+	int cpu = smp_processor_id();
+	struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0));
 
 	/* We can actually see this on Ultra/PCI PCI cards, which are bridges
 	 * to other devices.  Here a single IMAP enabled potentially multiple
 	 * unique interrupt sources (which each do have a unique ICLR register.
 	 *
 	 * So what we do is just register that the IVEC arrived, when registered
-	 * for real the request_irq() code will check the high bit and signal
+	 * for real the request_irq() code will check the bit and signal
 	 * a local CPU interrupt for it.
 	 */
-	ivector_to_mask[ivec_spurious_cookie] |= (0x80000000);
+#if 0
+	printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n",
+	       bucket - &ivector_table[0], regs->tpc);
+#endif
+	*irq_work(cpu, 0) = 0;
+	bucket->pending = 1;
 }
 
-void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs)
-{
-	int i;
-	struct irqaction *action;
-	unsigned int cpu_irq;
-
-	cpu_irq = irq & NR_IRQS;
-	action = *(cpu_irq + irq_action);
-
-	prom_printf("Unexpected IRQ[%d]: ", irq);
-	prom_printf("PC[%016lx] NPC[%016lx] FP[%016lx]\n",
-		    regs->tpc, regs->tnpc, regs->u_regs[14]);
-
-	if(action) {
-		prom_printf("Expecting: ");
-		for(i = 0; i < 16; i++) {
-			if(action->handler)
-				prom_printf("[%s:%d:0x%016lx] ", action->name,
-					    i, (unsigned long) action->handler);
-		}
-	}
-	prom_printf("AIEEE\n");
-	prom_printf("bogus interrupt received\n");
-	prom_cmdline ();
-}
+/* Tune this... */
+#define FORWARD_VOLUME		12
 
 void handler_irq(int irq, struct pt_regs *regs)
 {
-	struct ino_bucket *bucket = NULL;
-	struct irqaction *action, *act;
+	struct ino_bucket *bp, *nbp;
 	int cpu = smp_processor_id();
+#ifdef __SMP__
+	extern int this_is_starfire;
+	int should_forward = (this_is_starfire == 0	&&
+			      irq < 10			&&
+			      current->pid != 0);
+	unsigned int buddy = 0;
+
+	/* 'cpu' is the MID (ie. UPAID), calculate the MID
+	 * of our buddy.
+	 */
+	if(should_forward != 0) {
+		buddy = cpu_number_map[cpu] + 1;
+		if (buddy >= NR_CPUS ||
+		    (buddy = cpu_logical_map(buddy)) == -1)
+			buddy = cpu_logical_map(0);
+
+		/* Voo-doo programming. */
+		if(cpu_data[buddy].idle_volume < FORWARD_VOLUME)
+			should_forward = 0;
+		buddy <<= 26;
+	}
+#endif
 
 #ifndef __SMP__
 	/*
@@ -817,30 +1034,55 @@
 	clear_softint(1 << irq);
 
 	irq_enter(cpu, irq);
-	action = *(irq + irq_action);
 	kstat.irqs[cpu][irq]++;
-	if(!action) {
-		unexpected_irq(irq, 0, regs);
-	} else {
-		act = action;
-		do {
-			if(act->flags & SA_IMAP_MASKED) {
-				bucket = (struct ino_bucket *)act->mask;
-				if(!(ivector_to_mask[bucket->ino] & 0x80000000))
-					continue;
+
+	/* Sliiiick... */
+#ifndef __SMP__
+	bp = ((irq != 0) ?
+	      __bucket(xchg32(irq_work(cpu, irq), 0)) :
+	      &pil0_dummy_bucket);
+#else
+	bp = __bucket(xchg32(irq_work(cpu, irq), 0));
+#endif
+	for( ; bp != NULL; bp = nbp) {
+		unsigned char flags = bp->flags;
+
+		nbp = __bucket(bp->irq_chain);
+		if((flags & IBF_ACTIVE) != 0) {
+			if((flags & IBF_MULTI) == 0) {
+				struct irqaction *ap = bp->irq_info;
+				ap->handler(__irq(bp), ap->dev_id, regs);
+			} else {
+				void **vector = (void **)bp->irq_info;
+				int ent;
+				for(ent = 0; ent < 4; ent++) {
+					struct irqaction *ap = vector[ent];
+					if(ap != NULL)
+						ap->handler(__irq(bp), ap->dev_id, regs);
+				}
 			}
-			act->handler(__irq(bucket), act->dev_id, regs);
-		} while((act = act->next) != NULL);
-		act = action;
-		do {
-			if(act->flags & SA_IMAP_MASKED) {
-				bucket = (struct ino_bucket *)act->mask;
-				if(!(ivector_to_mask[bucket->ino] & 0x80000000))
-					continue;
-				ivector_to_mask[bucket->ino] &= ~(0x80000000);
-				*(bucket->iclr) = SYSIO_ICLR_IDLE;
+			/* Only the dummy bucket lacks IMAP/ICLR. */
+			if(bp->pil != 0) {
+#ifdef __SMP__
+				/* Ok, here is what is going on:
+				 * 1) Retargeting IRQs on Starfire is very
+				 *    expensive so just forget about it on them.
+				 * 2) Moving around very high priority interrupts
+				 *    is a losing game.
+				 * 3) If the current cpu is idle, interrupts are
+				 *    useful work, so keep them here.  But do not
+				 *    pass to our neighbour if he is not very idle.
+				 */
+				if (should_forward != 0) {
+					/* Push it to our buddy. */
+					should_forward = 0;
+					*(bp->imap) = (buddy | SYSIO_IMAP_VALID);
+				}
+#endif
+				*(bp->iclr) = SYSIO_ICLR_IDLE;
 			}
-		} while((act = act->next) != NULL);
+		} else
+			bp->pending = 1;
 	}
 	irq_exit(cpu, irq);
 }
@@ -856,10 +1098,13 @@
 
 	irq_enter(cpu, irq);
 	kstat.irqs[cpu][irq]++;
+
+	*(irq_work(cpu, irq)) = 0;
 	bucket = (struct ino_bucket *)action->mask;
+
 	floppy_interrupt(irq, dev_cookie, regs);
-	ivector_to_mask[bucket->ino] &= ~(0x80000000);
 	*(bucket->iclr) = SYSIO_ICLR_IDLE;
+
 	irq_exit(cpu, irq);
 }
 #endif
@@ -897,11 +1142,21 @@
 	struct ino_bucket *bucket = __bucket(irq);
 	unsigned long flags;
 
-	if (irq < 0x400000 || (irq & 0x80000000)) {
-		prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler);
-		prom_halt();
-	}
+	/* No pil0 dummy buckets allowed here. */
+	if (bucket < &ivector_table[0] ||
+	    bucket >= &ivector_table[NUM_IVECS]) {
+		unsigned int *caller;
+
+		__asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+		printk(KERN_CRIT "request_fast_irq: Old style IRQ registry attempt "
+		       "from %p, irq %08x.\n", caller, irq);
+		return -EINVAL;
+	}	
 	
+	/* Only IMAP style interrupts can be registered as fast. */
+	if(bucket->pil == 0)
+		return -EINVAL;
+
 	if(!handler)
 		return -EINVAL;
 
@@ -919,6 +1174,7 @@
 		printk("request_fast_irq: Trying to register yet already owned.\n");
 		return -EBUSY;
 	}
+
 	save_and_cli(flags);
 	if(irqflags & SA_STATIC_ALLOC) {
 		if(static_irq_count < MAX_STATIC_ALLOC)
@@ -936,7 +1192,8 @@
 	}
 	install_fast_irq(bucket->pil, handler);
 
-	ivector_to_mask[bucket->ino] = (1 << bucket->pil);
+	bucket->irq_info = action;
+	bucket->flags |= IBF_ACTIVE;
 
 	action->mask = (unsigned long) bucket;
 	action->handler = handler;
@@ -949,9 +1206,9 @@
 	enable_irq(irq);
 
 	restore_flags(flags);
+
 #ifdef __SMP__
-	if(irqs_have_been_distributed)
-		distribute_irqs();
+	distribute_irqs();
 #endif
 	return 0;
 }
@@ -1025,50 +1282,51 @@
 }
 
 #ifdef __SMP__
-/* Called from smp_commence, when we know how many cpus are in the system
- * and can have device IRQ's directed at them.
- */
-/* #define SMP_IRQ_VERBOSE */
-void distribute_irqs(void)
+static int retarget_one_irq(struct irqaction *p, int goal_cpu)
+{
+	extern int this_is_starfire;
+	struct ino_bucket *bucket = __bucket(p->mask);
+	unsigned int *imap = bucket->imap;
+	unsigned int tid;
+
+	/* Never change this, it causes problems on Ex000 systems. */
+	if (bucket->pil == 12)
+		return goal_cpu;
+
+	if(this_is_starfire == 0) {
+		tid = __cpu_logical_map[goal_cpu] << 26;
+	} else {
+		extern unsigned int starfire_translate(unsigned int *imap,
+						       unsigned int upaid);
+
+		tid = (starfire_translate(imap, __cpu_logical_map[goal_cpu]) << 26);
+	}
+	*imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
+
+	goal_cpu++;
+	if(goal_cpu >= NR_CPUS ||
+	   __cpu_logical_map[goal_cpu] == -1)
+		goal_cpu = 0;
+	return goal_cpu;
+}
+
+/* Called from request_irq. */
+static void distribute_irqs(void)
 {
 	unsigned long flags;
 	int cpu, level;
 
-#ifdef SMP_IRQ_VERBOSE
-	printk("SMP: redistributing interrupts...\n");
-#endif
 	save_and_cli(flags);
 	cpu = 0;
 	for(level = 0; level < NR_IRQS; level++) {
 		struct irqaction *p = irq_action[level];
-
 		while(p) {
-			if(p->flags & SA_IMAP_MASKED) {
-				struct ino_bucket *bucket = (struct ino_bucket *)p->mask;
-				unsigned int *imap = __imap(bucket);
-				unsigned int val;
-				unsigned long tid = __cpu_logical_map[cpu] << 26;
-
-				val = *imap;
-				*imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
-
-#ifdef SMP_IRQ_VERBOSE
-				printk("SMP: Redirecting IGN[%x] INO[%x] "
-				       "to cpu %d [%s]\n",
-				       (val & SYSIO_IMAP_IGN) >> 6,
-				       (val & SYSIO_IMAP_INO), cpu,
-				       p->name);
-#endif
-
-				cpu++;
-				if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1)
-					cpu = 0;
-			}
+			if(p->flags & SA_IMAP_MASKED)
+				cpu = retarget_one_irq(p, cpu);
 			p = p->next;
 		}
 	}
 	restore_flags(flags);
-	irqs_have_been_distributed = 1;
 }
 #endif
 
@@ -1146,13 +1404,13 @@
 	static int called = 0;
 
 	if (called == 0) {
-		int i;
-
 		called = 1;
 		map_prom_timers();
 		kill_prom_timer();
-		for(i = 0; i < NUM_IVECS; i++)
-			ivector_to_mask[i] = 0;
+		memset(&ivector_table[0], 0, sizeof(ivector_table));
+#ifndef __SMP__
+		memset(&__up_workvec[0], 0, sizeof(__up_workvec));
+#endif
 	}
 
 	/* We need to clear any IRQ's pending in the soft interrupt
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/itlb_base.S linux/arch/sparc64/kernel/itlb_base.S
--- v2.2.3/linux/arch/sparc64/kernel/itlb_base.S	Thu Aug  6 14:06:30 1998
+++ linux/arch/sparc64/kernel/itlb_base.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: itlb_base.S,v 1.5 1998/06/15 16:59:32 jj Exp $
+/* $Id: itlb_base.S,v 1.7 1999/03/02 15:42:12 jj Exp $
  * itlb_base.S:	Front end to ITLB miss replacement strategy.
  *              This is included directly into the trap table.
  *
@@ -15,11 +15,9 @@
  * 2) All user instruction misses.
  *
  * All real page faults merge their code paths to the
- * sparc64_realfault_* labels below.
+ * sparc64_realfault_common label below.
  */
 
-	.globl		sparc64_vpte_patchme
-
 /* ITLB ** ICACHE line 1: Quick user TLB misses		*/
 	ldxa		[%g1 + %g1] ASI_IMMU, %g4	! Get TAG_ACCESS
 	srax		%g4, VPTE_SHIFT, %g6		! Create VPTE offset
@@ -42,28 +40,25 @@
 
 /* ITLB ** ICACHE line 3: Real faults			*/
 	rdpr		%tpc, %g5			! And load faulting VA
+	clr		%g4				! It was read
 sparc64_realfault_common:				! Called by TL0 dtlb_miss too
 	sethi		%hi(1f), %g7			! Save state
 	ba,pt		%xcc, etrap			! ...
 1:	 or		%g7, %lo(1b), %g7		! ...
-	clr		%o2				! It was read
-sparc64_realfault_continue:				! Called by dtlb_prot handler
+	mov		%l4, %o2			! Read/Write/No idea
 	srlx		%l5, PAGE_SHIFT, %o1		! Page align faulting VA
 	add		%sp, STACK_BIAS + REGWIN_SZ, %o0! Compute pt_regs arg
-	call		do_sparc64_fault		! Call fault handler
 
 /* ITLB ** ICACHE line 4: Call fault processing code	 */
+	call		do_sparc64_fault		! Call fault handler
 	 sllx		%o1, PAGE_SHIFT, %o1		! Finish page alignment
 	ba,a,pt		%xcc, rtrap_clr_l6		! Restore cpu state
+	nop
 winfix_trampoline:
 	rdpr		%tpc, %g3			! Prepare winfixup TNPC
 	or		%g3, 0x7c, %g3			! Compute offset to branch
 	wrpr		%g3, %tnpc			! Write it into TNPC
 	done						! Do it to it
-sparc64_vpte_nucleus:
-	ba,pt		%xcc, sparc64_vpte_continue	! Part of dtlb_backend
-sparc64_vpte_patchme:
-	 sethi		%hi(0), %g5			! This has to be patched
 
 #undef TAG_CONTEXT_BITS
 #undef VPTE_SHIFT
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- v2.2.3/linux/arch/sparc64/kernel/process.c	Sun Nov  8 14:02:46 1998
+++ linux/arch/sparc64/kernel/process.c	Sun Mar 21 18:37:56 1999
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.82 1998/10/19 21:52:23 davem Exp $
+/*  $Id: process.c,v 1.90 1999/03/22 02:12:16 davem Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -53,11 +53,17 @@
 
 	/* endless idle loop with no priority at all */
 	current->priority = 0;
-	current->counter = 0;
+	current->counter = -100;
 	for (;;) {
-		check_pgt_cache();
-		run_task_queue(&tq_scheduler);
+		/* If current->need_resched is zero we should really
+		 * setup for a system wakup event and execute a shutdown
+		 * instruction.
+		 *
+		 * But this requires writing back the contents of the
+		 * L2 cache etc. so implement this later. -DaveM
+		 */
 		schedule();
+		check_pgt_cache();
 	}
 	return 0;
 }
@@ -67,20 +73,25 @@
 /*
  * the idle loop on a UltraMultiPenguin...
  */
+#define idle_me_harder()	(cpu_data[current->processor].idle_volume += 1)
+#define unidle_me()		(cpu_data[current->processor].idle_volume = 0)
 asmlinkage int cpu_idle(void)
 {
 	current->priority = 0;
+	current->counter = -100;
 	while(1) {
-		struct task_struct *p;
-
-		check_pgt_cache();
-		run_task_queue(&tq_scheduler);
-		current->counter = 0;
-		if (current->need_resched != 0 ||
-		    ((p = init_task.next_run) != NULL &&
-		     (p->processor == smp_processor_id() ||
-		      (p->tss.flags & SPARC_FLAG_NEWCHILD) != 0)))
+		if (current->need_resched != 0) {
+			unidle_me();
 			schedule();
+			check_pgt_cache();
+		}
+		idle_me_harder();
+
+		/* The store ordering is so that IRQ handlers on
+		 * other cpus see our increasing idleness for the buddy
+		 * redistribution algorithm.  -DaveM
+		 */
+		membar("#StoreStore | #StoreLoad");
 	}
 }
 
@@ -158,12 +169,12 @@
 	}
 	rw = &r_w;
 	set_fs (old_fs);			
-	printk("l0: %016x l1: %016x l2: %016x l3: %016x\n"
-	       "l4: %016x l5: %016x l6: %016x l7: %016x\n",
+	printk("l0: %08x l1: %08x l2: %08x l3: %08x "
+	       "l4: %08x l5: %08x l6: %08x l7: %08x\n",
 	       rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
 	       rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
-	printk("i0: %016x i1: %016x i2: %016x i3: %016x\n"
-	       "i4: %016x i5: %016x i6: %016x i7: %016x\n",
+	printk("i0: %08x i1: %08x i2: %08x i3: %08x "
+	       "i4: %08x i5: %08x i6: %08x i7: %08x\n",
 	       rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
 	       rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
 }
@@ -340,13 +351,13 @@
 {
 	printk("PSR: %08x PC: %08x NPC: %08x Y: %08x\n", regs->psr,
 	       regs->pc, regs->npc, regs->y);
-	printk("g0: %08x g1: %08x g2: %08x g3: %08x\n",
+	printk("g0: %08x g1: %08x g2: %08x g3: %08x ",
 	       regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
 	       regs->u_regs[3]);
 	printk("g4: %08x g5: %08x g6: %08x g7: %08x\n",
 	       regs->u_regs[4], regs->u_regs[5], regs->u_regs[6],
 	       regs->u_regs[7]);
-	printk("o0: %08x o1: %08x o2: %08x o3: %08x\n",
+	printk("o0: %08x o1: %08x o2: %08x o3: %08x ",
 	       regs->u_regs[8], regs->u_regs[9], regs->u_regs[10],
 	       regs->u_regs[11]);
 	printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n",
@@ -427,9 +438,7 @@
 		/* exec_mmap() set context to NO_CONTEXT, here is
 		 * where we grab a new one.
 		 */
-		current->mm->cpu_vm_mask = 0;
 		activate_context(current);
-		current->mm->cpu_vm_mask = (1UL<<smp_processor_id());
 	}
 	if (current->tss.flags & SPARC_FLAG_32BIT)
 		__asm__ __volatile__("stxa %%g0, [%0] %1"
@@ -447,6 +456,11 @@
 {
 	unsigned long fp, distance, rval;
 
+	/* do_fork() grabs the parent semaphore, we must release it
+	 * temporarily so we can build the child clone stack frame
+	 * without deadlocking.
+	 */
+	up(&current->mm->mmap_sem);
 	if(!(current->tss.flags & SPARC_FLAG_32BIT)) {
 		csp += STACK_BIAS;
 		psp += STACK_BIAS;
@@ -463,17 +477,20 @@
 	distance = fp - psp;
 	rval = (csp - distance);
 	if(copy_in_user(rval, psp, distance))
-		return 0;
-	if(current->tss.flags & SPARC_FLAG_32BIT) {
+		rval = 0;
+	else if(current->tss.flags & SPARC_FLAG_32BIT) {
 		if(put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6])))
-			return 0;
-		return rval;
+			rval = 0;
 	} else {
 		if(put_user(((u64)csp - STACK_BIAS),
 			    &(((struct reg_window *)rval)->ins[6])))
-			return 0;
-		return rval - STACK_BIAS;
+			rval = 0;
+		else
+			rval = rval - STACK_BIAS;
 	}
+	down(&current->mm->mmap_sem);
+
+	return rval;
 }
 
 /* Standard stuff. */
@@ -621,6 +638,37 @@
 	/* Set the second return value for the parent. */
 	regs->u_regs[UREG_I1] = 0;
 	return 0;
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+	long retval;
+
+	__asm__ __volatile("mov %1, %%g1\n\t"
+			   "mov %2, %%o0\n\t"	   /* Clone flags. */
+			   "mov 0, %%o1\n\t"	   /* usp arg == 0 */
+			   "t 0x6d\n\t"		   /* Linux/Sparc clone(). */
+			   "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
+			   " mov %%o0, %0\n\t"
+			   "jmpl %4, %%o7\n\t"	   /* Call the function. */
+			   " mov %5, %%o0\n\t"	   /* Set arg in delay. */
+			   "mov %3, %%g1\n\t"
+			   "t 0x6d\n\t"		   /* Linux/Sparc exit(). */
+			   /* Notreached by child. */
+			   "1:" :
+			   "=r" (retval) :
+			   "i" (__NR_clone), "r" (flags | CLONE_VM),
+			   "i" (__NR_exit),  "r" (fn), "r" (arg) :
+			   "g1", "o0", "o1", "memory", "cc");
+	return retval;
 }
 
 /*
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
--- v2.2.3/linux/arch/sparc64/kernel/psycho.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/kernel/psycho.c	Fri Mar 19 07:57:45 1999
@@ -1,8 +1,9 @@
-/* $Id: psycho.c,v 1.66 1998/11/02 22:27:45 davem Exp $
+/* $Id: psycho.c,v 1.79 1999/03/19 05:38:46 davem Exp $
  * psycho.c: Ultra/AX U2P PCI controller support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
  * Copyright (C) 1998 Eddie C. Dost   (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek   (jj@ultra.linux.cz)
  */
 
 #include <linux/config.h>
@@ -29,14 +30,13 @@
 #define dprintf printk
 #endif
 
-
 unsigned long pci_dvma_offset = 0x00000000UL;
 unsigned long pci_dvma_mask = 0xffffffffUL;
 
+#define PCI_DVMA_HASH_NONE	0xffffffffffffffffUL
 unsigned long pci_dvma_v2p_hash[PCI_DVMA_HASHSZ];
 unsigned long pci_dvma_p2v_hash[PCI_DVMA_HASHSZ];
 
-
 #ifndef CONFIG_PCI
 
 int pcibios_present(void)
@@ -74,9 +74,12 @@
 #include <asm/apb.h>
 #include <asm/uaccess.h>
 
+#define PSYCHO_REORDER_ONBOARDFIRST	1
+
 struct linux_psycho *psycho_root = NULL;
 int linux_num_psycho = 0;
 static struct linux_pbm_info *bus2pbm[256];
+static int psycho_reorder __initdata = 0;
 
 static int pbm_read_config_byte(struct linux_pbm_info *pbm,
 				unsigned char bus, unsigned char devfn,
@@ -112,8 +115,10 @@
 	pci_dvma_p2v_hash[pci_dvma_ahashfn(dvma_addr)] = vaddr - dvma_addr;
 }
 
-__initfunc(static void psycho_iommu_init(struct linux_psycho *psycho, int tsbsize))
+static void __init psycho_iommu_init(struct linux_psycho *psycho, int tsbsize)
 {
+	extern int this_is_starfire;
+	extern void *starfire_hookup(int);
 	struct linux_mlist_p1275 *mlist;
 	unsigned long tsbbase;
 	unsigned long control, i, n;
@@ -137,37 +142,77 @@
 			break;
 	}
 	tsbbase = __get_free_pages(GFP_DMA, order);
+	if (!tsbbase) {
+		prom_printf("IOMMU: Error, kmalloc(tsb) failed.\n");
+		prom_halt();
+	}
 	iopte = (unsigned long *)tsbbase;
 
-	memset(pci_dvma_v2p_hash, 0, sizeof(pci_dvma_v2p_hash));
-	memset(pci_dvma_p2v_hash, 0, sizeof(pci_dvma_p2v_hash));
+	/* Initialize to "none" settings. */
+	for(i = 0; i < PCI_DVMA_HASHSZ; i++) {
+		pci_dvma_v2p_hash[i] = PCI_DVMA_HASH_NONE;
+		pci_dvma_p2v_hash[i] = PCI_DVMA_HASH_NONE;
+	}
 
 	n = 0;
 	mlist = *prom_meminfo()->p1275_totphys;
 	while (mlist) {
 		unsigned long paddr = mlist->start_adr;
+		unsigned long num_bytes = mlist->num_bytes;
+
+		if(paddr >= (((unsigned long) high_memory) - PAGE_OFFSET))
+			goto next;
+
+		if((paddr + num_bytes) >= (((unsigned long) high_memory) - PAGE_OFFSET))
+			num_bytes = (((unsigned long) high_memory) - PAGE_OFFSET) - paddr;
+
+		/* Align base and length so we map whole hash table sized chunks
+		 * at a time (and therefore full 64K IOMMU pages).
+		 */
+		paddr &= ~((1UL << 24UL) - 1);
+		num_bytes = (num_bytes + ((1UL << 24UL) - 1)) & ~((1UL << 24) - 1);
 
-		for (i = 0; i < (mlist->num_bytes >> 16); i++) {
+		/* Move up the base for mappings already created. */
+		while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] !=
+		      PCI_DVMA_HASH_NONE) {
+			paddr += (1UL << 24UL);
+			num_bytes -= (1UL << 24UL);
+			if(num_bytes == 0UL)
+				goto next;
+		}
+
+		/* Move down the size for tail mappings already created. */
+		while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr + num_bytes - (1UL << 24UL))] !=
+		      PCI_DVMA_HASH_NONE) {
+			num_bytes -= (1UL << 24UL);
+			if(num_bytes == 0UL)
+				goto next;
+		}
 
+		/* Now map the rest. */
+		for (i = 0; i < ((num_bytes + ((1 << 16) - 1)) >> 16); i++) {
 			*iopte = (IOPTE_VALID | IOPTE_64K |
 				  IOPTE_CACHE | IOPTE_WRITE);
 			*iopte |= paddr;
 
 			if (!(n & 0xff))
 				set_dvma_hash(paddr, (n << 16));
-						  
+
 			if (++n > (tsbsize * 1024))
 				goto out;
 
 			paddr += (1 << 16);
 			iopte++;
 		}
-
+	next:
 		mlist = mlist->theres_more;
 	}
 out:
-	if (mlist)
-		printk("WARNING: not all physical memory mapped in IOMMU\n");
+	if (mlist) {
+		prom_printf("WARNING: not all physical memory mapped in IOMMU\n");
+		prom_printf("Try booting with mem=xxxM or similar\n");
+		prom_halt();
+	}
 
 	psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase);
 
@@ -193,6 +238,12 @@
 		break;
 	}
 	psycho->psycho_regs->iommu_control = control;
+
+	/* If necessary, hook us up for starfire IRQ translations. */
+	if(this_is_starfire)
+		psycho->starfire_cookie = starfire_hookup(psycho->upa_portid);
+	else
+		psycho->starfire_cookie = NULL;
 }
 
 extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
@@ -201,7 +252,7 @@
 /*
  * Poor man's PCI...
  */
-__initfunc(void sabre_init(int pnode))
+void __init sabre_init(int pnode)
 {
 	struct linux_prom64_registers pr_regs[2];
 	struct linux_psycho *sabre;
@@ -213,6 +264,10 @@
 	int bus;
 
 	sabre = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC);
+	if (!sabre) {
+		prom_printf("SABRE: Error, kmalloc(sabre) failed.\n");
+		prom_halt();
+	}
 
 	portid = prom_getintdefault(pnode, "upa-portid", 0xff);
 
@@ -248,9 +303,11 @@
 		prom_halt();
 	}
 
-	printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+	printk("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n",
+	       sabre->psycho_regs, sabre->psycho_regs->control);
 #ifdef PROM_DEBUG
-	dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+	dprintf("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n",
+		sabre->psycho_regs, sabre->psycho_regs->control);
 #endif
 
 	ctrl = sabre->psycho_regs->pci_a_control;
@@ -382,7 +439,7 @@
 	return psycho->pci_bus ? 1 : 0;
 }
 
-__initfunc(void pcibios_init(void))
+void __init pcibios_init(void)
 {
 	struct linux_prom64_registers pr_regs[3];
 	struct linux_psycho *psycho;
@@ -408,8 +465,6 @@
 			goto next_pci;
 		}
 
-		psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC);
-
 		portid = prom_getintdefault(node, "upa-portid", 0xff);
 		for(search = psycho_root; search; search = search->next) {
 			if(search->upa_portid == portid) {
@@ -424,6 +479,11 @@
 			}
 		}
 
+		psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC);
+		if (!psycho) {
+			prom_printf("PSYCHO: Error, kmalloc(psycho) failed.\n");
+			prom_halt();
+		}
 		memset(psycho, 0, sizeof(*psycho));
 
 		psycho->next = psycho_root;
@@ -494,8 +554,14 @@
 		is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
 
 		/* Enable arbitration for all PCI slots. */
-		psycho->psycho_regs->pci_a_control |= 0x3f;
-		psycho->psycho_regs->pci_b_control |= 0x3f;
+		psycho->psycho_regs->pci_a_control |= PSYCHO_PCICTRL_AEN;
+		psycho->psycho_regs->pci_b_control |= PSYCHO_PCICTRL_AEN;
+
+		/* Disable DMA write / PIO rd synchronization on both
+		 * PCI bus segments.
+		 */
+		psycho->psycho_regs->pci_a_diag |= PSYCHO_PCIDIAG_DDWSYNC;
+		psycho->psycho_regs->pci_b_diag |= PSYCHO_PCIDIAG_DDWSYNC;
 
 	other_pbm:
 		if(is_pbm_a)
@@ -609,8 +675,8 @@
 }
 
 
-__initfunc(static void
-pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus))
+static void __init
+pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
 {
 	unsigned int devfn, l, class;
 	unsigned char hdr_type = 0;
@@ -657,7 +723,7 @@
 	}
 }
 
-__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus))
+static void __init pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
 {
 	unsigned int nbus;
 
@@ -682,8 +748,7 @@
 	} while (nbus--);
 }
 
-
-__initfunc(static void apb_init(struct linux_psycho *sabre))
+static void __init apb_init(struct linux_psycho *sabre)
 {
 	struct pci_dev *pdev;
 	unsigned short stmp;
@@ -692,21 +757,20 @@
 	for(pdev = pci_devices; pdev; pdev = pdev->next) {
 		if(pdev->vendor == PCI_VENDOR_ID_SUN &&
 		   pdev->device == PCI_DEVICE_ID_SUN_SABRE) {
-			/* Increase latency timer on top level bridge. */
-			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
+			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128);
 			break;
 		}
 	}
 	for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) {
 		if (pdev->vendor == PCI_VENDOR_ID_SUN &&
 		    pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
-
 			pci_read_config_word(pdev, PCI_COMMAND, &stmp);
 			stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
 				PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
 				PCI_COMMAND_IO;
 			pci_write_config_word(pdev, PCI_COMMAND, stmp);
 
+			/* Status register bits are "write 1 to clear". */
 			pci_write_config_word(pdev, PCI_STATUS, 0xffff);
 			pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
 
@@ -721,28 +785,25 @@
 			       APB_PCI_CTL_HIGH_ARBITER_EN;
 			pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp);
 
+			/* Systems with SIMBA are usually workstations, so
+			 * we configure to park to SIMBA not to the previous
+			 * bus owner.
+			 */
 			pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp);
-			itmp = APB_PCI_CTL_LOW_ARB_PARK |
-			       APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
+			itmp = APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
 			pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp);
 
-			/*
-			 * Setup Registers for Guaranteed Completion.
+			/* Don't mess with the retry limit and PIO/DMA latency
+			 * timer settings.  But do set primary and secondary
+			 * latency timers.
 			 */
-			pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0);
-			pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0);
-			pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80);
-			pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0);
-			pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80);
-			pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0);
-
-			/* Increase primary latency timer. */
-			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
+			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128);
+			pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 128);
 		}
 	}
 }
 
-__initfunc(static void sabre_probe(struct linux_psycho *sabre))
+static void __init sabre_probe(struct linux_psycho *sabre)
 {
 	struct pci_bus *pbus = sabre->pci_bus;
 	static unsigned char busno = 0;
@@ -764,7 +825,7 @@
 }
 
 
-__initfunc(static void pbm_probe(struct linux_pbm_info *pbm))
+static void __init pbm_probe(struct linux_pbm_info *pbm)
 {
 	static struct pci_bus *pchain = NULL;
 	struct pci_bus *pbus = &pbm->pci_bus;
@@ -803,9 +864,9 @@
 	}
 }
 
-__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
-						struct pci_dev *pdev,
-						int pnode))
+static int __init pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+					    struct pci_dev *pdev,
+					    int pnode)
 {
 	struct linux_prom_pci_registers pregs[PROMREG_MAX];
 	int node;
@@ -827,8 +888,8 @@
 	return 0;
 }
 
-__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm,
-					  struct pci_dev *pdev, int pnode))
+static void __init pdev_cookie_fillin(struct linux_pbm_info *pbm,
+				      struct pci_dev *pdev, int pnode)
 {
 	struct pcidev_cookie *pcp;
 	int node;
@@ -846,9 +907,9 @@
 #endif
 }
 
-__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus,
-					   struct linux_pbm_info *pbm,
-					   int node))
+static void __init fill_in_pbm_cookies(struct pci_bus *pbus,
+				       struct linux_pbm_info *pbm,
+				       int node)
 {
 	struct pci_dev *pdev;
 
@@ -868,7 +929,7 @@
 	}
 }
 
-__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre))
+static void __init sabre_cookie_fillin(struct linux_psycho *sabre)
 {
 	struct pci_bus *pbus = sabre->pci_bus;
 
@@ -886,9 +947,9 @@
  * properties, and recording them in pci_vma's linked in via
  * PBM->assignments.
  */
-__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs))
+static int __init gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
 {
-	struct linux_prom_ebus_ranges erng[PROMREG_MAX];
+	struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX];
 	int err, iter;
 
 	err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng));
@@ -911,7 +972,7 @@
 	return err;
 }
 
-__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node))
+static void __init assignment_process(struct linux_pbm_info *pbm, int node)
 {
 	struct linux_prom_pci_registers aregs[PROMREG_MAX];
 	char pname[256];
@@ -968,7 +1029,7 @@
 	}
 }
 
-__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node))
+static void __init assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
 {
 	while(node) {
 		int child = prom_getchild(node);
@@ -1077,12 +1138,12 @@
 #endif
 }
 
-__initfunc(static void fixup_regs(struct pci_dev *pdev,
-				  struct linux_pbm_info *pbm,
-				  struct linux_prom_pci_registers *pregs,
-				  int nregs,
-				  struct linux_prom_pci_registers *assigned,
-				  int numaa))
+static void __init fixup_regs(struct pci_dev *pdev,
+			      struct linux_pbm_info *pbm,
+			      struct linux_prom_pci_registers *pregs,
+			      int nregs,
+			      struct linux_prom_pci_registers *assigned,
+			      int numaa)
 {
 	int preg, rng;
 	int IO_seen = 0;
@@ -1415,7 +1476,7 @@
 #define imap_offset(__member) \
 	((unsigned long)(&(((struct psycho_regs *)0)->__member)))
 
-__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
+static unsigned long __init psycho_pcislot_imap_offset(unsigned long ino)
 {
 	unsigned int bus, slot;
 
@@ -1431,11 +1492,8 @@
 		case 2:
 			return imap_offset(imap_a_slot2);
 		case 3:
-			return imap_offset(imap_a_slot3);
 		default:
-			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
-				    bus, slot);
-			prom_halt();
+			return imap_offset(imap_a_slot3);
 		}
 	} else {
 		switch(slot) {
@@ -1446,19 +1504,16 @@
 		case 2:
 			return imap_offset(imap_b_slot2);
 		case 3:
-			return imap_offset(imap_b_slot3);
 		default:
-			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
-				    bus, slot);
-			prom_halt();
+			return imap_offset(imap_b_slot3);
 		}
 	}
 }
 
 /* Exported for EBUS probing layer. */
-__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
-					 struct pci_dev *pdev,
-					 unsigned int ino))
+unsigned int __init psycho_irq_build(struct linux_pbm_info *pbm,
+				     struct pci_dev *pdev,
+				     unsigned int ino)
 {
 	unsigned long imap_off;
 	int need_dma_sync = 0;
@@ -1554,37 +1609,78 @@
 	return psycho_build_irq(pbm->parent, imap_off, ino, need_dma_sync);
 }
 
-__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm,
-				       struct pci_dev *pdev,
-				       struct linux_prom_pci_registers *preg,
-				       unsigned int *interrupt))
+static int __init pbm_intmap_match(struct linux_pbm_info *pbm,
+				   struct pci_dev *pdev,
+				   struct linux_prom_pci_registers *preg,
+				   unsigned int *interrupt)
 {
 	struct linux_prom_pci_registers ppreg;
 	unsigned int hi, mid, lo, irq;
 	int i;
 
-	if (!pbm->num_pbm_intmap)
+#ifdef FIXUP_IRQ_DEBUG
+	dprintf("pbm_intmap_match: ");
+#endif
+	if (!pbm->num_pbm_intmap) {
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("No intmap UPA[%x:%c]\n",
+			pbm->parent->upa_portid,
+			(pbm == &pbm->parent->pbm_A) ? 'A' : 'B');
+#endif
 		return 0;
-
+	}
 	/*
 	 * Underneath a bridge, use register of parent bridge.
 	 */
 	if (pdev->bus->number != pbm->pci_first_busno) {
-		struct pcidev_cookie *pcp = pdev->bus->self->sysdata;
-		int node;
+		struct pcidev_cookie *pcp;
+		int node, offset;
+		char prom_name[64];
 
-		if (!pcp)
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("UnderBridge, ");
+#endif
+		pcp = pdev->bus->self->sysdata;
+		if (!pcp) {
+#ifdef FIXUP_IRQ_DEBUG
+			dprintf("No bus PCP\n");
+#endif
 			goto out;
-
+		}
 		node = pcp->prom_node;
 
 		i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg));
-		if(i == 0 || i == -1)
+		if(i == 0 || i == -1) {
+#ifdef FIXUP_IRQ_DEBUG
+			dprintf("No reg property.\n");
+#endif
 			goto out;
+		}
+		/*
+		 * Did PROM know better and assign an interrupt different
+		 * to #INTA to the device? - We test here for presence of
+		 * FCODE on the card, in this case we assume PROM has set
+		 * correct 'interrupts' property, unless it is quadhme.
+		 */
+		pcp = pdev->sysdata;
+		if (!pcp) {
+#ifdef FIXUP_IRQ_DEBUG
+			dprintf("No dev PCP\n");
+#endif
+			goto out;
+		}
+		node = pcp->prom_node;
 
-		/* Use low slot number bits of child as IRQ line. */
-		*interrupt = ((pdev->devfn >> 3) & 3) + 1;
-
+		offset = prom_getint(node, "fcode-rom-offset");
+		prom_getstring(node, "name", prom_name, sizeof(prom_name));
+		if (offset == -1 || 
+		    !strcmp(prom_name, "SUNW,qfe") ||
+		    !strcmp(prom_name, "qfe")) {
+			/*
+			 * No, use low slot number bits of child as IRQ line.
+			 */
+			*interrupt = ((*interrupt - 1 + PCI_SLOT(pdev->devfn)) & 3) + 1;
+		}
 		preg = &ppreg;
 	}
 
@@ -1618,10 +1714,10 @@
 	prom_halt();
 }
 
-__initfunc(static void fixup_irq(struct pci_dev *pdev,
-				 struct linux_pbm_info *pbm,
-				 struct linux_prom_pci_registers *preg,
-				 int node))
+static void __init fixup_irq(struct pci_dev *pdev,
+			     struct linux_pbm_info *pbm,
+			     struct linux_prom_pci_registers *preg,
+			     int node)
 {
 	unsigned int prom_irq, portid = pbm->parent->upa_portid;
 	unsigned char pci_irq_line = pdev->irq;
@@ -1721,11 +1817,11 @@
 #endif
 }
 
-__initfunc(static void fixup_doit(struct pci_dev *pdev,
-				  struct linux_pbm_info *pbm,
-				  struct linux_prom_pci_registers *pregs,
-				  int nregs,
-				  int node))
+static void __init fixup_doit(struct pci_dev *pdev,
+			      struct linux_pbm_info *pbm,
+			      struct linux_prom_pci_registers *pregs,
+			      int nregs,
+			      int node)
 {
 	struct linux_prom_pci_registers assigned[PROMREG_MAX];
 	int numaa, err;
@@ -1745,9 +1841,9 @@
 	fixup_irq(pdev, pbm, &pregs[0], node);
 }
 
-__initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
-				     struct pci_bus *pbus,
-				     struct linux_pbm_info *pbm))
+static void __init fixup_pci_dev(struct pci_dev *pdev,
+				 struct pci_bus *pbus,
+				 struct linux_pbm_info *pbm)
 {
 	struct linux_prom_pci_registers pregs[PROMREG_MAX];
 	struct pcidev_cookie *pcp = pdev->sysdata;
@@ -1762,8 +1858,12 @@
 		cmd |= PCI_COMMAND_MASTER;
 		pci_write_config_word(pdev, PCI_COMMAND, cmd);
 
-		/* Now, set cache line size to 64-bytes. */
-		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64);
+		/* Now, set cache line size to 64-bytes.
+		 * NOTE: Cache line size is in 32-bit word units.
+		 */
+		pci_write_config_byte(pdev,
+				      PCI_CACHE_LINE_SIZE,
+				      (64 / sizeof(u32)));
 	}
 
 	/* Ignore if this is one of the PBM's, EBUS, or a
@@ -1808,7 +1908,7 @@
 	}
 }
 
-__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm))
+static void __init fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
 {
 	struct pci_dev *pdev;
 
@@ -1819,7 +1919,7 @@
 		fixup_pci_bus(pbus, pbm);
 }
 
-__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm))
+static void __init fixup_addr_irq(struct linux_pbm_info *pbm)
 {
 	struct pci_bus *pbus = &pbm->pci_bus;
 
@@ -1832,7 +1932,7 @@
 /* Walk all PCI devices probes, fixing up base registers and IRQ registers.
  * We use OBP for most of this work.
  */
-__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho))
+static void __init psycho_final_fixup(struct linux_psycho *psycho)
 {
 	/* Second, fixup base address registers and IRQ lines... */
 	if (psycho->pbm_A.parent)
@@ -1841,7 +1941,33 @@
 		fixup_addr_irq(&psycho->pbm_B);
 }
 
-__initfunc(void pcibios_fixup(void))
+/* Reorder the pci_dev chain, so that onboard devices come first
+   and then come the pluggable cards. */
+void __init psycho_reorder_devs(void)
+{
+	struct pci_dev **pci_onboard = &pci_devices;
+	struct pci_dev **pci_tail = &pci_devices;
+	struct pci_dev *pdev = pci_devices, *pci_other = NULL;
+
+	while (pdev) {
+		if (pdev->irq && (__irq_ino(pdev->irq) & 0x20)) {
+			if (pci_other) {
+				*pci_onboard = pdev;
+				pci_onboard = &pdev->next;
+				pdev = pdev->next;
+				*pci_onboard = pci_other;
+				*pci_tail = pdev;
+				continue;
+			} else
+				pci_onboard = &pdev->next;
+		} else if (!pci_other)
+			pci_other = pdev;
+		pci_tail = &pdev->next;
+		pdev = pdev->next;
+	}
+}
+
+void __init pcibios_fixup(void)
 {
 	struct linux_psycho *psycho;
 
@@ -1861,9 +1987,9 @@
 
 	for (psycho = psycho_root; psycho; psycho = psycho->next) {
 		/* Probe bus on builtin PCI. */
-		if (apb_present(psycho))
+		if (apb_present(psycho)) {
 			sabre_probe(psycho);
-		else {
+		} else {
 			/* Probe busses under PBM B. */
 			pbm_probe(&psycho->pbm_B);
 
@@ -1896,6 +2022,9 @@
 		psycho_final_fixup(psycho);
 	}
 
+	if (psycho_reorder & PSYCHO_REORDER_ONBOARDFIRST)
+		psycho_reorder_devs();
+
 	return ebus_init();
 }
 
@@ -2418,12 +2547,20 @@
 	return err;
 }
 
-__initfunc(void pcibios_fixup_bus(struct pci_bus *bus))
+void __init pcibios_fixup_bus(struct pci_bus *bus)
 {
 }
 
-__initfunc(char *pcibios_setup(char *str))
+char * __init pcibios_setup(char *str)
 {
+	if (!strcmp(str, "onboardfirst")) {
+		psycho_reorder |= PSYCHO_REORDER_ONBOARDFIRST;
+		return NULL;
+	}
+	if (!strcmp(str, "noreorder")) {
+		psycho_reorder = 0;
+		return NULL;
+	}
 	return str;
 }
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c
--- v2.2.3/linux/arch/sparc64/kernel/ptrace.c	Sun Nov  8 14:02:46 1998
+++ linux/arch/sparc64/kernel/ptrace.c	Mon Mar 15 16:10:43 1999
@@ -591,6 +591,8 @@
 
 	if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH))
 	    || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) {
+		unsigned long flags;
+
 		if(child == current) {
 			/* Try this under SunOS/Solaris, bwa haha
 			 * You'll never be able to kill the process. ;-)
@@ -602,8 +604,9 @@
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
 		    (current->gid != child->egid) ||
-		    (current->gid != child->gid)) && 
-		   !capable(CAP_SYS_PTRACE)) {
+		    (current->gid != child->sgid) ||
+		    (cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+		    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) {
 			pt_error_return(regs, EPERM);
 			goto out;
 		}
@@ -613,15 +616,13 @@
 			goto out;
 		}
 		child->flags |= PF_PTRACED;
+		write_lock_irqsave(&tasklist_lock, flags);
 		if(child->p_pptr != current) {
-			unsigned long flags;
-
-			write_lock_irqsave(&tasklist_lock, flags);
 			REMOVE_LINKS(child);
 			child->p_pptr = current;
 			SET_LINKS(child);
-			write_unlock_irqrestore(&tasklist_lock, flags);
 		}
+		write_unlock_irqrestore(&tasklist_lock, flags);
 		send_sig(SIGSTOP, child, 1);
 		pt_succ_return(regs, 0);
 		goto out;
@@ -670,14 +671,18 @@
 				pt_error_return(regs, EINVAL);
 				goto out;
 			}
+			down(&child->mm->mmap_sem);
 			res = read_int(child, addr, &x);
+			up(&child->mm->mmap_sem);
 			tmp = x;
 		} else {
 			if(addr & (sizeof(unsigned long) - 1)) {
 				pt_error_return(regs, EINVAL);
 				goto out;
 			}
+			down(&child->mm->mmap_sem);
 			res = read_long(child, addr, &tmp);
+			up(&child->mm->mmap_sem);
 		}
 		if (res < 0) {
 			pt_error_return(regs, -res);
@@ -709,13 +714,17 @@
 				pt_error_return(regs, EINVAL);
 				goto out;
 			}
+			down(&child->mm->mmap_sem);
 			res = write_int(child, addr, data);
+			up(&child->mm->mmap_sem);
 		} else {
 			if(addr & (sizeof(unsigned long) - 1)) {
 				pt_error_return(regs, EINVAL);
 				goto out;
 			}
+			down(&child->mm->mmap_sem);
 			res = write_long(child, addr, data);
+			up(&child->mm->mmap_sem);
 		}
 		if(res < 0)
 			pt_error_return(regs, -res);
@@ -944,12 +953,15 @@
 		unsigned long page;
 
 		while(len) {
+			down(&child->mm->mmap_sem);
 			vma = find_extend_vma(child, src);
 			if (!vma) {
+				up(&child->mm->mmap_sem);
 				pt_error_return(regs, EIO);
 				goto flush_and_out;
 			}
 			pgtable = get_page (child, vma, src, 0);
+			up(&child->mm->mmap_sem);
 			if (src & ~PAGE_MASK) {
 				curlen = PAGE_SIZE - (src & ~PAGE_MASK);
 				if (curlen > len) curlen = len;
@@ -988,12 +1000,15 @@
 		unsigned long page;
 
 		while(len) {
+			down(&child->mm->mmap_sem);
 			vma = find_extend_vma(child, dest);
 			if (!vma) {
+				up(&child->mm->mmap_sem);
 				pt_error_return(regs, EIO);
 				goto flush_and_out;
 			}
 			pgtable = get_page (child, vma, dest, 1);
+			up(&child->mm->mmap_sem);
 			if (dest & ~PAGE_MASK) {
 				curlen = PAGE_SIZE - (dest & ~PAGE_MASK);
 				if (curlen > len) curlen = len;
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.2.3/linux/arch/sparc64/kernel/setup.c	Sun Nov  8 14:02:47 1998
+++ linux/arch/sparc64/kernel/setup.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.37 1998/10/14 15:49:09 ecd Exp $
+/*  $Id: setup.c,v 1.41 1999/01/04 20:12:25 davem Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -277,6 +277,22 @@
 #endif
 static unsigned long memory_size = 0;
 
+#ifdef PROM_DEBUG_CONSOLE
+static struct console prom_debug_console = {
+	"debug",
+	prom_console_write,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+#endif
+
 /* XXX Implement this at some point... */
 void kernel_enter_debugger(void)
 {
@@ -430,6 +446,10 @@
 	*cmdline_p = prom_getbootargs();
 	strcpy(saved_command_line, *cmdline_p);
 
+#ifdef PROM_DEBUG_CONSOLE
+	register_console(&prom_debug_console);
+#endif
+
 	printk("ARCH: SUN4U\n");
 
 #ifdef CONFIG_DUMMY_CONSOLE
@@ -531,7 +551,7 @@
 			ic_servaddr = sv;
 			if (gw)
 				ic_gateway = gw;
-			ic_bootp_flag = ic_rarp_flag = 0;
+			ic_proto_enabled = 0;
 		}
 	}
 #endif
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.2.3/linux/arch/sparc64/kernel/smp.c	Sun Nov  8 14:02:47 1998
+++ linux/arch/sparc64/kernel/smp.c	Mon Mar 15 16:10:43 1999
@@ -5,6 +5,8 @@
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/tasks.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -34,24 +36,23 @@
 extern void calibrate_delay(void);
 extern unsigned prom_cpu_nodes[];
 
-volatile int smp_processors_ready = 0;
-unsigned long cpu_present_map = 0;
-int smp_num_cpus = 1;
-int smp_threads_ready = 0;
+struct cpuinfo_sparc cpu_data[NR_CPUS]  __attribute__ ((aligned (64)));
 
-struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64)));
+volatile int cpu_number_map[NR_CPUS]    __attribute__ ((aligned (64)));
+volatile int __cpu_logical_map[NR_CPUS] __attribute__ ((aligned (64)));
 
-/* Please don't make this initdata!!!  --DaveM */
+/* Please don't make this stuff initdata!!!  --DaveM */
 static unsigned char boot_cpu_id = 0;
-
 static int smp_activated = 0;
 
-volatile int cpu_number_map[NR_CPUS];
-volatile int __cpu_logical_map[NR_CPUS];
-
 /* Kernel spinlock */
 spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;
 
+volatile int smp_processors_ready = 0;
+unsigned long cpu_present_map = 0;
+int smp_num_cpus = 1;
+int smp_threads_ready = 0;
+
 __initfunc(void smp_setup(char *str, int *ints))
 {
 	/* XXX implement me XXX */
@@ -84,6 +85,8 @@
 
 __initfunc(void smp_store_cpu_info(int id))
 {
+	int i;
+
 	cpu_data[id].irq_count			= 0;
 	cpu_data[id].bh_count			= 0;
 	/* multiplier and counter set by
@@ -94,16 +97,18 @@
 	cpu_data[id].pte_cache			= NULL;
 	cpu_data[id].pgdcache_size		= 0;
 	cpu_data[id].pgd_cache			= NULL;
-}
+	cpu_data[id].idle_volume		= 0;
 
-extern void distribute_irqs(void);
+	for(i = 0; i < 16; i++)
+		cpu_data[id].irq_worklists[i] = 0;
+}
 
 __initfunc(void smp_commence(void))
 {
-	distribute_irqs();
 }
 
 static void smp_setup_percpu_timer(void);
+static void smp_tune_scheduling(void);
 
 static volatile unsigned long callin_flag = 0;
 
@@ -173,10 +178,16 @@
 	panic("SMP bolixed\n");
 }
 
-extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+extern struct prom_cpuinfo linux_cpus[64];
 
 extern unsigned long smp_trampoline;
 
+/* The OBP cpu startup callback truncates the 3rd arg cookie to
+ * 32-bits (I think) so to be safe we have it read the pointer
+ * contained here so we work on >4GB machines. -DaveM
+ */
+static struct task_struct *cpu_new_task = NULL;
+
 __initfunc(void smp_boot_cpus(void))
 {
 	int cpucount = 0, i;
@@ -184,6 +195,7 @@
 	printk("Entering UltraSMPenguin Mode...\n");
 	__sti();
 	smp_store_cpu_info(boot_cpu_id);
+	smp_tune_scheduling();
 
 	if(linux_num_cpus == 1)
 		return;
@@ -194,12 +206,14 @@
 
 		if(cpu_present_map & (1UL << i)) {
 			unsigned long entry = (unsigned long)(&smp_trampoline);
+			unsigned long cookie = (unsigned long)(&cpu_new_task);
 			struct task_struct *p;
 			int timeout;
 			int no;
 			extern unsigned long phys_base;
 
 			entry += phys_base - KERNBASE;
+			cookie += phys_base - KERNBASE;
 			kernel_thread(start_secondary, NULL, CLONE_PID);
 			p = task[++cpucount];
 			p->processor = i;
@@ -207,8 +221,9 @@
 			for (no = 0; no < linux_num_cpus; no++)
 				if (linux_cpus[no].mid == i)
 					break;
+			cpu_new_task = p;
 			prom_startcpu(linux_cpus[no].prom_node,
-				      entry, ((unsigned long)p));
+				      entry, cookie);
 			for(timeout = 0; timeout < 5000000; timeout++) {
 				if(callin_flag)
 					break;
@@ -216,8 +231,8 @@
 			}
 			if(callin_flag) {
 				cpu_number_map[i] = cpucount;
-				prom_cpu_nodes[i] = linux_cpus[no].prom_node;
 				__cpu_logical_map[cpucount] = i;
+				prom_cpu_nodes[i] = linux_cpus[no].prom_node;
 			} else {
 				cpucount--;
 				printk("Processor %d is stuck.\n", i);
@@ -228,6 +243,7 @@
 			cpu_number_map[i] = -1;
 		}
 	}
+	cpu_new_task = NULL;
 	if(cpucount == 0) {
 		printk("Error: only one processor found.\n");
 		cpu_present_map = (1UL << smp_processor_id());
@@ -249,17 +265,6 @@
 	membar("#StoreStore | #StoreLoad");
 }
 
-/* We don't even need to do anything, the only generic message pass done
- * anymore is to stop all cpus during a panic().  When the user drops to
- * the PROM prompt, the firmware will send the other cpu's it's MONDO
- * vector anyways, so doing anything special here is pointless.
- *
- * This whole thing should go away anyways...
- */
-void smp_message_pass(int target, int msg, unsigned long data, int wait)
-{
-}
-
 /* #define XCALL_DEBUG */
 
 static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
@@ -642,6 +647,100 @@
 	__cpu_logical_map[0] = boot_cpu_id;
 	current->processor = boot_cpu_id;
 	prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1;
+}
+
+static inline unsigned long find_flush_base(unsigned long size)
+{
+	struct page *p = mem_map;
+	unsigned long found, base;
+
+	size = PAGE_ALIGN(size);
+	found = size;
+	base = page_address(p);
+	while(found != 0) {
+		/* Failure. */
+		if(p >= (mem_map + max_mapnr))
+			return 0UL;
+		if(PageSkip(p)) {
+			p = p->next_hash;
+			base = page_address(p);
+			found = size;
+		} else {
+			found -= PAGE_SIZE;
+			p++;
+		}
+	}
+	return base;
+}
+
+cycles_t cacheflush_time;
+
+__initfunc(static void smp_tune_scheduling (void))
+{
+	unsigned long flush_base, flags, *p;
+	unsigned int ecache_size;
+	cycles_t tick1, tick2, raw;
+
+	/* Approximate heuristic for SMP scheduling.  It is an
+	 * estimation of the time it takes to flush the L2 cache
+	 * on the local processor.
+	 *
+	 * The ia32 chooses to use the L1 cache flush time instead,
+	 * and I consider this complete nonsense.  The Ultra can service
+	 * a miss to the L1 with a hit to the L2 in 7 or 8 cycles, and
+	 * L2 misses are what create extra bus traffic (ie. the "cost"
+	 * of moving a process from one cpu to another).
+	 */
+	printk("SMP: Calibrating ecache flush... ");
+	ecache_size = prom_getintdefault(linux_cpus[0].prom_node,
+					 "ecache-size", (512 *1024));
+	flush_base = find_flush_base(ecache_size << 1);
+
+	if(flush_base != 0UL) {
+		__save_and_cli(flags);
+
+		/* Scan twice the size once just to get the TLB entries
+		 * loaded and make sure the second scan measures pure misses.
+		 */
+		for(p = (unsigned long *)flush_base;
+		    ((unsigned long)p) < (flush_base + (ecache_size<<1));
+		    p += (64 / sizeof(unsigned long)))
+			*((volatile unsigned long *)p);
+
+		/* Now the real measurement. */
+		__asm__ __volatile__("
+		b,pt	%%xcc, 1f
+		 rd	%%tick, %0
+
+		.align	64
+1:		ldx	[%2 + 0x000], %%g1
+		ldx	[%2 + 0x040], %%g2
+		ldx	[%2 + 0x080], %%g3
+		ldx	[%2 + 0x0c0], %%g5
+		add	%2, 0x100, %2
+		cmp	%2, %4
+		bne,pt	%%xcc, 1b
+		 nop
+	
+		rd	%%tick, %1"
+		: "=&r" (tick1), "=&r" (tick2), "=&r" (flush_base)
+		: "2" (flush_base), "r" (flush_base + ecache_size)
+		: "g1", "g2", "g3", "g5");
+
+		__restore_flags(flags);
+
+		raw = (tick2 - tick1);
+
+		/* Dampen it a little, considering two processes
+		 * sharing the cache and fitting.
+		 */
+		cacheflush_time = (raw - (raw >> 2));
+	} else
+		cacheflush_time = ((ecache_size << 2) +
+				   (ecache_size << 1));
+
+	printk("Using heuristic of %d cycles.\n",
+	       (int) cacheflush_time);
 }
 
 int __init setup_profiling_timer(unsigned int multiplier)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.2.3/linux/arch/sparc64/kernel/sparc64_ksyms.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Mar 15 16:10:43 1999
@@ -1,8 +1,9 @@
-/* $Id: sparc64_ksyms.c,v 1.49 1998/10/28 08:11:28 jj Exp $
+/* $Id: sparc64_ksyms.c,v 1.57 1999/03/14 20:51:28 davem Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 /* Tell string.h we don't want memcpy etc. as cpp defines */
@@ -52,8 +53,9 @@
 	short revents;
 };
 
-extern unsigned prom_cpu_nodes[NR_CPUS];
+extern unsigned prom_cpu_nodes[64];
 extern void die_if_kernel(char *str, struct pt_regs *regs);
+extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
 				unsigned long, unsigned long, unsigned long);
 void _sigpause_common (unsigned int set, struct pt_regs *);
@@ -102,6 +104,8 @@
 #endif
 #endif
 
+extern unsigned long phys_base;
+
 /* One thing to note is that the way the symbols of the mul/div
  * support routines are named is a mess, they all start with
  * a '.' which makes it a bitch to export, here is the trick:
@@ -155,6 +159,8 @@
 EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL(local_bh_count);
 #endif
+
+EXPORT_SYMBOL(ivector_table);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
 
@@ -171,6 +177,7 @@
 EXPORT_SYMBOL(mmu_release_scsi_one);
 EXPORT_SYMBOL(mmu_release_scsi_sgl);
 #if CONFIG_SBUS
+EXPORT_SYMBOL(mmu_set_sbus64);
 EXPORT_SYMBOL(SBus_chain);
 EXPORT_SYMBOL(dma_chain);
 #endif
@@ -199,6 +206,9 @@
 /* math-emu wants this */
 EXPORT_SYMBOL(die_if_kernel);
 
+/* Kernel thread creation. */
+EXPORT_SYMBOL(kernel_thread);
+
 /* prom symbols */
 EXPORT_SYMBOL(idprom);
 EXPORT_SYMBOL(prom_root_node);
@@ -214,6 +224,7 @@
 EXPORT_SYMBOL(saved_command_line);
 EXPORT_SYMBOL(prom_getname);
 EXPORT_SYMBOL(prom_feval);
+EXPORT_SYMBOL(prom_getbool);
 EXPORT_SYMBOL(prom_getstring);
 EXPORT_SYMBOL(prom_apply_sbus_ranges);
 EXPORT_SYMBOL(prom_getint);
@@ -257,7 +268,6 @@
 EXPORT_SYMBOL(prom_cpu_nodes);
 EXPORT_SYMBOL(sys_ioctl);
 EXPORT_SYMBOL(sys32_ioctl);
-EXPORT_SYMBOL(get_unmapped_area);
 EXPORT_SYMBOL(move_addr_to_kernel);
 EXPORT_SYMBOL(move_addr_to_user);
 #endif
@@ -280,6 +290,10 @@
 EXPORT_SYMBOL(__copy_from_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(__bzero_noasi);
+
+/* Various address conversion macros use this. */
+EXPORT_SYMBOL(phys_base);
+EXPORT_SYMBOL(sparc64_valid_addr_bitmap);
 
 /* No version information on this, heavily used in inline asm,
  * and will always be 'void __ret_efault(void)'.
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/starfire.c linux/arch/sparc64/kernel/starfire.c
--- v2.2.3/linux/arch/sparc64/kernel/starfire.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/starfire.c	Wed Mar 10 16:53:37 1999
@@ -0,0 +1,121 @@
+/* $Id: starfire.c,v 1.2 1998/12/09 18:53:11 davem Exp $
+ * starfire.c: Starfire/E10000 support.
+ *
+ * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <asm/page.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+
+/* A few places around the kernel check this to see if
+ * they need to call us to do things in a Starfire specific
+ * way.
+ */
+int this_is_starfire = 0;
+
+void starfire_check(void)
+{
+	int ssnode = prom_finddevice("/ssp-serial");
+
+	if(ssnode != 0 && ssnode != -1) {
+		int i;
+
+		this_is_starfire = 1;
+
+		/* Now must fixup cpu MIDs.  OBP gave us a logical
+		 * linear cpuid number, not the real upaid.
+		 */
+		for(i = 0; i < linux_num_cpus; i++) {
+			unsigned int mid = linux_cpus[i].mid;
+
+			mid = (((mid & 0x3c) << 1) |
+			       ((mid & 0x40) >> 4) |
+			       (mid & 0x3));
+
+			linux_cpus[i].mid = mid;
+		}
+	}
+}
+
+int starfire_hard_smp_processor_id(void)
+{
+	return *((unsigned int *) __va(0x1fff40000d0));
+}
+
+/* Each Starfire board has 32 registers which perform translation
+ * and delivery of traditional interrupt packets into the extended
+ * Starfire hardware format.  Essentially UPAID's now have 2 more
+ * bits than in all previous Sun5 systems.
+ */
+struct starfire_irqinfo {
+	unsigned int *imap_slots[32];
+	unsigned int *tregs[32];
+	struct starfire_irqinfo *next;
+	int upaid, hwmid;
+};
+
+static struct starfire_irqinfo *sflist = NULL;
+
+/* Beam me up Scott(McNeil)y... */
+void *starfire_hookup(int upaid)
+{
+	struct starfire_irqinfo *p;
+	unsigned long treg_base, hwmid, i;
+
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if(!p) {
+		prom_printf("starfire_hookup: No memory, this is insane.\n");
+		prom_halt();
+	}
+	treg_base = 0x100fc000000UL;
+	hwmid = ((upaid & 0x3c) << 1) |
+		((upaid & 0x40) >> 4) |
+		(upaid & 0x3);
+	p->hwmid = hwmid;
+	treg_base += (hwmid << 33UL);
+	treg_base += 0x200UL;
+	for(i = 0; i < 32; i++) {
+		p->imap_slots[i] = NULL;
+		p->tregs[i] = __va(treg_base + (i * 0x10));
+	}
+	p->upaid = upaid;
+	p->next = sflist;
+	sflist = p;
+
+	return (void *) p;
+}
+
+unsigned int starfire_translate(unsigned int *imap,
+				unsigned int upaid)
+{
+	struct starfire_irqinfo *p;
+	unsigned int bus_hwmid;
+	unsigned int i;
+
+	bus_hwmid = (((unsigned long)imap) >> 33) & 0x7f;
+	for(p = sflist; p != NULL; p = p->next)
+		if(p->hwmid == bus_hwmid)
+			break;
+	if(p == NULL) {
+		prom_printf("XFIRE: Cannot find irqinfo for imap %016lx\n",
+			    ((unsigned long)imap));
+		prom_halt();
+	}
+	for(i = 0; i < 32; i++) {
+		if(p->imap_slots[i] == imap ||
+		   p->imap_slots[i] == NULL)
+			break;
+	}
+	if(i == 32) {
+		printk("starfire_translate: Are you kidding me?\n");
+		panic("Lucy in the sky....");
+	}
+	p->imap_slots[i] = imap;
+	*(p->tregs[i]) = upaid;
+
+	return i;
+}
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- v2.2.3/linux/arch/sparc64/kernel/sys_sparc.c	Sun Nov  8 14:02:47 1998
+++ linux/arch/sparc64/kernel/sys_sparc.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.25 1998/10/21 03:21:15 davem Exp $
+/* $Id: sys_sparc.c,v 1.26 1999/01/07 19:07:01 jj Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -27,6 +27,8 @@
 #include <asm/utrap.h>
 #include <asm/perfctr.h>
 
+/* #define DEBUG_UNIMP_SYSCALL */
+
 /* XXX Make this per-binary type, this way we can detect the type of
  * XXX a binary.  Every Sparc executable calls this very early on.
  */
@@ -200,11 +202,14 @@
 c_sys_nis_syscall (struct pt_regs *regs)
 {
 	static int count=0;
+	
+	/* Don't make the system unusable, if someone goes stuck */
+	if (count++ > 5) return -ENOSYS;
 	lock_kernel();
-	if (++count <= 20) { /* Don't make the system unusable, if someone goes stuck */
-		printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]);
-		show_regs (regs);
-	}
+	printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]);
+#ifdef DEBUG_UNIMP_SYSCALL	
+	show_regs (regs);
+#endif
 	unlock_kernel();
 	return -ENOSYS;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.2.3/linux/arch/sparc64/kernel/sys_sparc32.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.100 1998/11/08 11:14:00 davem Exp $
+/* $Id: sys_sparc32.c,v 1.107 1999/03/05 13:21:02 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -43,6 +43,7 @@
 #include <linux/poll.h>
 #include <linux/personality.h>
 #include <linux/stat.h>
+#include <linux/timex.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -50,6 +51,8 @@
 #include <asm/fpumacro.h>
 #include <asm/semaphore.h>
 
+#include <net/scm.h>
+
 /* Use this to get at 32-bit user passed pointers. */
 /* Things to consider: the low-level assembly stub does
    srl x, 0, x for first four arguments, so if you have
@@ -74,15 +77,6 @@
 	__ret;				\
 })
 
-static inline char * get_page(void)
-{
-	char * res;
-	res = (char *)__get_free_page(GFP_KERNEL);
-	return res;
-}
-
-#define putname32 putname
-
 /* In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
  * kernel data space before using them..
@@ -109,13 +103,13 @@
 	char *tmp, *result;
 
 	result = ERR_PTR(-ENOMEM);
-	tmp = get_page();
+	tmp = (char *)__get_free_page(GFP_KERNEL);
 	if (tmp)  {
 		int retval = do_getname32(filename, tmp);
 
 		result = tmp;
 		if (retval < 0) {
-			putname32(tmp);
+			putname(tmp);
 			result = ERR_PTR(retval);
 		}
 	}
@@ -243,7 +237,10 @@
 	err = -EFAULT;
 	if (get_user (pad, (u32 *)uptr))
 		goto out;
-	fourth.__pad = (void *)A(pad);
+	if(third == SETVAL)
+		fourth.val = (int)pad;
+	else
+		fourth.__pad = (void *)A(pad);
 	if (IPCOP_MASK (third) &
 	    (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) |
 	     IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) |
@@ -652,7 +649,7 @@
 	set_fs (KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs (old_fs);
-	putname32 (spec);
+	putname (spec);
 	if (cmds == Q_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
 		((struct dqblk32 *)&d)->dqb_itime = i;
@@ -696,7 +693,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_statfs((const char *)pth, &s);
 		set_fs (old_fs);
-		putname32 (pth);
+		putname (pth);
 		if (put_statfs(buf, &s))
 			return -EFAULT;
 	}
@@ -744,7 +741,7 @@
 		set_fs (KERNEL_DS); 
 		ret = sys_utime(filenam, &t);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 	}
 	return ret;
 }
@@ -796,8 +793,9 @@
 	}
 
 	inode = file->f_dentry->d_inode;
-	retval = locks_verify_area((type == VERIFY_READ) ?
-				   FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+	/* VERIFY_WRITE actually means a read, as we write to user space */
+	retval = locks_verify_area((type == VERIFY_WRITE
+				    ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
 				   inode, file, file->f_pos, tot_len);
 	if (retval) {
 		if (iov != iovstack)
@@ -1106,13 +1104,17 @@
 		__put_user(*fdset, ufdset);
 }
 
+#define MAX_SELECT_SECONDS \
+	((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
+
 asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x)
 {
-	fd_set_buffer *fds;
+	fd_set_bits fds;
 	struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x);
+	char *bits;
 	unsigned long nn;
 	long timeout;
-	int ret;
+	int ret, size;
 
 	timeout = MAX_SCHEDULE_TIMEOUT;
 	if (tvp) {
@@ -1123,30 +1125,47 @@
 		    || (ret = __get_user(usec, &tvp->tv_usec)))
 			goto out_nofds;
 
-		timeout = (usec + 1000000/HZ - 1) / (1000000/HZ);
-		timeout += sec * HZ;
+		ret = -EINVAL;
+		if(sec < 0 || usec < 0)
+			goto out_nofds;
+
+		if ((unsigned long) sec < MAX_SELECT_SECONDS) {
+			timeout = (usec + 1000000/HZ - 1) / (1000000/HZ);
+			timeout += sec * (unsigned long) HZ;
+		}
 	}
 
+	ret = -EINVAL;
+	if (n < 0 || n > KFDS_NR)
+		goto out_nofds;
+
+	/*
+	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+	 * since we used fdset we need to allocate memory in units of
+	 * long-words. 
+	 */
 	ret = -ENOMEM;
-	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
-	if (!fds)
+	size = FDS_BYTES(n);
+	bits = kmalloc(6 * size, GFP_KERNEL);
+	if (!bits)
 		goto out_nofds;
-	ret = -EINVAL;
-	if (n < 0)
-		goto out;
-	if (n > KFDS_NR)
-		n = KFDS_NR;
+	fds.in      = (unsigned long *)  bits;
+	fds.out     = (unsigned long *) (bits +   size);
+	fds.ex      = (unsigned long *) (bits + 2*size);
+	fds.res_in  = (unsigned long *) (bits + 3*size);
+	fds.res_out = (unsigned long *) (bits + 4*size);
+	fds.res_ex  = (unsigned long *) (bits + 5*size);
 
 	nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32));
-	if ((ret = get_fd_set32(nn, fds->in, inp)) ||
-	    (ret = get_fd_set32(nn, fds->out, outp)) ||
-	    (ret = get_fd_set32(nn, fds->ex, exp)))
+	if ((ret = get_fd_set32(nn, fds.in, inp)) ||
+	    (ret = get_fd_set32(nn, fds.out, outp)) ||
+	    (ret = get_fd_set32(nn, fds.ex, exp)))
 		goto out;
-	zero_fd_set(n, fds->res_in);
-	zero_fd_set(n, fds->res_out);
-	zero_fd_set(n, fds->res_ex);
+	zero_fd_set(n, fds.res_in);
+	zero_fd_set(n, fds.res_out);
+	zero_fd_set(n, fds.res_ex);
 
-	ret = do_select(n, fds, &timeout);
+	ret = do_select(n, &fds, &timeout);
 
 	if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
 		time_t sec = 0, usec = 0;
@@ -1168,12 +1187,12 @@
 		ret = 0;
 	}
 
-	set_fd_set32(nn, inp, fds->res_in);
-	set_fd_set32(nn, outp, fds->res_out);
-	set_fd_set32(nn, exp, fds->res_ex);
+	set_fd_set32(nn, inp, fds.res_in);
+	set_fd_set32(nn, outp, fds.res_out);
+	set_fd_set32(nn, exp, fds.res_ex);
 
 out:
-	free_page ((unsigned long)fds);
+	kfree(bits);
 out_nofds:
 	return ret;
 }
@@ -1213,7 +1232,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat (statbuf, &s))
 			return -EFAULT;
 	}
@@ -1235,7 +1254,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newlstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat (statbuf, &s))
 			return -EFAULT;
 	}
@@ -2010,74 +2029,6 @@
 	return ret;
 }
 
-struct timex32 {
-	unsigned int modes;
-	s32 offset;
-	s32 freq;
-	s32 maxerror;
-	s32 esterror;
-	int status;
-	s32 constant;
-	s32 precision;
-	s32 tolerance;
-	struct timeval32 time;
-	s32 tick;
-	s32 ppsfreq;
-	s32 jitter;
-	int shift;
-	s32 stabil;
-	s32 jitcnt;
-	s32 calcnt;
-	s32 errcnt;
-	s32 stbcnt;
-	int  :32; int  :32; int  :32; int  :32;
-	int  :32; int  :32; int  :32; int  :32;
-	int  :32; int  :32; int  :32; int  :32;
-};
-
-extern int do_adjtimex(struct timex *);
-
-asmlinkage int sys32_adjtimex(struct timex32 *txc_p)
-{
-	struct timex t;
-	int ret;
-
-	ret = get_user (t.modes, &txc_p->modes);
-	ret |= __get_user (t.offset, &txc_p->offset);
-	ret |= __get_user (t.freq, &txc_p->freq);
-	ret |= __get_user (t.maxerror, &txc_p->maxerror);
-	ret |= __get_user (t.esterror, &txc_p->esterror);
-	ret |= __get_user (t.status, &txc_p->status);
-	ret |= __get_user (t.constant, &txc_p->constant);
-	ret |= __get_user (t.tick, &txc_p->tick);
-	ret |= __get_user (t.shift, &txc_p->shift);
-	if (ret || (ret = do_adjtimex(&t)))
-		return ret;
-	ret = __put_user (t.modes, &txc_p->modes);
-	ret |= __put_user (t.offset, &txc_p->offset);
-	ret |= __put_user (t.freq, &txc_p->freq);
-	ret |= __put_user (t.maxerror, &txc_p->maxerror);
-	ret |= __put_user (t.esterror, &txc_p->esterror);
-	ret |= __put_user (t.status, &txc_p->status);
-	ret |= __put_user (t.constant, &txc_p->constant);
-	ret |= __put_user (t.precision, &txc_p->precision);
-	ret |= __put_user (t.tolerance, &txc_p->tolerance);
-	ret |= __put_user (t.time.tv_sec, &txc_p->time.tv_sec);
-	ret |= __put_user (t.time.tv_usec, &txc_p->time.tv_usec);
-	ret |= __put_user (t.tick, &txc_p->tick);
-	ret |= __put_user (t.ppsfreq, &txc_p->ppsfreq);
-	ret |= __put_user (t.jitter, &txc_p->jitter);
-	ret |= __put_user (t.shift, &txc_p->shift);
-	ret |= __put_user (t.stabil, &txc_p->stabil);
-	ret |= __put_user (t.jitcnt, &txc_p->jitcnt);
-	ret |= __put_user (t.calcnt, &txc_p->calcnt);
-	ret |= __put_user (t.errcnt, &txc_p->errcnt);
-	ret |= __put_user (t.stbcnt, &txc_p->stbcnt);
-	if (!ret)
-		ret = time_state;
-	return ret;
-}
-
 /* XXX This really belongs in some header file... -DaveM */
 #define MAX_SOCK_ADDR	128		/* 108 for Unix domain - 
 					   16 for IP, 16 for IPX,
@@ -2131,9 +2082,44 @@
         __kernel_size_t32 cmsg_len;
         int               cmsg_level;
         int               cmsg_type;
-        unsigned char     cmsg_data[0];
 };
 
+/* Bleech... */
+#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen))
+#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen))
+
+#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) )
+
+#define CMSG32_DATA(cmsg)	((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32))))
+#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len))
+#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len))
+
+#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \
+				    (struct cmsghdr32 *)(ctl) : \
+				    (struct cmsghdr32 *)NULL)
+#define CMSG32_FIRSTHDR(msg)	__CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen)
+
+__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size,
+					      struct cmsghdr32 *__cmsg, int __cmsg_len)
+{
+	struct cmsghdr32 * __ptr;
+
+	__ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) +
+				     CMSG32_ALIGN(__cmsg_len));
+	if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
+		return NULL;
+
+	return __ptr;
+}
+
+__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg,
+					    struct cmsghdr32 *__cmsg,
+					    int __cmsg_len)
+{
+	return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen,
+			       __cmsg, __cmsg_len);
+}
+
 static inline int iov_from_user32_to_kern(struct iovec *kiov,
 					  struct iovec32 *uiov32,
 					  int niov)
@@ -2175,6 +2161,7 @@
 	kmsg->msg_control = (void *)A(tmp3);
 
 	err = get_user(kmsg->msg_namelen, &umsg->msg_namelen);
+	err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen);
 	err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen);
 	err |= get_user(kmsg->msg_flags, &umsg->msg_flags);
 	
@@ -2217,6 +2204,165 @@
 	return tot_len;
 }
 
+/* There is a lot of hair here because the alignment rules (and
+ * thus placement) of cmsg headers and length are different for
+ * 32-bit apps.  -DaveM
+ */
+static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg,
+				       unsigned char *stackbuf, int stackbuf_size)
+{
+	struct cmsghdr32 *ucmsg;
+	struct cmsghdr *kcmsg, *kcmsg_base;
+	__kernel_size_t32 ucmlen;
+	__kernel_size_t kcmlen, tmp;
+
+	kcmlen = 0;
+	kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
+	ucmsg = CMSG32_FIRSTHDR(kmsg);
+	while(ucmsg != NULL) {
+		if(get_user(ucmlen, &ucmsg->cmsg_len))
+			return -EFAULT;
+
+		/* Catch bogons. */
+		if(CMSG32_ALIGN(ucmlen) <
+		   CMSG32_ALIGN(sizeof(struct cmsghdr32)))
+			return -EINVAL;
+		if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control)
+				   + ucmlen) > kmsg->msg_controllen)
+			return -EINVAL;
+
+		tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) +
+		       CMSG_ALIGN(sizeof(struct cmsghdr)));
+		kcmlen += tmp;
+		ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen);
+	}
+	if(kcmlen == 0)
+		return -EINVAL;
+
+	/* The kcmlen holds the 64-bit version of the control length.
+	 * It may not be modified as we do not stick it into the kmsg
+	 * until we have successfully copied over all of the data
+	 * from the user.
+	 */
+	if(kcmlen > stackbuf_size)
+		kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL);
+	if(kcmsg == NULL)
+		return -ENOBUFS;
+
+	/* Now copy them over neatly. */
+	memset(kcmsg, 0, kcmlen);
+	ucmsg = CMSG32_FIRSTHDR(kmsg);
+	while(ucmsg != NULL) {
+		__get_user(ucmlen, &ucmsg->cmsg_len);
+		tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) +
+		       CMSG_ALIGN(sizeof(struct cmsghdr)));
+		kcmsg->cmsg_len = tmp;
+		__get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level);
+		__get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type);
+
+		/* Copy over the data. */
+		if(copy_from_user(CMSG_DATA(kcmsg),
+				  CMSG32_DATA(ucmsg),
+				  (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg)))))
+			goto out_free_efault;
+
+		/* Advance. */
+		kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp));
+		ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen);
+	}
+
+	/* Ok, looks like we made it.  Hook it up and return success. */
+	kmsg->msg_control = kcmsg_base;
+	kmsg->msg_controllen = kcmlen;
+	return 0;
+
+out_free_efault:
+	if(kcmsg_base != (struct cmsghdr *)stackbuf)
+		kfree(kcmsg_base);
+	return -EFAULT;
+}
+
+static void put_cmsg32(struct msghdr *kmsg, int level, int type,
+		       int len, void *data)
+{
+	struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control;
+	struct cmsghdr32 cmhdr;
+	int cmlen = CMSG32_LEN(len);
+
+	if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
+		kmsg->msg_flags |= MSG_CTRUNC;
+		return;
+	}
+
+	if(kmsg->msg_controllen < cmlen) {
+		kmsg->msg_flags |= MSG_CTRUNC;
+		cmlen = kmsg->msg_controllen;
+	}
+	cmhdr.cmsg_level = level;
+	cmhdr.cmsg_type = type;
+	cmhdr.cmsg_len = cmlen;
+
+	if(copy_to_user(cm, &cmhdr, sizeof cmhdr))
+		return;
+	if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32)))
+		return;
+	cmlen = CMSG32_SPACE(len);
+	kmsg->msg_control += cmlen;
+	kmsg->msg_controllen -= cmlen;
+}
+
+static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm)
+{
+	struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control;
+	int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int);
+	int fdnum = scm->fp->count;
+	struct file **fp = scm->fp->fp;
+	int *cmfptr;
+	int err = 0, i;
+
+	if (fdnum < fdmax)
+		fdmax = fdnum;
+
+	for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) {
+		int new_fd;
+		err = get_unused_fd();
+		if (err < 0)
+			break;
+		new_fd = err;
+		err = put_user(new_fd, cmfptr);
+		if (err) {
+			put_unused_fd(new_fd);
+			break;
+		}
+		/* Bump the usage count and install the file. */
+		fp[i]->f_count++;
+		current->files->fd[new_fd] = fp[i];
+	}
+
+	if (i > 0) {
+		int cmlen = CMSG32_LEN(i * sizeof(int));
+		if (!err)
+			err = put_user(SOL_SOCKET, &cm->cmsg_level);
+		if (!err)
+			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
+		if (!err)
+			err = put_user(cmlen, &cm->cmsg_len);
+		if (!err) {
+			cmlen = CMSG32_SPACE(i * sizeof(int));
+			kmsg->msg_control += cmlen;
+			kmsg->msg_controllen -= cmlen;
+		}
+	}
+	if (i < fdnum)
+		kmsg->msg_flags |= MSG_CTRUNC;
+
+	/*
+	 * All of the files that fit in the message have had their
+	 * usage counts incremented, so we just free the list.
+	 */
+	__scm_destroy(scm);
+}
+
 asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags)
 {
 	struct socket *sock;
@@ -2237,25 +2383,10 @@
 	total_len = err;
 
 	if(kern_msg.msg_controllen) {
-		struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control;
-		unsigned long *kcmsg;
-		__kernel_size_t32 cmlen;
-
-		if(kern_msg.msg_controllen > sizeof(ctl) &&
-		   kern_msg.msg_controllen <= 256) {
-			err = -ENOBUFS;
-			ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL);
-			if(!ctl_buf)
-				goto out_freeiov;
-		}
-		__get_user(cmlen, &ucmsg->cmsg_len);
-		kcmsg = (unsigned long *) ctl_buf;
-		*kcmsg++ = (unsigned long)cmlen;
-		err = -EFAULT;
-		if(copy_from_user(kcmsg, &ucmsg->cmsg_level,
-				  kern_msg.msg_controllen - sizeof(__kernel_size_t32)))
-			goto out_freectl;
-		kern_msg.msg_control = ctl_buf;
+		err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl));
+		if(err)
+			goto out_freeiov;
+		ctl_buf = kern_msg.msg_control;
 	}
 	kern_msg.msg_flags = user_flags;
 
@@ -2269,7 +2400,6 @@
 	}
 	unlock_kernel();
 
-out_freectl:
 	/* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
 	if(ctl_buf != ctl)
 		kfree(ctl_buf);
@@ -2310,26 +2440,43 @@
 	lock_kernel();
 	sock = sockfd_lookup(fd, &err);
 	if (sock != NULL) {
+		struct scm_cookie scm;
+
 		if (sock->file->f_flags & O_NONBLOCK)
 			user_flags |= MSG_DONTWAIT;
-		err = sock_recvmsg(sock, &kern_msg, total_len, user_flags);
-		if(err >= 0)
+		memset(&scm, 0, sizeof(scm));
+		err = sock->ops->recvmsg(sock, &kern_msg, total_len,
+					 user_flags, &scm);
+		if(err >= 0) {
 			len = err;
+			if(!kern_msg.msg_control) {
+				if(sock->passcred || scm.fp)
+					kern_msg.msg_flags |= MSG_CTRUNC;
+				if(scm.fp)
+					__scm_destroy(&scm);
+			} else {
+				/* Wheee... */
+				if(sock->passcred)
+					put_cmsg32(&kern_msg,
+						   SOL_SOCKET, SCM_CREDENTIALS,
+						   sizeof(scm.creds), &scm.creds);
+				if(scm.fp != NULL)
+					scm_detach_fds32(&kern_msg, &scm);
+			}
+		}
 		sockfd_put(sock);
 	}
 	unlock_kernel();
 
 	if(uaddr != NULL && err >= 0)
 		err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
-	if(err >= 0) {
-		err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
-		if(!err) {
-			/* XXX Convert cmsg back into userspace 32-bit format... */
-			err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr,
-					 &user_msg->msg_controllen);
-		}
+	if(cmsg_ptr != 0 && err >= 0) {
+		u32 ucmsg_ptr = ((u32)(unsigned long)kern_msg.msg_control);
+		err  = __put_user(ucmsg_ptr, &user_msg->msg_control);
+		err |= __put_user(kern_msg.msg_controllen, &user_msg->msg_controllen);
 	}
-
+	if(err >= 0)
+		err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
 	if(kern_msg.msg_iov != iov)
 		kfree(kern_msg.msg_iov);
 out:
@@ -2653,7 +2800,7 @@
         error = do_execve32(filename,
         	(u32 *)AA((u32)regs->u_regs[base + UREG_I1]),
         	(u32 *)AA((u32)regs->u_regs[base + UREG_I2]), regs);
-        putname32(filename);
+        putname(filename);
 
 	if(!error) {
 		fprs_write(0);
@@ -2943,8 +3090,10 @@
 		info.addr = (unsigned long)mod;
 		info.size = mod->size;
 		info.flags = mod->flags;
-		info.usecount = (mod_member_present(mod, can_unload)
-				 && mod->can_unload ? -1 : mod->usecount);
+		info.usecount =
+			((mod_member_present(mod, can_unload)
+			  && mod->can_unload)
+			 ? -1 : atomic_read(&mod->uc.usecount));
 
 		if (copy_to_user(buf, &info, sizeof(struct module_info32)))
 			return -EFAULT;
@@ -3452,7 +3601,7 @@
 		ret = sys_utimes(kfilename, &ktvs[0]);
 		set_fs(old_fs);
 
-		putname32(kfilename);
+		putname(kfilename);
 	}
 	return ret;
 }
@@ -3575,5 +3724,78 @@
 	if (!ret && offset && put_user(of, offset))
 		return -EFAULT;
 		
+	return ret;
+}
+
+/* Handle adjtimex compatability. */
+
+struct timex32 {
+	u32 modes;
+	s32 offset, freq, maxerror, esterror;
+	s32 status, constant, precision, tolerance;
+	struct timeval32 time;
+	s32 tick;
+	s32 ppsfreq, jitter, shift, stabil;
+	s32 jitcnt, calcnt, errcnt, stbcnt;
+	s32  :32; s32  :32; s32  :32; s32  :32;
+	s32  :32; s32  :32; s32  :32; s32  :32;
+	s32  :32; s32  :32; s32  :32; s32  :32;
+};
+
+extern int do_adjtimex(struct timex *);
+
+asmlinkage int sys32_adjtimex(struct timex32 *utp)
+{
+	struct timex txc;
+	int ret;
+
+	memset(&txc, 0, sizeof(struct timex));
+
+	if(get_user(txc.modes, &utp->modes) ||
+	   __get_user(txc.offset, &utp->offset) ||
+	   __get_user(txc.freq, &utp->freq) ||
+	   __get_user(txc.maxerror, &utp->maxerror) ||
+	   __get_user(txc.esterror, &utp->esterror) ||
+	   __get_user(txc.status, &utp->status) ||
+	   __get_user(txc.constant, &utp->constant) ||
+	   __get_user(txc.precision, &utp->precision) ||
+	   __get_user(txc.tolerance, &utp->tolerance) ||
+	   __get_user(txc.time.tv_sec, &utp->time.tv_sec) ||
+	   __get_user(txc.time.tv_usec, &utp->time.tv_usec) ||
+	   __get_user(txc.tick, &utp->tick) ||
+	   __get_user(txc.ppsfreq, &utp->ppsfreq) ||
+	   __get_user(txc.jitter, &utp->jitter) ||
+	   __get_user(txc.shift, &utp->shift) ||
+	   __get_user(txc.stabil, &utp->stabil) ||
+	   __get_user(txc.jitcnt, &utp->jitcnt) ||
+	   __get_user(txc.calcnt, &utp->calcnt) ||
+	   __get_user(txc.errcnt, &utp->errcnt) ||
+	   __get_user(txc.stbcnt, &utp->stbcnt))
+		return -EFAULT;
+
+	ret = do_adjtimex(&txc);
+
+	if(put_user(txc.modes, &utp->modes) ||
+	   __put_user(txc.offset, &utp->offset) ||
+	   __put_user(txc.freq, &utp->freq) ||
+	   __put_user(txc.maxerror, &utp->maxerror) ||
+	   __put_user(txc.esterror, &utp->esterror) ||
+	   __put_user(txc.status, &utp->status) ||
+	   __put_user(txc.constant, &utp->constant) ||
+	   __put_user(txc.precision, &utp->precision) ||
+	   __put_user(txc.tolerance, &utp->tolerance) ||
+	   __put_user(txc.time.tv_sec, &utp->time.tv_sec) ||
+	   __put_user(txc.time.tv_usec, &utp->time.tv_usec) ||
+	   __put_user(txc.tick, &utp->tick) ||
+	   __put_user(txc.ppsfreq, &utp->ppsfreq) ||
+	   __put_user(txc.jitter, &utp->jitter) ||
+	   __put_user(txc.shift, &utp->shift) ||
+	   __put_user(txc.stabil, &utp->stabil) ||
+	   __put_user(txc.jitcnt, &utp->jitcnt) ||
+	   __put_user(txc.calcnt, &utp->calcnt) ||
+	   __put_user(txc.errcnt, &utp->errcnt) ||
+	   __put_user(txc.stbcnt, &utp->stbcnt))
+		ret = -EFAULT;
+
 	return ret;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.2.3/linux/arch/sparc64/kernel/systbls.S	Sun Nov  8 14:02:48 1998
+++ linux/arch/sparc64/kernel/systbls.S	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.50 1998/10/07 01:27:27 davem Exp $
+/* $Id: systbls.S,v 1.52 1999/03/20 22:02:05 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c
--- v2.2.3/linux/arch/sparc64/kernel/time.c	Wed Jan 20 23:14:04 1999
+++ linux/arch/sparc64/kernel/time.c	Mon Mar 15 16:10:43 1999
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.16 1998/09/05 17:25:28 jj Exp $
+/* $Id: time.c,v 1.20 1999/03/15 22:13:40 davem Exp $
  * time.c: UltraSparc timer and TOD clock support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -31,6 +31,8 @@
 #include <asm/pbm.h>
 #include <asm/ebus.h>
 
+extern rwlock_t xtime_lock;
+
 struct mostek48t02 *mstk48t02_regs = 0;
 static struct mostek48t08 *mstk48t08_regs = 0;
 static struct mostek48t59 *mstk48t59_regs = 0;
@@ -69,6 +71,8 @@
 {
 	unsigned long ticks;
 
+	write_lock(&xtime_lock);
+
 	do {
 		do_timer(regs);
 
@@ -82,11 +86,15 @@
 	} while (ticks >= timer_tick_compare);
 
 	timer_check_rtc();
+
+	write_unlock(&xtime_lock);
 }
 
 #ifdef __SMP__
 void timer_tick_interrupt(struct pt_regs *regs)
 {
+	write_lock(&xtime_lock);
+
 	do_timer(regs);
 
 	/*
@@ -99,6 +107,8 @@
 		: "r" (timer_tick_offset));
 
 	timer_check_rtc();
+
+	write_unlock(&xtime_lock);
 }
 #endif
 
@@ -256,13 +266,17 @@
 	node = prom_getchild(busnd);
 
 	while(1) {
-		prom_getstring(node, "model", model, sizeof(model));
+		if (!node)
+			model[0] = 0;
+		else
+			prom_getstring(node, "model", model, sizeof(model));
 		if(strcmp(model, "mk48t02") &&
 		   strcmp(model, "mk48t08") &&
 		   strcmp(model, "mk48t59")) {
-			node = prom_getsibling(node);
+		   	if (node)
+				node = prom_getsibling(node);
 #ifdef CONFIG_PCI
-			if ((node == 0) && ebus) {
+			while ((node == 0) && ebus) {
 				ebus = ebus->next;
 				if (ebus) {
 					busnd = ebus->prom_node;
@@ -397,6 +411,9 @@
 	return ticks / timer_ticks_per_usec;
 }
 
+/* This need not obtain the xtime_lock as it is coded in
+ * an implicitly SMP safe way already.
+ */
 void do_gettimeofday(struct timeval *tv)
 {
 	/* Load doubles must be used on xtime so that what we get
@@ -450,7 +467,7 @@
 
 void do_settimeofday(struct timeval *tv)
 {
-	cli();
+	write_lock_irq(&xtime_lock);
 
 	tv->tv_usec -= do_gettimeoffset();
 	if(tv->tv_usec < 0) {
@@ -461,10 +478,10 @@
 	xtime = *tv;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti();
+
+	write_unlock_irq(&xtime_lock);
 }
 
 static int set_rtc_mmss(unsigned long nowtime)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S
--- v2.2.3/linux/arch/sparc64/kernel/trampoline.S	Sun Nov  8 14:02:48 1998
+++ linux/arch/sparc64/kernel/trampoline.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.6 1998/10/11 06:58:23 davem Exp $
+/* $Id: trampoline.S,v 1.8 1998/12/09 21:01:15 davem Exp $
  * trampoline.S: Jump start slave processors on sparc64.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -34,8 +34,8 @@
 	sllx	%g4, 32, %g4
 
 	/* XXX Buggy PROM... */
-	srl	%o0, 0, %g6
-	add	%g6, %g4, %g6
+	srl	%o0, 0, %o0
+	ldx	[%o0], %g6
 
 	sethi	%uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
 	sllx	%g5, 32, %g5
@@ -197,10 +197,18 @@
 #undef KERN_LOWBITS
 #undef VPTE_BASE
 
+	/* Setup interrupt globals, we are always SMP. */
 	wrpr	%o1, (PSTATE_IG | PSTATE_IE), %pstate
-	sethi	%hi(ivector_to_mask), %g5
-	or	%g5, %lo(ivector_to_mask), %g1
-	mov	0x40, %g2
+
+	/* Get our UPA MID. */
+	lduw	[%o2 + AOFF_task_processor], %g1
+	sethi	%hi(cpu_data), %g5
+	or	%g5, %lo(cpu_data), %g5
+
+	/* In theory this is: &(cpu_data[this_upamid].irq_worklists[0]) */
+	sllx	%g1, 7, %g1
+	add	%g5, %g1, %g1
+	add	%g1, 64, %g1
 
 	wrpr	%g0, 0, %wstate
 	or	%o1, PSTATE_IE, %o1
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.2.3/linux/arch/sparc64/kernel/traps.c	Sun Nov  8 14:02:48 1998
+++ linux/arch/sparc64/kernel/traps.c	Wed Mar 10 16:53:37 1999
@@ -1,8 +1,8 @@
-/* $Id: traps.c,v 1.55 1998/10/11 06:58:22 davem Exp $
+/* $Id: traps.c,v 1.57 1999/03/02 15:42:18 jj Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 /*
@@ -462,6 +462,9 @@
 
 void die_if_kernel(char *str, struct pt_regs *regs)
 {
+	extern void __show_regs(struct pt_regs * regs);
+	extern void smp_report_regs(void);
+	
 	/* Amuse the user. */
 	printk(
 "              \\|/ ____ \\|/\n"
@@ -471,7 +474,7 @@
 
 	printk("%s(%d): %s\n", current->comm, current->pid, str);
 	__asm__ __volatile__("flushw");
-	show_regs(regs);
+	__show_regs(regs);
 	{
 		struct reg_window *rw = (struct reg_window *)
 			(regs->u_regs[UREG_FP] + STACK_BIAS);
@@ -491,6 +494,10 @@
 		printk("Instruction DUMP:");
 		instruction_dump ((unsigned int *) regs->tpc);
 	}
+#ifdef __SMP__
+	smp_report_regs();
+#endif
+                                                	
 	lock_kernel(); /* Or else! */
 	if(regs->tstate & TSTATE_PRIV)
 		do_exit(SIGKILL);
@@ -498,7 +505,7 @@
 }
 
 extern int handle_popc(u32 insn, struct pt_regs *regs);
-extern int handle_ldq_stq(u32 insn, struct pt_regs *regs);
+extern int handle_ldf_stq(u32 insn, struct pt_regs *regs);
 
 void do_illegal_instruction(struct pt_regs *regs)
 {
@@ -515,7 +522,7 @@
 			if (handle_popc(insn, regs))
 				return;
 		} else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ {
-			if (handle_ldq_stq(insn, regs))
+			if (handle_ldf_stq(insn, regs))
 				return;
 		}
 	}
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c
--- v2.2.3/linux/arch/sparc64/kernel/unaligned.c	Sun Nov  8 14:02:48 1998
+++ linux/arch/sparc64/kernel/unaligned.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.13 1998/10/07 22:43:13 davem Exp $
+/* $Id: unaligned.c,v 1.14 1999/03/02 15:42:16 jj Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -470,7 +470,7 @@
 extern void do_privact(struct pt_regs *regs);
 extern void data_access_exception(struct pt_regs *regs);
 
-int handle_ldq_stq(u32 insn, struct pt_regs *regs)
+int handle_ldf_stq(u32 insn, struct pt_regs *regs)
 {
 	unsigned long addr = compute_effective_address(regs, insn, 0);
 	int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
@@ -522,8 +522,10 @@
 		    	return 1;
 		}
 	} else {
-		/* LDQ */
-		u32 first, second, third, fourth;
+		/* LDF, LDDF, LDQF */
+		u32 data[4] __attribute__ ((aligned(8)));
+		int size, i;
+		int err;
 
 		if (asi < 0x80) {
 			do_privact(regs);
@@ -532,25 +534,35 @@
 			data_access_exception(regs);
 			return 1;
 		}
-		if (get_user (first, (u32 *)addr) ||
-		    __get_user (second, (u32 *)(addr + 4)) ||
-		    __get_user (third, (u32 *)(addr + 8)) ||
-		    __get_user (fourth, (u32 *)(addr + 12))) {
-			if (asi & 0x2) /* NF */ {
-				first = 0; second = 0; third = 0; fourth = 0;
-			} else {
-		    		data_access_exception(regs);
-		    		return 1;
-		    	}
+		switch (insn & 0x180000) {
+		case 0x000000: size = 1; break;
+		case 0x100000: size = 4; break;
+		default: size = 2; break;
+		}
+		for (i = 0; i < size; i++)
+			data[i] = 0;
+		
+		err = get_user (data[0], (u32 *)addr);
+		if (!err) {
+			for (i = 1; i < size; i++)
+				err |= __get_user (data[i], (u32 *)(addr + 4*i));
+		}
+		if (err && !(asi & 0x2 /* NF */)) {
+			data_access_exception(regs);
+			return 1;
 		}
 		if (asi & 0x8) /* Little */ {
-			u32 tmp = le32_to_cpup(&first);
-			
-			first = le32_to_cpup(&fourth);
-			fourth = tmp;
-			tmp = le32_to_cpup(&second);
-			second = le32_to_cpup(&third);
-			third = tmp;
+			u64 tmp;
+
+			switch (size) {
+			case 1: data[0] = le32_to_cpup(data + 0); break;
+			default:*(u64 *)(data + 0) = le64_to_cpup((u64 *)(data + 0));
+				break;
+			case 4: tmp = le64_to_cpup((u64 *)(data + 0));
+				*(u64 *)(data + 0) = le64_to_cpup((u64 *)(data + 2));
+				*(u64 *)(data + 2) = tmp;
+				break;
+			}
 		}
 		if (!(current->tss.fpsaved[0] & FPRS_FEF)) {
 			current->tss.fpsaved[0] = FPRS_FEF;
@@ -562,14 +574,25 @@
 			else
 				memset(f->regs+32, 0, 32*sizeof(u32));
 		}
-		f->regs[freg] = first;
-		f->regs[freg+1] = second;
-		f->regs[freg+2] = third;
-		f->regs[freg+3] = fourth;
+		memcpy(f->regs + freg, data, size * 4);
 		current->tss.fpsaved[0] |= flag;
 	}
 	advance(regs);
 	return 1;
+}
+
+void handle_ld_nf(u32 insn, struct pt_regs *regs)
+{
+	int rd = ((insn >> 25) & 0x1f);
+	int from_kernel = (regs->tstate & TSTATE_PRIV) != 0;
+	unsigned long *reg;
+	                        
+	maybe_flush_windows(0, 0, rd, from_kernel);
+	reg = fetch_reg_addr(rd, regs);
+	if ((insn & 0x780000) == 0x180000)
+		reg[1] = 0;
+	reg[0] = 0;
+	advance(regs);
 }
 
 void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fabsq.c linux/arch/sparc64/math-emu/fabsq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fabsq.c	Thu Apr 23 20:21:32 1998
+++ linux/arch/sparc64/math-emu/fabsq.c	Wed Mar 10 16:53:37 1999
@@ -2,5 +2,5 @@
 {
 	rd[0] = rs2[0] & 0x7fffffffffffffffUL;
 	rd[1] = rs2[1];
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/faddd.c linux/arch/sparc64/math-emu/faddd.c
--- v2.2.3/linux/arch/sparc64/math-emu/faddd.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/faddd.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 	__FP_UNPACK_D(A, rs1);
 	__FP_UNPACK_D(B, rs2);
 	FP_ADD_D(R, A, B);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/faddq.c linux/arch/sparc64/math-emu/faddq.c
--- v2.2.3/linux/arch/sparc64/math-emu/faddq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/faddq.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 	__FP_UNPACK_Q(A, rs1);
 	__FP_UNPACK_Q(B, rs2);
 	FP_ADD_Q(R, A, B);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fadds.c linux/arch/sparc64/math-emu/fadds.c
--- v2.2.3/linux/arch/sparc64/math-emu/fadds.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fadds.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 	__FP_UNPACK_S(A, rs1);
 	__FP_UNPACK_S(B, rs2);
 	FP_ADD_S(R, A, B);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fcmpeq.c linux/arch/sparc64/math-emu/fcmpeq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fcmpeq.c	Thu Apr 23 20:21:32 1998
+++ linux/arch/sparc64/math-emu/fcmpeq.c	Wed Mar 10 16:53:37 1999
@@ -21,5 +21,5 @@
 	case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
 	}
 	*(unsigned long *)rd = fsr;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fcmpq.c linux/arch/sparc64/math-emu/fcmpq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fcmpq.c	Thu Apr 23 20:21:32 1998
+++ linux/arch/sparc64/math-emu/fcmpq.c	Wed Mar 10 16:53:37 1999
@@ -21,5 +21,5 @@
 	case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
 	}
 	*(unsigned long *)rd = fsr;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdivd.c linux/arch/sparc64/math-emu/fdivd.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdivd.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdivd.c	Wed Mar 10 16:53:37 1999
@@ -4,10 +4,16 @@
 int FDIVD(void *rd, void *rs2, void *rs1)
 {
 	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+	int ret = 0;
 
 	__FP_UNPACK_D(A, rs1);
 	__FP_UNPACK_D(B, rs2);
+	if(B_c == FP_CLS_ZERO &&
+	   A_c != FP_CLS_ZERO) {
+		ret |= EFLAG_DIVZERO;
+		if(__FPU_TRAP_P(EFLAG_DIVZERO))
+			return ret;
+	}
 	FP_DIV_D(R, A, B);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return (ret | __FP_PACK_D(rd, R));
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdivq.c linux/arch/sparc64/math-emu/fdivq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdivq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdivq.c	Wed Mar 10 16:53:37 1999
@@ -4,10 +4,16 @@
 int FDIVQ(void *rd, void *rs2, void *rs1)
 {
 	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+	int ret;
 
 	__FP_UNPACK_Q(A, rs1);
 	__FP_UNPACK_Q(B, rs2);
+	if(B_c == FP_CLS_ZERO &&
+	   A_c != FP_CLS_ZERO) {
+		ret |= EFLAG_DIVZERO;
+		if(__FPU_TRAP_P(EFLAG_DIVZERO))
+			return ret;
+	}
 	FP_DIV_Q(R, A, B);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return (ret | __FP_PACK_Q(rd, R));
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdivs.c linux/arch/sparc64/math-emu/fdivs.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdivs.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdivs.c	Wed Mar 10 16:53:37 1999
@@ -4,10 +4,17 @@
 int FDIVS(void *rd, void *rs2, void *rs1)
 {
 	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
+	int ret = 0;
 
 	__FP_UNPACK_S(A, rs1);
 	__FP_UNPACK_S(B, rs2);
+	if(B_c == FP_CLS_ZERO &&
+	   A_c != FP_CLS_ZERO) {
+		ret |= EFLAG_DIVZERO;
+		if(__FPU_TRAP_P(EFLAG_DIVZERO))
+			return ret;
+	}
 	FP_DIV_S(R, A, B);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return (ret | __FP_PACK_S(rd, R));
 }
+
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdmulq.c linux/arch/sparc64/math-emu/fdmulq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdmulq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdmulq.c	Wed Mar 10 16:53:37 1999
@@ -11,6 +11,5 @@
 	__FP_UNPACK_D(IN, rs2);
 	FP_CONV(Q,D,2,1,B,IN);
 	FP_MUL_Q(R, A, B);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtoi.c linux/arch/sparc64/math-emu/fdtoi.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdtoi.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdtoi.c	Wed Mar 10 16:53:37 1999
@@ -9,5 +9,5 @@
 	__FP_UNPACK_D(A, rs2);
 	FP_TO_INT_D(r, A, 32, 1);
 	*rd = r;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtoq.c linux/arch/sparc64/math-emu/fdtoq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdtoq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdtoq.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_D(A, rs2);
 	FP_CONV(Q,D,2,1,R,A);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtos.c linux/arch/sparc64/math-emu/fdtos.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdtos.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdtos.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_D(A, rs2);
 	FP_CONV(S,D,1,1,R,A);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fdtox.c linux/arch/sparc64/math-emu/fdtox.c
--- v2.2.3/linux/arch/sparc64/math-emu/fdtox.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fdtox.c	Wed Mar 10 16:53:37 1999
@@ -9,5 +9,5 @@
 	__FP_UNPACK_D(A, rs2);
 	FP_TO_INT_D(r, A, 64, 1);
 	*rd = r;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fitoq.c linux/arch/sparc64/math-emu/fitoq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fitoq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fitoq.c	Wed Mar 10 16:53:37 1999
@@ -7,6 +7,5 @@
 	int a = *(int *)rs2;
 
 	FP_FROM_INT_Q(R, a, 32, int);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fmuld.c linux/arch/sparc64/math-emu/fmuld.c
--- v2.2.3/linux/arch/sparc64/math-emu/fmuld.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fmuld.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 	__FP_UNPACK_D(A, rs1);
 	__FP_UNPACK_D(B, rs2);
 	FP_MUL_D(R, A, B);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fmulq.c linux/arch/sparc64/math-emu/fmulq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fmulq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fmulq.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 	__FP_UNPACK_Q(A, rs1);
 	__FP_UNPACK_Q(B, rs2);
 	FP_MUL_Q(R, A, B);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fmuls.c linux/arch/sparc64/math-emu/fmuls.c
--- v2.2.3/linux/arch/sparc64/math-emu/fmuls.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fmuls.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 	__FP_UNPACK_S(A, rs1);
 	__FP_UNPACK_S(B, rs2);
 	FP_MUL_S(R, A, B);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fnegq.c linux/arch/sparc64/math-emu/fnegq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fnegq.c	Thu Apr 23 20:21:32 1998
+++ linux/arch/sparc64/math-emu/fnegq.c	Wed Mar 10 16:53:37 1999
@@ -2,6 +2,6 @@
 {
 	rd[0] = rs2[0] ^ 0x8000000000000000UL;
 	rd[1] = rs2[1];
-	return 1;
+	return 0;
 }
 
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtod.c linux/arch/sparc64/math-emu/fqtod.c
--- v2.2.3/linux/arch/sparc64/math-emu/fqtod.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fqtod.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_Q(A, rs2);
 	FP_CONV(D,Q,1,2,R,A);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtoi.c linux/arch/sparc64/math-emu/fqtoi.c
--- v2.2.3/linux/arch/sparc64/math-emu/fqtoi.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fqtoi.c	Wed Mar 10 16:53:37 1999
@@ -9,5 +9,5 @@
 	__FP_UNPACK_Q(A, rs2);
 	FP_TO_INT_Q(r, A, 32, 1);
 	*rd = r;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtos.c linux/arch/sparc64/math-emu/fqtos.c
--- v2.2.3/linux/arch/sparc64/math-emu/fqtos.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fqtos.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_Q(A, rs2);
 	FP_CONV(S,Q,1,2,R,A);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fqtox.c linux/arch/sparc64/math-emu/fqtox.c
--- v2.2.3/linux/arch/sparc64/math-emu/fqtox.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fqtox.c	Wed Mar 10 16:53:37 1999
@@ -9,5 +9,5 @@
 	__FP_UNPACK_Q(A, rs2);
 	FP_TO_INT_Q(r, A, 64, 1);
 	*rd = r;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsmuld.c linux/arch/sparc64/math-emu/fsmuld.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsmuld.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsmuld.c	Wed Mar 10 16:53:37 1999
@@ -11,6 +11,5 @@
 	__FP_UNPACK_S(IN, rs2);
 	FP_CONV(D,S,1,1,B,IN);
 	FP_MUL_D(R, A, B);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsqrtd.c linux/arch/sparc64/math-emu/fsqrtd.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsqrtd.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsqrtd.c	Wed Mar 10 16:53:37 1999
@@ -7,6 +7,5 @@
         
 	__FP_UNPACK_D(A, rs2);
 	FP_SQRT_D(R, A);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsqrtq.c linux/arch/sparc64/math-emu/fsqrtq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsqrtq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsqrtq.c	Wed Mar 10 16:53:37 1999
@@ -7,6 +7,5 @@
         
 	__FP_UNPACK_Q(A, rs2);
 	FP_SQRT_Q(R, A);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsqrts.c linux/arch/sparc64/math-emu/fsqrts.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsqrts.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsqrts.c	Wed Mar 10 16:53:37 1999
@@ -7,6 +7,5 @@
         
 	__FP_UNPACK_S(A, rs2);
 	FP_SQRT_S(R, A);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstod.c linux/arch/sparc64/math-emu/fstod.c
--- v2.2.3/linux/arch/sparc64/math-emu/fstod.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fstod.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_S(A, rs2);
 	FP_CONV(D,S,1,1,R,A);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstoi.c linux/arch/sparc64/math-emu/fstoi.c
--- v2.2.3/linux/arch/sparc64/math-emu/fstoi.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fstoi.c	Wed Mar 10 16:53:37 1999
@@ -9,5 +9,5 @@
 	__FP_UNPACK_S(A, rs2);
 	FP_TO_INT_S(r, A, 32, 1);
 	*rd = r;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstoq.c linux/arch/sparc64/math-emu/fstoq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fstoq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fstoq.c	Wed Mar 10 16:53:37 1999
@@ -8,6 +8,5 @@
 
 	__FP_UNPACK_S(A, rs2);
 	FP_CONV(Q,S,2,1,R,A);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fstox.c linux/arch/sparc64/math-emu/fstox.c
--- v2.2.3/linux/arch/sparc64/math-emu/fstox.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fstox.c	Wed Mar 10 16:53:37 1999
@@ -9,5 +9,5 @@
 	__FP_UNPACK_S(A, rs2);
 	FP_TO_INT_S(r, A, 64, 1);
 	*rd = r;
-	return 1;
+	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsubd.c linux/arch/sparc64/math-emu/fsubd.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsubd.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsubd.c	Wed Mar 10 16:53:37 1999
@@ -10,6 +10,5 @@
 	if (B_c != FP_CLS_NAN)
 		B_s ^= 1;
 	FP_ADD_D(R, A, B);
-	__FP_PACK_D(rd, R);
-	return 1;
+	return __FP_PACK_D(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsubq.c linux/arch/sparc64/math-emu/fsubq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsubq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsubq.c	Wed Mar 10 16:53:37 1999
@@ -10,6 +10,5 @@
 	if (B_c != FP_CLS_NAN)
 		B_s ^= 1;
 	FP_ADD_Q(R, A, B);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fsubs.c linux/arch/sparc64/math-emu/fsubs.c
--- v2.2.3/linux/arch/sparc64/math-emu/fsubs.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fsubs.c	Wed Mar 10 16:53:37 1999
@@ -10,6 +10,5 @@
 	if (B_c != FP_CLS_NAN)
 		B_s ^= 1;
 	FP_ADD_S(R, A, B);
-	__FP_PACK_S(rd, R);
-	return 1;
+	return __FP_PACK_S(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/fxtoq.c linux/arch/sparc64/math-emu/fxtoq.c
--- v2.2.3/linux/arch/sparc64/math-emu/fxtoq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fxtoq.c	Wed Mar 10 16:53:37 1999
@@ -7,6 +7,5 @@
 	long a = *(long *)rs2;
 
 	FP_FROM_INT_Q(R, a, 64, long);
-	__FP_PACK_Q(rd, R);
-	return 1;
+	return __FP_PACK_Q(rd, R);
 }
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/math.c linux/arch/sparc64/math-emu/math.c
--- v2.2.3/linux/arch/sparc64/math-emu/math.c	Thu Aug  6 14:06:31 1998
+++ linux/arch/sparc64/math-emu/math.c	Wed Mar 10 16:53:37 1999
@@ -1,7 +1,8 @@
-/* $Id: math.c,v 1.5 1998/06/12 14:54:27 jj Exp $
+/* $Id: math.c,v 1.7 1999/02/10 14:16:26 davem Exp $
  * arch/sparc64/math-emu/math.c
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
  *
  * Emulation routines originate from soft-fp package, which is part
  * of glibc and has appropriate copyrights in it.
@@ -14,6 +15,8 @@
 #include <asm/ptrace.h>
 #include <asm/uaccess.h>
 
+#include "soft-fp.h"
+
 #define FLOATFUNC(x) extern int x(void *,void *,void *);
 
 FLOATFUNC(FMOVQ)
@@ -54,6 +57,91 @@
 FLOATFUNC(FSTOI)
 FLOATFUNC(FDTOI)
 
+#define FSR_TEM_SHIFT	23UL
+#define FSR_TEM_MASK	(0x1fUL << FSR_TEM_SHIFT)
+#define FSR_AEXC_SHIFT	5UL
+#define FSR_AEXC_MASK	(0x1fUL << FSR_AEXC_SHIFT)
+#define FSR_CEXC_SHIFT	0UL
+#define FSR_CEXC_MASK	(0x1fUL << FSR_CEXC_SHIFT)
+
+/* All routines returning an exception to raise should detect
+ * such exceptions _before_ rounding to be consistant with
+ * the behavior of the hardware in the implemented cases
+ * (and thus with the recommendations in the V9 architecture
+ * manual).
+ *
+ * We return 0 if a SIGFPE should be sent, 1 otherwise.
+ */
+static int record_exception(struct pt_regs *regs, int eflag)
+{
+	u64 fsr = current->tss.xfsr[0];
+	int would_trap;
+
+	/* Determine if this exception would have generated a trap. */
+	would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
+
+	/* If trapping, we only want to signal one bit. */
+	if(would_trap != 0) {
+		eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
+		if((eflag & (eflag - 1)) != 0) {
+			if(eflag & EFLAG_INVALID)
+				eflag = EFLAG_INVALID;
+			else if(eflag & EFLAG_DIVZERO)
+				eflag = EFLAG_DIVZERO;
+			else if(eflag & EFLAG_INEXACT)
+				eflag = EFLAG_INEXACT;
+		}
+	}
+
+	/* Set CEXC, here are the rules:
+	 *
+	 * 1) In general all FPU ops will set one and only one
+	 *    bit in the CEXC field, this is always the case
+	 *    when the IEEE exception trap is enabled in TEM.
+	 *
+	 * 2) As a special case, if an overflow or underflow
+	 *    is being signalled, AND the trap is not enabled
+	 *    in TEM, then the inexact field shall also be set.
+	 */
+	fsr &= ~(FSR_CEXC_MASK);
+	if(would_trap ||
+	   (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) {
+		fsr |= ((long)eflag << FSR_CEXC_SHIFT);
+	} else {
+		fsr |= (((long)eflag << FSR_CEXC_SHIFT) |
+			(EFLAG_INEXACT << FSR_CEXC_SHIFT));
+	}
+
+	/* Set the AEXC field, rules are:
+	 *
+	 * 1) If a trap would not be generated, the
+	 *    CEXC just generated is OR'd into the
+	 *    existing value of AEXC.
+	 *
+	 * 2) When a trap is generated, AEXC is cleared.
+	 */
+	if(would_trap == 0)
+		fsr |= ((long)eflag << FSR_AEXC_SHIFT);
+	else
+		fsr &= ~(FSR_AEXC_MASK);
+
+	/* If trapping, indicate fault trap type IEEE. */
+	if(would_trap != 0)
+		fsr |= (1UL << 14);
+
+	current->tss.xfsr[0] = fsr;
+
+	/* If we will not trap, advance the program counter over
+	 * the instruction being handled.
+	 */
+	if(would_trap == 0) {
+		regs->tpc = regs->tnpc;
+		regs->tnpc += 4;
+	}
+
+	return (would_trap ? 0 : 1);
+}
+
 int do_mathemu(struct pt_regs *regs, struct fpustate *f)
 {
 	unsigned long pc = regs->tpc;
@@ -175,7 +263,12 @@
 			current->tss.fpsaved[0] |= flags;
 			break;
 		}
-		func(rd, rs2, rs1);
+		flags = func(rd, rs2, rs1);
+		if(flags != 0)
+			return record_exception(regs, flags);
+
+		/* Success and no exceptions detected. */
+		current->tss.xfsr[0] &= ~(FSR_CEXC_MASK);
 		regs->tpc = regs->tnpc;
 		regs->tnpc += 4;
 		return 1;
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h
--- v2.2.3/linux/arch/sparc64/math-emu/op-2.h	Thu Apr 23 20:21:32 1998
+++ linux/arch/sparc64/math-emu/op-2.h	Wed Mar 10 16:53:37 1999
@@ -190,14 +190,14 @@
 									\
     __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
 		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
+		    0, _b_f1, _b_f0, 0,					\
 		    _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
-		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
-		    0, _b_f1, _b_f0, 0);				\
+		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0));	\
     __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
 		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
+		    0, _c_f1, _c_f0, 0,					\
 		    _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
-		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
-		    0, _c_f1, _c_f0, 0);				\
+		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0));	\
 									\
     /* Normalize since we know where the msb of the multiplicands	\
        were (bit B), we know that the msb of the of the product is	\
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h
--- v2.2.3/linux/arch/sparc64/math-emu/op-common.h	Thu Aug  6 14:06:31 1998
+++ linux/arch/sparc64/math-emu/op-common.h	Wed Mar 10 16:53:37 1999
@@ -53,14 +53,14 @@
  */
 
 #define _FP_PACK_CANONICAL(fs, wc, X)				\
-do {								\
+({int __ret = 0;						\
   switch (X##_c)						\
   {								\
   case FP_CLS_NORMAL:						\
     X##_e += _FP_EXPBIAS_##fs;					\
     if (X##_e > 0)						\
       {								\
-	_FP_ROUND(wc, X);					\
+	__ret |= _FP_ROUND(wc, X);				\
 	if (_FP_FRAC_OVERP_##wc(fs, X))				\
 	  {							\
 	    _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1));		\
@@ -73,6 +73,7 @@
 	    /* overflow to infinity */				\
 	    X##_e = _FP_EXPMAX_##fs;				\
 	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);		\
+            __ret |= EFLAG_OVERFLOW;				\
 	  }							\
       }								\
     else							\
@@ -82,7 +83,7 @@
 	if (X##_e <= _FP_WFRACBITS_##fs)			\
 	  {							\
 	    _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs);	\
-	    _FP_ROUND(wc, X);					\
+	    __ret |= _FP_ROUND(wc, X);				\
 	    _FP_FRAC_SLL_##wc(X, 1);				\
 	    if (_FP_FRAC_OVERP_##wc(fs, X))			\
 	      {							\
@@ -93,6 +94,7 @@
 	      {							\
 		X##_e = 0;					\
 		_FP_FRAC_SRL_##wc(X, _FP_WORKBITS+1);		\
+                __ret |= EFLAG_UNDERFLOW;			\
 	      }							\
 	  }							\
 	else							\
@@ -100,6 +102,7 @@
 	    /* underflow to zero */				\
 	    X##_e = 0;						\
 	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);		\
+            __ret |= EFLAG_UNDERFLOW;				\
 	  }							\
       }								\
     break;							\
@@ -125,7 +128,8 @@
       _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs;		\
     break;							\
   }								\
-} while (0)
+  __ret;							\
+})
 
 
 /*
@@ -424,11 +428,19 @@
       }									\
     else								\
       {									\
-	/* Force -0 -> +0 */						\
-	if (!X##_e && _FP_FRAC_ZEROP_##wc(X)) X##_s = 0;		\
-	if (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) X##_s = 0;		\
+	int __is_zero_x;						\
+	int __is_zero_y;						\
+									\
+	__is_zero_x = (!X##_e && _FP_FRAC_ZEROP_##wc(X)) ? 1 : 0;	\
+	__is_zero_y = (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) ? 1 : 0;	\
 									\
-	if (X##_s != Y##_s)						\
+	if (__is_zero_x && __is_zero_y)					\
+		ret = 0;						\
+	else if (__is_zero_x)						\
+		ret = Y##_s ? 1 : -1;					\
+	else if (__is_zero_y)						\
+		ret = X##_s ? -1 : 1;					\
+	else if (X##_s != Y##_s)					\
 	  ret = X##_s ? -1 : 1;						\
 	else if (X##_e > Y##_e)						\
 	  ret = X##_s ? -1 : 1;						\
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/sfp-machine.h linux/arch/sparc64/math-emu/sfp-machine.h
--- v2.2.3/linux/arch/sparc64/math-emu/sfp-machine.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/sfp-machine.h	Tue Mar 16 21:52:06 1999
@@ -52,16 +52,6 @@
     X##_s = _flo->bits.sign;					\
   } while (0)
 
-#define __FP_PACK_RAW_1(fs, val, X)				\
-  do {								\
-    union _FP_UNION_##fs *_flo =				\
-    	(union _FP_UNION_##fs *)val;				\
-								\
-    _flo->bits.frac = X##_f;					\
-    _flo->bits.exp  = X##_e;					\
-    _flo->bits.sign = X##_s;					\
-  } while (0)
-  
 #define __FP_UNPACK_RAW_2(fs, X, val)			\
   do {							\
     union _FP_UNION_##fs *_flo =			\
@@ -73,52 +63,79 @@
     X##_s  = _flo->bits.sign;				\
   } while (0)
 
-#define __FP_PACK_RAW_2(fs, val, X)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    _flo->bits.frac0 = X##_f0;				\
-    _flo->bits.frac1 = X##_f1;				\
-    _flo->bits.exp   = X##_e;				\
-    _flo->bits.sign  = X##_s;				\
-  } while (0)
-
 #define __FP_UNPACK_S(X,val)		\
   do {					\
     __FP_UNPACK_RAW_1(S,X,val);		\
     _FP_UNPACK_CANONICAL(S,1,X);	\
   } while (0)
 
-#define __FP_PACK_S(val,X)		\
-  do {					\
-    _FP_PACK_CANONICAL(S,1,X);		\
-    __FP_PACK_RAW_1(S,val,X);		\
-  } while (0)
-
 #define __FP_UNPACK_D(X,val)		\
   do {					\
     __FP_UNPACK_RAW_1(D,X,val);		\
     _FP_UNPACK_CANONICAL(D,1,X);	\
   } while (0)
 
-#define __FP_PACK_D(val,X)		\
-  do {					\
-    _FP_PACK_CANONICAL(D,1,X);		\
-    __FP_PACK_RAW_1(D,val,X);		\
-  } while (0)
-
 #define __FP_UNPACK_Q(X,val)		\
   do {					\
     __FP_UNPACK_RAW_2(Q,X,val);		\
     _FP_UNPACK_CANONICAL(Q,2,X);	\
   } while (0)
 
-#define __FP_PACK_Q(val,X)		\
-  do {					\
-    _FP_PACK_CANONICAL(Q,2,X);		\
-    __FP_PACK_RAW_2(Q,val,X);		\
+#define __FP_PACK_RAW_1(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+    	(union _FP_UNION_##fs *)val;				\
+								\
+    _flo->bits.frac = X##_f;					\
+    _flo->bits.exp  = X##_e;					\
+    _flo->bits.sign = X##_s;					\
   } while (0)
+  
+#define __FP_PACK_RAW_2(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+    	(union _FP_UNION_##fs *)val;			\
+							\
+    _flo->bits.frac0 = X##_f0;				\
+    _flo->bits.frac1 = X##_f1;				\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
+  } while (0)
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+/* We only actually write to the destination register
+ * if exceptions signalled (if any) will not trap.
+ */
+#define __FPU_TEM \
+	(((current->tss.xfsr[0])>>23)&0x1f)
+#define __FPU_TRAP_P(bits) \
+	((__FPU_TEM & (bits)) != 0)
+
+#define __FP_PACK_S(val,X)			\
+({  int __exc = _FP_PACK_CANONICAL(S,1,X);	\
+    if(!__exc || !__FPU_TRAP_P(__exc))		\
+        __FP_PACK_RAW_1(S,val,X);		\
+    __exc;					\
+})
+
+#define __FP_PACK_D(val,X)			\
+({  int __exc = _FP_PACK_CANONICAL(D,1,X);	\
+    if(!__exc || !__FPU_TRAP_P(__exc))		\
+        __FP_PACK_RAW_1(D,val,X);		\
+    __exc;					\
+})
+
+#define __FP_PACK_Q(val,X)			\
+({  int __exc = _FP_PACK_CANONICAL(Q,2,X);	\
+    if(!__exc || !__FPU_TRAP_P(__exc))		\
+        __FP_PACK_RAW_2(Q,val,X);		\
+    __exc;					\
+})
+
+/* Obtain the current rounding mode. */
+#define FP_ROUNDMODE	((current->tss.xfsr[0] >> 30) & 0x3)
 
 #include <linux/types.h>
 #include <asm/byteorder.h>
@@ -153,28 +170,24 @@
 	   
 #define umul_ppmm(wh, wl, u, v) 							\
   do {											\
-  	long tmp1 = 0, tmp2 = 0, tmp3 = 0;						\
 	  __asm__ ("mulx %2,%3,%1
-  		    srlx %2,32,%4
-	  	    srl %3,0,%5
-  		    mulx %4,%5,%6
-  		    srlx %3,32,%4
-  		    srl %2,0,%5
-  		    mulx %4,%5,%5
-  		    srlx %2,32,%4
-  		    add %5,%6,%6
-  		    srlx %3,32,%5
-  		    mulx %4,%5,%4
-  		    srlx %6,32,%5
-  		    add %4,%5,%0"							\
+  		    srlx %2,32,%%g1
+	  	    srl %3,0,%%g2
+  		    mulx %%g1,%%g2,%%g3
+  		    srlx %3,32,%%g1
+  		    srl %2,0,%%g2
+  		    mulx %%g1,%%g2,%%g2
+  		    srlx %2,32,%%g1
+  		    add %%g2,%%g3,%%g3
+  		    srlx %3,32,%%g2
+  		    mulx %%g1,%%g2,%%g1
+  		    srlx %%g3,32,%%g2
+  		    add %%g1,%%g2,%0"							\
 	   : "=r" ((UDItype)(wh)),				      			\
 	     "=&r" ((UDItype)(wl))				      			\
 	   : "r" ((UDItype)(u)),				     			\
-	     "r" ((UDItype)(v)),				      			\
-	     "r" ((UDItype)(tmp1)),				      			\
-	     "r" ((UDItype)(tmp2)),				      			\
-	     "r" ((UDItype)(tmp3))				      			\
-	   : "cc");									\
+	     "r" ((UDItype)(v))				      				\
+	   : "g1", "g2", "g3", "cc");							\
   } while (0)
   
 #define udiv_qrnnd(q, r, n1, n0, d) 							\
@@ -223,3 +236,10 @@
 #else
 #define __BYTE_ORDER __LITTLE_ENDIAN
 #endif
+
+/* Exception flags. */
+#define EFLAG_INVALID		(1 << 4)
+#define EFLAG_OVERFLOW		(1 << 3)
+#define EFLAG_UNDERFLOW		(1 << 2)
+#define EFLAG_DIVZERO		(1 << 1)
+#define EFLAG_INEXACT		(1 << 0)
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/math-emu/soft-fp.h linux/arch/sparc64/math-emu/soft-fp.h
--- v2.2.3/linux/arch/sparc64/math-emu/soft-fp.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/soft-fp.h	Wed Mar 10 16:53:37 1999
@@ -14,45 +14,56 @@
 # define FP_RND_ZERO		1
 # define FP_RND_PINF		2
 # define FP_RND_MINF		3
+#ifndef FP_ROUNDMODE
 # define FP_ROUNDMODE		FP_RND_NEAREST
 #endif
+#endif
 
 #define _FP_ROUND_NEAREST(wc, X)			\
-  do { 							\
+({  int __ret = EFLAG_INEXACT;				\
     if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND)	\
       _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND);		\
-  } while(0)
+    else __ret = 0;					\
+    __ret;						\
+})
 
-#define _FP_ROUND_ZERO(wc, X)
+#define _FP_ROUND_ZERO(wc, X)		0 /* XXX */
 
 #define _FP_ROUND_PINF(wc, X)				\
-  do {							\
+({  int __ret = EFLAG_INEXACT;				\
     if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7))		\
       _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB);		\
-  } while (0)
+    else __ret = 0;					\
+    __ret;						\
+})
 
 #define _FP_ROUND_MINF(wc, X)				\
-  do {							\
+({  int __ret = EFLAG_INEXACT;				\
     if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7))		\
       _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB);		\
-  } while (0)
+    else __ret = 0;					\
+    __ret;						\
+})
 
 #define _FP_ROUND(wc, X)			\
+({	int __ret = 0;				\
 	switch (FP_ROUNDMODE)			\
 	{					\
 	  case FP_RND_NEAREST:			\
-	    _FP_ROUND_NEAREST(wc,X);		\
+	    __ret |= _FP_ROUND_NEAREST(wc,X);	\
 	    break;				\
 	  case FP_RND_ZERO:			\
-	    _FP_ROUND_ZERO(wc,X);		\
+	    __ret |= _FP_ROUND_ZERO(wc,X);	\
 	    break;				\
 	  case FP_RND_PINF:			\
-	    _FP_ROUND_PINF(wc,X);		\
+	    __ret |= _FP_ROUND_PINF(wc,X);	\
 	    break;				\
 	  case FP_RND_MINF:			\
-	    _FP_ROUND_MINF(wc,X);		\
+	    __ret |= _FP_ROUND_MINF(wc,X);	\
 	    break;				\
-	}
+	};					\
+	__ret;					\
+})
 
 #define FP_CLS_NORMAL		0
 #define FP_CLS_ZERO		1
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c
--- v2.2.3/linux/arch/sparc64/mm/fault.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/mm/fault.c	Tue Mar 16 21:52:06 1999
@@ -1,8 +1,8 @@
-/* $Id: fault.c,v 1.26 1998/11/08 11:14:03 davem Exp $
+/* $Id: fault.c,v 1.34 1999/03/16 12:12:28 jj Exp $
  * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 #include <asm/head.h>
@@ -14,6 +14,8 @@
 #include <linux/signal.h>
 #include <linux/mm.h>
 #include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -26,7 +28,7 @@
 extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 
 /* Nice, simple, prom library does all the sweating for us. ;) */
-unsigned long prom_probe_memory (void)
+unsigned long __init prom_probe_memory (void)
 {
 	register struct linux_mlist_p1275 *mlist;
 	register unsigned long bytes, base_paddr, tally;
@@ -35,7 +37,7 @@
 	i = 0;
 	mlist = *prom_meminfo()->p1275_available;
 	bytes = tally = mlist->num_bytes;
-	base_paddr = (unsigned int) mlist->start_adr;
+	base_paddr = mlist->start_adr;
   
 	sp_banks[0].base_addr = base_paddr;
 	sp_banks[0].num_bytes = bytes;
@@ -55,12 +57,12 @@
 			break;
 		}
     
-		sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+		sp_banks[i].base_addr = mlist->start_adr;
 		sp_banks[i].num_bytes = mlist->num_bytes;
 	}
 
 	i++;
-	sp_banks[i].base_addr = 0xdeadbeef;
+	sp_banks[i].base_addr = 0xdeadbeefbeefdeadUL;
 	sp_banks[i].num_bytes = 0;
 
 	/* Now mask all bank sizes on a page boundary, it is all we can
@@ -72,26 +74,12 @@
 	return tally;
 }
 
-/* Traverse the memory lists in the prom to see how much physical we
- * have.
- */
-unsigned long
-probe_memory(void)
-{
-	unsigned long total;
-
-	total = prom_probe_memory();
-
-	/* Oh man, much nicer, keep the dirt in promlib. */
-	return total;
-}
-
 void unhandled_fault(unsigned long address, struct task_struct *tsk,
                      struct pt_regs *regs)
 {
 	if((unsigned long) address < PAGE_SIZE) {
 		printk(KERN_ALERT "Unable to handle kernel NULL "
-		       "pointer dereference");
+		       "pointer dereference\n");
 	} else {
 		printk(KERN_ALERT "Unable to handle kernel paging request "
 		       "at virtual address %016lx\n", (unsigned long)address);
@@ -100,22 +88,74 @@
 	       (unsigned long) tsk->mm->context);
 	printk(KERN_ALERT "tsk->mm->pgd = %016lx\n",
 	       (unsigned long) tsk->mm->pgd);
-	lock_kernel();
 	die_if_kernel("Oops", regs);
-	unlock_kernel();
 }
 
 /* #define DEBUG_EXCEPTIONS */
 /* #define DEBUG_LOCKUPS */
 
+/* #define INSN_VPTE_LOOKUP */
+
+static inline u32 get_user_insn(unsigned long tpc)
+{
+	u32 insn;
+#ifndef INSN_VPTE_LOOKUP
+	pgd_t *pgdp = pgd_offset(current->mm, tpc);
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	if(pgd_none(*pgdp))
+		return 0;
+	pmdp = pmd_offset(pgdp, tpc);
+	if(pmd_none(*pmdp))
+		return 0;
+	ptep = pte_offset(pmdp, tpc);
+	if(!pte_present(*ptep))
+		return 0;
+	insn = *(unsigned int *)(pte_page(*ptep) + (tpc & ~PAGE_MASK));
+#else
+	register unsigned long pte asm("l1");
+
+	/* So that we don't pollute TLB, we read the instruction
+	 * using PHYS bypass. For that, we of course need
+	 * to know its page table entry. Do this by simulating
+	 * dtlb_miss handler. -jj */
+	pte = ((((long)tpc) >> (PAGE_SHIFT-3)) & ~7);
+	asm volatile ("
+		rdpr    %%pstate, %%l0
+		wrpr    %%l0, %2, %%pstate
+		wrpr    %%g0, 1, %%tl
+		mov	%%l1, %%g6
+		ldxa    [%%g3 + %%l1] %3, %%g5
+		mov     %%g5, %%l1
+		wrpr    %%g0, 0, %%tl
+		wrpr    %%l0, 0, %%pstate
+	" : "=r" (pte) : "0" (pte), "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_S) : "l0");
+			
+	if ((long)pte >= 0) return 0;
+
+	pte = (pte & _PAGE_PADDR) + (tpc & ~PAGE_MASK);
+	asm ("lduwa	[%1] %2, %0" : "=r" (insn) : "r" (pte), "i" (ASI_PHYS_USE_EC));
+#endif
+
+	return insn;
+}
+
 asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write)
 {
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
+	unsigned int insn = 0;
 #ifdef DEBUG_LOCKUPS
 	static unsigned long lastaddr, lastpc;
 	static int lastwrite, lockcnt;
 #endif
+	/*
+	 * If we're in an interrupt or have no user
+	 * context, we must not take the fault..
+	 */
+	if (in_interrupt() || mm == &init_mm)
+		goto do_kernel_fault;
 
 	down(&mm->mmap_sem);
 #ifdef DEBUG_LOCKUPS
@@ -135,6 +175,21 @@
 	vma = find_vma(mm, address);
 	if(!vma)
 		goto bad_area;
+#ifndef INSN_VPTE_LOOKUP
+	write &= 0xf;
+#else
+	if (write & 0x10) {
+		write = 0;
+		if((vma->vm_flags & VM_WRITE)) {
+			if (regs->tstate & TSTATE_PRIV)
+				insn = *(unsigned int *)regs->tpc;
+			else
+				insn = get_user_insn(regs->tpc);
+			if ((insn & 0xc0200000) == 0xc0200000 && (insn & 0x1780000) != 0x1680000)
+				write = 1;
+		}
+	}
+#endif
 	if(vma->vm_start <= address)
 		goto good_area;
 	if(!(vma->vm_flags & VM_GROWSDOWN))
@@ -168,16 +223,44 @@
 
 do_kernel_fault:
 	{
-		unsigned long g2 = regs->u_regs[UREG_G2];
+		unsigned long g2;
+		unsigned char asi = ASI_P;
+		
+		if (!insn) {
+			if (regs->tstate & TSTATE_PRIV)
+				insn = *(unsigned int *)regs->tpc;
+			else
+				insn = get_user_insn(regs->tpc);
+		}
+		if (write != 1 && (insn & 0xc0800000) == 0xc0800000) {
+			if (insn & 0x2000)
+				asi = (regs->tstate >> 24);
+			else
+				asi = (insn >> 5);
+			if ((asi & 0xf2) == 0x82) {
+				/* This was a non-faulting load. Just clear the
+				   destination register(s) and continue with the next
+				   instruction. -jj */
+				if (insn & 0x1000000) {
+					extern int handle_ldf_stq(u32, struct pt_regs *);
+					
+					handle_ldf_stq(insn, regs);
+				} else {
+					extern int handle_ld_nf(u32, struct pt_regs *);
+					
+					handle_ld_nf(insn, regs);
+				}
+				return;
+			}
+		}
+		
+		g2 = regs->u_regs[UREG_G2];
 
 		/* Is this in ex_table? */
 		if (regs->tstate & TSTATE_PRIV) {
-			unsigned char asi = ASI_P;
-			unsigned int insn;
 			unsigned long fixup;
-		
-			insn = *(unsigned int *)regs->tpc;
-			if ((insn & 0xc0800000) == 0xc0800000) {
+
+			if (asi == ASI_P && (insn & 0xc0800000) == 0xc0800000) {
 				if (insn & 0x2000)
 					asi = (regs->tstate >> 24);
 				else
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/mm/generic.c linux/arch/sparc64/mm/generic.c
--- v2.2.3/linux/arch/sparc64/mm/generic.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/mm/generic.c	Mon Mar 15 16:10:43 1999
@@ -1,4 +1,4 @@
-/* $Id: generic.c,v 1.3 1998/10/27 23:28:07 davem Exp $
+/* $Id: generic.c,v 1.8 1999/03/12 06:51:50 davem Exp $
  * generic.c: Generic Sparc mm routines that are not dependent upon
  *            MMU type but are Sparc specific.
  *
@@ -56,6 +56,10 @@
  *
  * They use a pgprot that sets PAGE_IO and does not check the
  * mem_map table as this is independent of normal memory.
+ *
+ * As a special hack if the lowest bit of offset is set the
+ * side-effect bit will be turned off.  This is used as a
+ * performance improvement on FFB/AFB. -DaveM
  */
 static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
 	unsigned long offset, pgprot_t prot, int space)
@@ -71,23 +75,32 @@
 		pte_t entry;
 		unsigned long curend = address + PAGE_SIZE;
 		
-		entry = mk_pte_io(offset, prot, space);
-		offset += PAGE_SIZE;
+		entry = mk_pte_io((offset & ~(0x1UL)), prot, space);
 		if (!(address & 0xffff)) {
-			if (!(address & 0x3fffff) && !(offset & 0x3fffff) && end >= address + 0x400000) {
-				entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), space);
+			if (!(address & 0x3fffff) && !(offset & 0x3ffffe) && end >= address + 0x400000) {
+				entry = mk_pte_io((offset & ~(0x1UL)),
+						  __pgprot(pgprot_val (prot) | _PAGE_SZ4MB),
+						  space);
 				curend = address + 0x400000;
-				offset += 0x400000 - PAGE_SIZE;
-			} else if (!(address & 0x7ffff) && !(offset & 0x7ffff) && end >= address + 0x80000) {
-				entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ512K), space);
+				offset += 0x400000;
+			} else if (!(address & 0x7ffff) && !(offset & 0x7fffe) && end >= address + 0x80000) {
+				entry = mk_pte_io((offset & ~(0x1UL)),
+						  __pgprot(pgprot_val (prot) | _PAGE_SZ512K),
+						  space);
 				curend = address + 0x80000;
-				offset += 0x80000 - PAGE_SIZE;
-			} else if (!(offset & 0xffff) && end >= address + 0x10000) {
-				entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ64K), space);
+				offset += 0x80000;
+			} else if (!(offset & 0xfffe) && end >= address + 0x10000) {
+				entry = mk_pte_io((offset & ~(0x1UL)),
+						  __pgprot(pgprot_val (prot) | _PAGE_SZ64K),
+						  space);
 				curend = address + 0x10000;
-				offset += 0x10000 - PAGE_SIZE;
+				offset += 0x10000;
 			}
-		}
+		} else
+			offset += PAGE_SIZE;
+
+		if (offset & 0x1UL)
+			pte_val(entry) &= ~(_PAGE_E);
 		do {
 			oldpage = *pte;
 			pte_clear(pte);
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.2.3/linux/arch/sparc64/mm/init.c	Sun Nov  8 14:02:49 1998
+++ linux/arch/sparc64/mm/init.c	Wed Mar 10 16:53:37 1999
@@ -1,8 +1,8 @@
-/*  $Id: init.c,v 1.103 1998/10/20 03:09:12 jj Exp $
+/*  $Id: init.c,v 1.124 1999/02/08 07:01:42 ecd Exp $
  *  arch/sparc64/mm/init.c
  *
- *  Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
  
 #include <linux/config.h>
@@ -23,6 +23,7 @@
 #include <asm/io.h>
 #include <asm/mmu_context.h>
 #include <asm/vaddrs.h>
+#include <asm/dma.h>
 
 /* Turn this off if you suspect some place in some physical memory hole
    might get into page tables (something would be broken very much). */
@@ -34,6 +35,8 @@
 
 struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 
+unsigned long *sparc64_valid_addr_bitmap;
+
 /* Ugly, but necessary... -DaveM */
 unsigned long phys_base;
 
@@ -69,7 +72,7 @@
                                 page->next_hash = NULL;
                                 page->pprev_hash = NULL;
                                 pgd_cache_size -= 2;
-                                free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+                                __free_page(page);
                                 freed++;
                                 if (page2)
                                         page = page2->next_hash;
@@ -160,27 +163,31 @@
 static unsigned long dvmaiobase = 0;
 static unsigned long dvmaiosz __initdata = 0;
 
-/* #define E3000_DEBUG */
-
 __initfunc(void dvmaio_init(void))
 {
-	int i;
+	long i;
 	
 	if (!dvmaiobase) {
 		for (i = 0; sp_banks[i].num_bytes != 0; i++)
 			if (sp_banks[i].base_addr + sp_banks[i].num_bytes > dvmaiobase)
 				dvmaiobase = sp_banks[i].base_addr + sp_banks[i].num_bytes;
+
+		/* We map directly phys_base to phys_base+(4GB-DVMAIO_SIZE). */
+		dvmaiobase -= phys_base;
+
 		dvmaiobase = (dvmaiobase + DVMAIO_SIZE + 0x400000 - 1) & ~(0x400000 - 1);
 		for (i = 0; i < 6; i++)
-			if (dvmaiobase <= ((1024 * 64 * 1024) << i))
+			if (dvmaiobase <= ((1024L * 64 * 1024) << i))
 				break;
-		dvmaiobase = ((1024 * 64 * 1024) << i) - DVMAIO_SIZE;
+		dvmaiobase = ((1024L * 64 * 1024) << i) - DVMAIO_SIZE;
 		dvmaiosz = i;
 	}
 }
 
 __initfunc(void iommu_init(int iommu_node, struct linux_sbus *sbus))
 {
+	extern int this_is_starfire;
+	extern void *starfire_hookup(int);
 	struct iommu_struct *iommu;
 	struct sysio_regs *sregs;
 	struct linux_prom64_registers rprop;
@@ -191,10 +198,6 @@
 	int err, i, j;
 	
 	dvmaio_init();
-#ifdef E3000_DEBUG
-	prom_printf("\niommu_init: [%x:%p] ",
-		    iommu_node, sbus);
-#endif
 	err = prom_getproperty(iommu_node, "reg", (char *)&rprop,
 			       sizeof(rprop));
 	if(err == -1) {
@@ -203,19 +206,16 @@
 	}
 	sregs = (struct sysio_regs *) __va(rprop.phys_addr);
 
-#ifdef E3000_DEBUG
-	prom_printf("sregs[%p]\n");
-#endif
 	if(!sregs) {
 		prom_printf("iommu_init: Fatal error, sysio regs not mapped\n");
 		prom_halt();
 	}
 
 	iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC);
-
-#ifdef E3000_DEBUG
-	prom_printf("iommu_init: iommu[%p] ", iommu);
-#endif
+	if (!iommu) {
+		prom_printf("iommu_init: Fatal error, kmalloc(iommu) failed\n");
+		prom_halt();
+	}
 
 	spin_lock_init(&iommu->iommu_lock);
 	iommu->sysio_regs = sregs;
@@ -224,14 +224,19 @@
 	control = sregs->iommu_control;
 	impl = (control & IOMMU_CTRL_IMPL) >> 60;
 	vers = (control & IOMMU_CTRL_VERS) >> 56;
-#ifdef E3000_DEBUG
-	prom_printf("sreg_control[%08x]\n", control);
-	prom_printf("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n",
-		    (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs);
-#endif
 	printk("IOMMU(SBUS): IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n",
 	       (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs);
 	
+	/* Streaming buffer is unreliable on VERS 0 of SYSIO,
+	 * although such parts were never shipped in production
+	 * Sun hardware, I check just to be robust.  --DaveM
+	 */
+	vers = ((sregs->control & SYSIO_CONTROL_VER) >> 56);
+	if (vers == 0)
+		iommu->strbuf_enabled = 0;
+	else
+		iommu->strbuf_enabled = 1;
+
 	control &= ~(IOMMU_CTRL_TSBSZ);
 	control |= ((IOMMU_TSBSZ_2K * dvmaiosz) | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
 
@@ -281,9 +286,14 @@
 
 	/* Setup aliased mappings... */
 	for(i = 0; i < (dvmaiobase >> 16); i++) {
-		*iopte  = (IOPTE_VALID | IOPTE_64K | IOPTE_STBUF |
-			   IOPTE_CACHE | IOPTE_WRITE);
-		*iopte |= (i << 16);
+		unsigned long val = ((((unsigned long)i) << 16UL) + phys_base);
+
+		val |= IOPTE_VALID | IOPTE_64K | IOPTE_WRITE;
+		if (iommu->strbuf_enabled)
+			val |= IOPTE_STBUF;
+		else
+			val |= IOPTE_CACHE;
+		*iopte = val;
 		iopte++;
 	}
 
@@ -291,38 +301,34 @@
 	for( ; i < ((dvmaiobase + DVMAIO_SIZE) >> 16); i++)
 		*iopte++ = 0;
 
-#ifdef E3000_DEBUG
-	prom_printf("IOMMU: pte's mapped, enabling IOMMU... ");
-#endif
 	sregs->iommu_tsbbase = __pa(tsbbase);
 	sregs->iommu_control = control;
 
-#ifdef E3000_DEBUG
-	prom_printf("done\n");
-#endif
 	/* Get the streaming buffer going. */
 	control = sregs->sbuf_control;
 	impl = (control & SYSIO_SBUFCTRL_IMPL) >> 60;
 	vers = (control & SYSIO_SBUFCTRL_REV) >> 56;
-#ifdef E3000_DEBUG
-	prom_printf("IOMMU: enabling streaming buffer, control[%08x]... ",
-		    control);
-#endif
-	printk("IOMMU: Streaming Buffer IMPL[%x] REV[%x] ",
+	printk("IOMMU: Streaming Buffer IMPL[%x] REV[%x] ... ",
 	       (unsigned int)impl, (unsigned int)vers);
-	iommu->sbuf_flushflag_va = kmalloc(sizeof(unsigned long), GFP_DMA);
-	printk("FlushFLAG[%016lx] ... ", (iommu->sbuf_flushflag_pa = __pa(iommu->sbuf_flushflag_va)));
-	*(iommu->sbuf_flushflag_va) = 0;
-
-	sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN);
+	iommu->flushflag = 0;
 
-#ifdef E3000_DEBUG
-	prom_printf("done, returning\n");
-#endif
-	printk("ENABLED\n");
+	if (iommu->strbuf_enabled != 0) {
+		sregs->sbuf_control = (control | SYSIO_SBUFCTRL_SB_EN);
+		printk("ENABLED\n");
+	} else {
+		sregs->sbuf_control = (control & ~(SYSIO_SBUFCTRL_SB_EN));
+		printk("DISABLED\n");
+	}
 
 	/* Finally enable DVMA arbitration for all devices, just in case. */
 	sregs->sbus_control |= SYSIO_SBCNTRL_AEN;
+
+	/* If necessary, hook us up for starfire IRQ translations. */
+	sbus->upaid = prom_getintdefault(sbus->prom_node, "upa-portid", -1);
+	if(this_is_starfire)
+		sbus->starfire_cookie = starfire_hookup(sbus->upaid);
+	else
+		sbus->starfire_cookie = NULL;
 }
 
 void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr,
@@ -397,19 +403,35 @@
 
 __u32 mmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
 {
-	__u32 sbus_addr = (__u32) __pa(vaddr);
+	struct iommu_struct *iommu = sbus->iommu;
+	struct sysio_regs *sregs = iommu->sysio_regs;
+	unsigned long start = (unsigned long) vaddr;
+	unsigned long end = PAGE_ALIGN(start + len);
+	unsigned long flags, tmp;
+	volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control;
 
-#ifndef DEBUG_IOMMU
-	return sbus_addr;
-#else
-	if((sbus_addr < dvmaiobase) &&
-	   ((sbus_addr + len) < dvmaiobase))
-		return sbus_addr;
-
-	/* "can't happen"... GFP_DMA assures this. */
-	panic("Very high scsi_one mappings should never happen.");
-        return (__u32)0;
-#endif        
+	start &= PAGE_MASK;
+	if (end > MAX_DMA_ADDRESS) {
+		printk("mmu_get_scsi_one: Bogus DMA buffer address [%016lx:%d]\n",
+		       (unsigned long) vaddr, (int)len);
+		panic("DMA address too large, tell DaveM");
+	}
+
+	if (iommu->strbuf_enabled) {
+		spin_lock_irqsave(&iommu->iommu_lock, flags);
+		iommu->flushflag = 0;
+		while(start < end) {
+			sregs->sbuf_pflush = start;
+			start += PAGE_SIZE;
+		}
+		sregs->sbuf_fsync = __pa(&(iommu->flushflag));
+		tmp = *sbctrl;
+		while(iommu->flushflag == 0)
+			membar("#LoadLoad");
+		spin_unlock_irqrestore(&iommu->iommu_lock, flags);
+	}
+
+	return sbus_dvma_addr(vaddr);
 }
 
 void mmu_release_scsi_one(u32 vaddr, unsigned long len, struct linux_sbus *sbus)
@@ -418,43 +440,89 @@
 	struct sysio_regs *sregs = iommu->sysio_regs;
 	unsigned long start = (unsigned long) vaddr;
 	unsigned long end = PAGE_ALIGN(start + len);
-	unsigned long flags;
-	unsigned int *sync_word;
+	unsigned long flags, tmp;
+	volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control;
 
 	start &= PAGE_MASK;
 
-	spin_lock_irqsave(&iommu->iommu_lock, flags);
+	if (iommu->strbuf_enabled) {
+		spin_lock_irqsave(&iommu->iommu_lock, flags);
 
-	while(start < end) {
-		sregs->sbuf_pflush = start;
-		start += PAGE_SIZE;
-	}
-	sync_word = iommu->sbuf_flushflag_va;
-	sregs->sbuf_fsync = iommu->sbuf_flushflag_pa;
-	membar("#StoreLoad | #MemIssue");
-	while((*sync_word & 0x1) == 0)
-		membar("#LoadLoad");
-	*sync_word = 0;
+		/* 1) Clear the flush flag word */
+		iommu->flushflag = 0;
 
-	spin_unlock_irqrestore(&iommu->iommu_lock, flags);
+		/* 2) Tell the streaming buffer which entries
+		 *    we want flushed.
+		 */
+		while(start < end) {
+			sregs->sbuf_pflush = start;
+			start += PAGE_SIZE;
+		}
+
+		/* 3) Initiate flush sequence. */
+		sregs->sbuf_fsync = __pa(&(iommu->flushflag));
+
+		/* 4) Guarentee completion of all previous writes
+		 *    by reading SYSIO's SBUS control register.
+		 */
+		tmp = *sbctrl;
+
+		/* 5) Wait for flush flag to get set. */
+		while(iommu->flushflag == 0)
+			membar("#LoadLoad");
+
+		spin_unlock_irqrestore(&iommu->iommu_lock, flags);
+	}
 }
 
 void mmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
-	while(sz >= 0) {
-		__u32 page = (__u32) __pa(((unsigned long) sg[sz].addr));
-#ifndef DEBUG_IOMMU
-		sg[sz].dvma_addr = page;
-#else		
-		if((page < dvmaiobase) &&
-		   (page + sg[sz].len) < dvmaiobase) {
-			sg[sz].dvma_addr = page;
-		} else {
-			/* "can't happen"... GFP_DMA assures this. */
-			panic("scsi_sgl high mappings should never happen.");
+	struct iommu_struct *iommu = sbus->iommu;
+	struct sysio_regs *sregs = iommu->sysio_regs;
+	unsigned long flags, tmp;
+	volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control;
+
+	if (iommu->strbuf_enabled) {
+		spin_lock_irqsave(&iommu->iommu_lock, flags);
+		iommu->flushflag = 0;
+
+		while(sz >= 0) {
+			unsigned long start = (unsigned long)sg[sz].addr;
+			unsigned long end = PAGE_ALIGN(start + sg[sz].len);
+
+			if (end > MAX_DMA_ADDRESS) {
+				printk("mmu_get_scsi_sgl: Bogus DMA buffer address "
+				       "[%016lx:%d]\n", start, (int) sg[sz].len);
+				panic("DMA address too large, tell DaveM");
+			}
+
+			sg[sz--].dvma_addr = sbus_dvma_addr(start);
+			start &= PAGE_MASK;
+			while(start < end) {
+				sregs->sbuf_pflush = start;
+				start += PAGE_SIZE;
+			}
+		}
+
+		sregs->sbuf_fsync = __pa(&(iommu->flushflag));
+		tmp = *sbctrl;
+		while(iommu->flushflag == 0)
+			membar("#LoadLoad");
+		spin_unlock_irqrestore(&iommu->iommu_lock, flags);
+	} else {
+		/* Just verify the addresses and fill in the
+		 * dvma_addr fields in this case.
+		 */
+		while(sz >= 0) {
+			unsigned long start = (unsigned long)sg[sz].addr;
+			unsigned long end = PAGE_ALIGN(start + sg[sz].len);
+			if (end > MAX_DMA_ADDRESS) {
+				printk("mmu_get_scsi_sgl: Bogus DMA buffer address "
+				       "[%016lx:%d]\n", start, (int) sg[sz].len);
+				panic("DMA address too large, tell DaveM");
+			}
+			sg[sz--].dvma_addr = sbus_dvma_addr(start);
 		}
-#endif
-		sz--;
 	}
 }
 
@@ -462,30 +530,98 @@
 {
 	struct iommu_struct *iommu = sbus->iommu;
 	struct sysio_regs *sregs = iommu->sysio_regs;
-	unsigned long flags;
-	unsigned int *sync_word;
+	volatile u64 *sbctrl = (volatile u64 *) &sregs->sbus_control;
+	unsigned long flags, tmp;
 
-	spin_lock_irqsave(&iommu->iommu_lock, flags);
+	if (iommu->strbuf_enabled) {
+		spin_lock_irqsave(&iommu->iommu_lock, flags);
 
-	while(sz >= 0) {
-		unsigned long start = sg[sz].dvma_addr;
-		unsigned long end = PAGE_ALIGN(start + sg[sz].len);
+		/* 1) Clear the flush flag word */
+		iommu->flushflag = 0;
 
-		start &= PAGE_MASK;
-		while(start < end) {
-			sregs->sbuf_pflush = start;
-			start += PAGE_SIZE;
+		/* 2) Tell the streaming buffer which entries
+		 *    we want flushed.
+		 */
+		while(sz >= 0) {
+			unsigned long start = sg[sz].dvma_addr;
+			unsigned long end = PAGE_ALIGN(start + sg[sz].len);
+
+			start &= PAGE_MASK;
+			while(start < end) {
+				sregs->sbuf_pflush = start;
+				start += PAGE_SIZE;
+			}
+			sz--;
 		}
-		sz--;
+
+		/* 3) Initiate flush sequence. */
+		sregs->sbuf_fsync = __pa(&(iommu->flushflag));
+
+		/* 4) Guarentee completion of previous writes
+		 *    by reading SYSIO's SBUS control register.
+		 */
+		tmp = *sbctrl;
+
+		/* 5) Wait for flush flag to get set. */
+		while(iommu->flushflag == 0)
+			membar("#LoadLoad");
+
+		spin_unlock_irqrestore(&iommu->iommu_lock, flags);
 	}
-	sync_word = iommu->sbuf_flushflag_va;
-	sregs->sbuf_fsync = iommu->sbuf_flushflag_pa;
-	membar("#StoreLoad | #MemIssue");
-	while((*sync_word & 0x1) == 0)
-		membar("#LoadLoad");
-	*sync_word = 0;
+}
+
+void mmu_set_sbus64(struct linux_sbus_device *sdev, int bursts)
+{
+	struct linux_sbus *sbus = sdev->my_bus;
+	struct sysio_regs *sregs = sbus->iommu->sysio_regs;
+	int slot = sdev->slot;
+	u64 *cfg, tmp;
+
+	switch(slot) {
+	case 0:
+		cfg = &sregs->sbus_s0cfg;
+		break;
+	case 1:
+		cfg = &sregs->sbus_s1cfg;
+		break;
+	case 2:
+		cfg = &sregs->sbus_s2cfg;
+		break;
+	case 3:
+		cfg = &sregs->sbus_s3cfg;
+		break;
+
+	case 13:
+		cfg = &sregs->sbus_s4cfg;
+		break;
+	case 14:
+		cfg = &sregs->sbus_s5cfg;
+		break;
+	case 15:
+		cfg = &sregs->sbus_s6cfg;
+		break;
+
+	default:
+		return;
+	};
+
+	/* ETM already enabled?  If so, we're done. */
+	tmp = *cfg;
+	if ((tmp & SYSIO_SBSCFG_ETM) != 0)
+		return;
 
-	spin_unlock_irqrestore(&iommu->iommu_lock, flags);
+	/* Set burst bits. */
+	if (bursts & DMA_BURST8)
+		tmp |= SYSIO_SBSCFG_BA8;
+	if (bursts & DMA_BURST16)
+		tmp |= SYSIO_SBSCFG_BA16;
+	if (bursts & DMA_BURST32)
+		tmp |= SYSIO_SBSCFG_BA32;
+	if (bursts & DMA_BURST64)
+		tmp |= SYSIO_SBSCFG_BA64;
+
+	/* Finally turn on ETM and set register. */
+	*cfg = (tmp | SYSIO_SBSCFG_ETM);
 }
 
 int mmu_info(char *buf)
@@ -562,16 +698,9 @@
  */
 static void __flush_nucleus_vptes(void)
 {
-	unsigned long pstate;
 	unsigned long prom_reserved_base = 0xfffffffc00000000UL;
 	int i;
 
-	__asm__ __volatile__("flushw\n\t"
-			     "rdpr	%%pstate, %0\n\t"
-			     "wrpr	%0, %1, %%pstate"
-			     : "=r" (pstate)
-			     : "i" (PSTATE_IE));
-
 	/* Only DTLB must be checked for VPTE entries. */
 	for(i = 0; i < 63; i++) {
 		unsigned long tag = spitfire_get_dtlb_tag(i);
@@ -586,8 +715,6 @@
 			membar("#Sync");
 		}
 	}
-	__asm__ __volatile__("wrpr	%0, 0, %%pstate"
-			     : : "r" (pstate));
 }
 
 static int prom_ditlb_set = 0;
@@ -600,10 +727,19 @@
 
 void prom_world(int enter)
 {
+	unsigned long pstate;
 	int i;
 
 	if (!prom_ditlb_set)
 		return;
+
+	/* Make sure the following runs atomically. */
+	__asm__ __volatile__("flushw\n\t"
+			     "rdpr	%%pstate, %0\n\t"
+			     "wrpr	%0, %1, %%pstate"
+			     : "=r" (pstate)
+			     : "i" (PSTATE_IE));
+
 	if (enter) {
 		/* Kick out nucleus VPTEs. */
 		__flush_nucleus_vptes();
@@ -648,6 +784,8 @@
 			}
 		}
 	}
+	__asm__ __volatile__("wrpr	%0, 0, %%pstate"
+			     : : "r" (pstate));
 }
 
 void inherit_locked_prom_mappings(int save_p)
@@ -680,7 +818,7 @@
 		unsigned long data;
 
 		data = spitfire_get_dtlb_data(i);
-		if(data & _PAGE_L) {
+		if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) {
 			unsigned long tag = spitfire_get_dtlb_tag(i);
 
 			if(save_p) {
@@ -703,7 +841,7 @@
 		unsigned long data;
 
 		data = spitfire_get_itlb_data(i);
-		if(data & _PAGE_L) {
+		if((data & (_PAGE_L|_PAGE_VALID)) == (_PAGE_L|_PAGE_VALID)) {
 			unsigned long tag = spitfire_get_itlb_tag(i);
 
 			if(save_p) {
@@ -867,15 +1005,11 @@
 struct pgtable_cache_struct pgt_quicklists;
 #endif
 
-/* XXX Add __GFP_HIGH to these calls to "fool" page allocator
- * XXX so we don't go to swap so quickly... then do the same
- * XXX for get_user_page as well -DaveM
- */
 pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset)
 {
 	pmd_t *pmd;
 
-	pmd = (pmd_t *) __get_free_page(GFP_DMA|GFP_KERNEL);
+	pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
 	if(pmd) {
 		memset(pmd, 0, PAGE_SIZE);
 		pgd_set(pgd, pmd);
@@ -888,7 +1022,7 @@
 {
 	pte_t *pte;
 
-	pte = (pte_t *) __get_free_page(GFP_DMA|GFP_KERNEL);
+	pte = (pte_t *) __get_free_page(GFP_KERNEL);
 	if(pte) {
 		memset(pte, 0, PAGE_SIZE);
 		pmd_set(pmd, pte);
@@ -997,11 +1131,11 @@
 __initfunc(unsigned long 
 paging_init(unsigned long start_mem, unsigned long end_mem))
 {
-	extern void setup_tba(void);
 	extern pmd_t swapper_pmd_dir[1024];
-	extern unsigned long irq_init(unsigned long start_mem, unsigned long end_mem);
-	extern unsigned int sparc64_vpte_patchme[1];
+	extern unsigned int sparc64_vpte_patchme1[1];
+	extern unsigned int sparc64_vpte_patchme2[1];
 	unsigned long alias_base = phys_base + PAGE_OFFSET;
+	unsigned long second_alias_page = 0;
 	unsigned long pt;
 	unsigned long flags;
 	unsigned long shift = alias_base - ((unsigned long)&empty_zero_page);
@@ -1026,6 +1160,21 @@
 	: "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pt),
 	  "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3)
 	: "memory");
+	if (start_mem >= KERNBASE + 0x340000) {
+		second_alias_page = alias_base + 0x400000;
+		__asm__ __volatile__("
+		stxa	%1, [%0] %3
+		stxa	%2, [%5] %4
+		membar	#Sync
+		flush	%%g6
+		nop
+		nop
+		nop"
+		: /* No outputs */
+		: "r" (TLB_TAG_ACCESS), "r" (second_alias_page), "r" (pt + 0x400000),
+		  "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (60 << 3)
+		: "memory");
+	}
 	__restore_flags(flags);
 	
 	/* Now set kernel pgd to upper alias so physical page computations
@@ -1038,10 +1187,10 @@
 	/* Now can init the kernel/bad page tables. */
 	pgd_set(&swapper_pg_dir[0], swapper_pmd_dir + (shift / sizeof(pgd_t)));
 	
-	sparc64_vpte_patchme[0] |= (init_mm.pgd[0] >> 10);
+	sparc64_vpte_patchme1[0] |= (init_mm.pgd[0] >> 10);
+	sparc64_vpte_patchme2[0] |= (init_mm.pgd[0] & 0x3ff);
+	flushi((long)&sparc64_vpte_patchme1[0]);
 	
-	start_mem = irq_init(start_mem, end_mem);
-
 	/* We use mempool to create page tables, therefore adjust it up
 	 * such that __pa() macros etc. work.
 	 */
@@ -1051,9 +1200,20 @@
 	allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000);
 	inherit_prom_mappings();
 	
-
-	/* Ok, we can use our TLB miss and window trap handlers safely. */
-	setup_tba();
+	/* Ok, we can use our TLB miss and window trap handlers safely.
+	 * We need to do a quick peek here to see if we are on StarFire
+	 * or not, so setup_tba can setup the IRQ globals correctly (it
+	 * needs to get the hard smp processor id correctly).
+	 */
+	{
+		extern void setup_tba(int);
+		int is_starfire = prom_finddevice("/ssp-serial");
+		if(is_starfire != 0 && is_starfire != -1)
+			is_starfire = 1;
+		else
+			is_starfire = 0;
+		setup_tba(is_starfire);
+	}
 
 	/* Really paranoid. */
 	flushi((long)&empty_zero_page);
@@ -1064,6 +1224,8 @@
 	 */
 	/* We only created DTLB mapping of this stuff. */
 	spitfire_flush_dtlb_nucleus_page(alias_base);
+	if (second_alias_page)
+		spitfire_flush_dtlb_nucleus_page(second_alias_page);
 	membar("#Sync");
 
 	/* Paranoid */
@@ -1079,10 +1241,6 @@
 	return device_scan (PAGE_ALIGN (start_mem));
 }
 
-/* XXX Add also PG_Hole flag, set it in the page structs here,
- * XXX remove FREE_UNUSED_MEM_MAP code, and the nfsd file handle
- * problems will all be gone.  -DaveM
- */
 __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
 {
 	unsigned long tmp = 0, paddr, endaddr;
@@ -1096,14 +1254,24 @@
 		if (!sp_banks[tmp].num_bytes) {
 			mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip);
 			mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
+			mem_map[(paddr>>PAGE_SHIFT)+1UL].flags |= (1<<PG_skip);
+			mem_map[(paddr>>PAGE_SHIFT)+1UL].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
 			return;
 		}
 		
 		if (sp_banks[tmp].base_addr > paddr) {
-			/* Making a one or two pages PG_skip holes is not necessary */
-			if (sp_banks[tmp].base_addr - paddr > 2 * PAGE_SIZE) {
+			/* Making a one or two pages PG_skip holes
+			 * is not necessary.  We add one more because
+			 * we must set the PG_skip flag on the first
+			 * two mem_map[] entries for the hole.  Go and
+			 * see the mm/filemap.c:shrink_mmap() loop for
+			 * details. -DaveM
+			 */
+			if (sp_banks[tmp].base_addr - paddr > 3 * PAGE_SIZE) {
 				mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip);
 				mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT);
+				mem_map[(paddr>>PAGE_SHIFT)+1UL].flags |= (1<<PG_skip);
+				mem_map[(paddr>>PAGE_SHIFT)+1UL].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT);
 			}
 			paddr = sp_banks[tmp].base_addr;
 		}
@@ -1111,7 +1279,8 @@
 		endaddr = sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes;
 		while (paddr < endaddr) {
 			mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_reserved);
-			if (paddr >= dvmaiobase)
+			set_bit(paddr >> 22, sparc64_valid_addr_bitmap);
+			if (paddr >= (MAX_DMA_ADDRESS - PAGE_OFFSET))
 				mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_DMA);
 			paddr += PAGE_SIZE;
 		}
@@ -1131,6 +1300,12 @@
 	end_mem &= PAGE_MASK;
 	max_mapnr = MAP_NR(end_mem);
 	high_memory = (void *) end_mem;
+	
+	sparc64_valid_addr_bitmap = (unsigned long *)start_mem;
+	i = max_mapnr >> ((22 - PAGE_SHIFT) + 6);
+	i += 1;
+	memset(sparc64_valid_addr_bitmap, 0, i << 3);
+	start_mem += i << 3;
 
 	start_mem = PAGE_ALIGN(start_mem);
 	num_physpages = 0;
@@ -1138,6 +1313,8 @@
 	if (phys_base) {
 		mem_map[0].flags |= (1<<PG_skip) | (1<<PG_reserved);
 		mem_map[0].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
+		mem_map[1].flags |= (1<<PG_skip) | (1<<PG_reserved);
+		mem_map[1].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
 	}
 
 	addr = PAGE_OFFSET + phys_base;
@@ -1148,6 +1325,7 @@
 		else
 #endif	
 			mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+		set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap);
 		addr += PAGE_SIZE;
 	}
 
@@ -1159,6 +1337,9 @@
 		if (PageSkip(page)) {
 			unsigned long low, high;
 			
+			/* See taint_real_pages() for why this is done.  -DaveM */
+			page++;
+
 			low = PAGE_ALIGN((unsigned long)(page+1));
 			if (page->next_hash < page)
 				high = ((unsigned long)end) & PAGE_MASK;
@@ -1255,7 +1436,7 @@
 
 	val->totalram = 0;
 	val->sharedram = 0;
-	val->freeram = nr_free_pages << PAGE_SHIFT;
+	val->freeram = ((unsigned long)nr_free_pages) << PAGE_SHIFT;
 	val->bufferram = buffermem;
 	for (page = mem_map, end = mem_map + max_mapnr;
 	     page < end; page++) {
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/prom/memory.c linux/arch/sparc64/prom/memory.c
--- v2.2.3/linux/arch/sparc64/prom/memory.c	Mon Mar 17 14:54:24 1997
+++ linux/arch/sparc64/prom/memory.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: memory.c,v 1.3 1997/03/04 16:27:10 jj Exp $
+/* $Id: memory.c,v 1.4 1998/11/25 10:04:06 jj Exp $
  * memory.c: Prom routine for acquiring various bits of information
  *           about RAM on the machine, both virtual and physical.
  *
@@ -41,8 +41,8 @@
 prom_sortmemlist(struct linux_mlist_p1275 *thislist))
 {
 	int swapi = 0;
-	int i, mitr, tmpsize;
-	unsigned long tmpaddr;
+	int i, mitr;
+	unsigned long tmpaddr, tmpsize;
 	unsigned long lowest;
 
 	for(i=0; thislist[i].theres_more != 0; i++) {
@@ -79,7 +79,7 @@
 		prom_phys_avail[iter].start_adr =
 			prom_reg_memlist[iter].phys_addr;
 		prom_phys_avail[iter].num_bytes =
-			(unsigned long) prom_reg_memlist[iter].reg_size;
+			prom_reg_memlist[iter].reg_size;
 		prom_phys_avail[iter].theres_more =
 			&prom_phys_avail[iter+1];
 	}
@@ -93,7 +93,7 @@
 		prom_phys_total[iter].start_adr =
 			prom_reg_memlist[iter].phys_addr;
 		prom_phys_total[iter].num_bytes =
-			(unsigned long) prom_reg_memlist[iter].reg_size;
+			prom_reg_memlist[iter].reg_size;
 		prom_phys_total[iter].theres_more =
 			&prom_phys_total[iter+1];
 	}
@@ -112,7 +112,7 @@
 		prom_prom_taken[iter].start_adr =
 			prom_reg_memlist[iter].phys_addr;
 		prom_prom_taken[iter].num_bytes =
-			(unsigned long) prom_reg_memlist[iter].reg_size;
+			prom_reg_memlist[iter].reg_size;
 		prom_prom_taken[iter].theres_more =
 			&prom_phys_total[iter+1];
 	}
@@ -130,7 +130,7 @@
 			prom_prom_taken[iter].start_adr;
 	}
 	prom_prom_taken[iter-1].num_bytes =
-		((unsigned long)-1) - (unsigned long) prom_prom_taken[iter-1].start_adr;
+		-1UL - prom_prom_taken[iter-1].start_adr;
 
 	/* Sort the other two lists. */
 	prom_sortmemlist(prom_phys_total);
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/prom/misc.c linux/arch/sparc64/prom/misc.c
--- v2.2.3/linux/arch/sparc64/prom/misc.c	Sun Nov  8 14:02:50 1998
+++ linux/arch/sparc64/prom/misc.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.13 1998/10/13 14:03:49 davem Exp $
+/* $Id: misc.c,v 1.14 1998/12/18 10:01:59 davem Exp $
  * misc.c:  Miscellaneous prom functions that don't belong
  *          anywhere else.
  *
@@ -122,9 +122,123 @@
 	p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba);
 }
 
+/* This is only used internally below. */
+static int prom_get_mmu_ihandle(void)
+{
+	int node;
+	int ret;
+
+	node = prom_finddevice("/chosen");
+	ret = prom_getint(node, "mmu");
+	if(ret == -1 || ret == 0) {
+		prom_printf("PROMLIB: Fatal error, cannot get mmu ihandle.\n");
+		prom_halt();
+	}
+	return ret;
+}
+
+/* Load explicit I/D TLB entries. */
+long prom_itlb_load(unsigned long index,
+		    unsigned long tte_data,
+		    unsigned long vaddr)
+{
+	return p1275_cmd("call-method",
+			 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)),
+			 "SUNW,itlb-load",
+			 prom_get_mmu_ihandle(),
+			 /* And then our actual args are pushed backwards. */
+			 vaddr,
+			 tte_data,
+			 index);
+}
+
+long prom_dtlb_load(unsigned long index,
+		    unsigned long tte_data,
+		    unsigned long vaddr)
+{
+	return p1275_cmd("call-method",
+			 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 1)),
+			 "SUNW,dtlb-load",
+			 prom_get_mmu_ihandle(),
+			 /* And then our actual args are pushed backwards. */
+			 vaddr,
+			 tte_data,
+			 index);
+}
+
+/* Set aside physical memory which is not touched or modified
+ * across soft resets.
+ */
+unsigned long prom_retain(char *name,
+			  unsigned long pa_low, unsigned long pa_high,
+			  long size, long align)
+{
+	/* XXX I don't think we return multiple values correctly.
+	 * XXX OBP supposedly returns pa_low/pa_high here, how does
+	 * XXX it work?
+	 */
+
+	/* If align is zero, the pa_low/pa_high args are passed,
+	 * else they are not.
+	 */
+	if(align == 0)
+		return p1275_cmd("SUNW,retain",
+				 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)),
+				 name, pa_low, pa_high, size, align);
+	else
+		return p1275_cmd("SUNW,retain",
+				 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)),
+				 name, size, align);
+}
+
+/* Get "Unumber" string for the SIMM at the given
+ * memory address.  Usually this will be of the form
+ * "Uxxxx" where xxxx is a decimal number which is
+ * etched into the motherboard next to the SIMM slot
+ * in question.
+ */
+int prom_getunumber(unsigned long phys_lo, unsigned long phys_hi,
+		    char *buf, int buflen)
+{
+	return p1275_cmd("SUNW,get-unumber",
+			 (P1275_ARG(2, P1275_ARG_OUT_BUF) | P1275_INOUT(4, 1)),
+			 phys_lo, phys_hi, buf, buflen);
+}
+
+/* Power management extensions. */
+void prom_sleepself(void)
+{
+	p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0));
+}
+
+int prom_sleepsystem(void)
+{
+	return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1));
+}
+
+int prom_wakeupsystem(void)
+{
+	return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1));
+}
+
 #ifdef __SMP__
 void prom_startcpu(int cpunode, unsigned long pc, unsigned long o0)
 {
 	p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0);
+}
+
+void prom_stopself(void)
+{
+	p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0));
+}
+
+void prom_idleself(void)
+{
+	p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0));
+}
+
+void prom_resumecpu(int cpunode)
+{
+	p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode);
 }
 #endif
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/solaris/fs.c linux/arch/sparc64/solaris/fs.c
--- v2.2.3/linux/arch/sparc64/solaris/fs.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/solaris/fs.c	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: fs.c,v 1.11 1998/10/28 08:12:04 jj Exp $
+/* $Id: fs.c,v 1.12 1999/01/02 16:46:06 davem Exp $
  * fs.c: fs related syscall emulation for Solaris
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -7,6 +7,7 @@
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
+#include <linux/mm.h>
 #include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/smp_lock.h>
@@ -21,7 +22,6 @@
 #include "conv.h"
 
 extern char * getname32(u32 filename);
-#define putname32 putname
  
 #define R4_DEV(DEV) ((DEV & 0xff) | ((DEV & 0xff00) << 10))
 #define R4_MAJOR(DEV) (((DEV) >> 18) & 0x3fff)
@@ -138,7 +138,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat ((struct sol_stat *)A(statbuf), &s))
 			return -EFAULT;
 	}
@@ -166,7 +166,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat64 ((struct sol_stat64 *)A(statbuf), &s))
 			return -EFAULT;
 	}
@@ -188,7 +188,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newlstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat ((struct sol_stat *)A(statbuf), &s))
 			return -EFAULT;
 	}
@@ -215,7 +215,7 @@
 		set_fs (KERNEL_DS);
 		ret = sys_newlstat(filenam, &s);
 		set_fs (old_fs);
-		putname32 (filenam);
+		putname (filenam);
 		if (putstat64 ((struct sol_stat64 *)A(statbuf), &s))
 			return -EFAULT;
 	}
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/solaris/systbl.S linux/arch/sparc64/solaris/systbl.S
--- v2.2.3/linux/arch/sparc64/solaris/systbl.S	Thu Nov 19 09:56:28 1998
+++ linux/arch/sparc64/solaris/systbl.S	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: systbl.S,v 1.7 1998/10/28 08:11:49 jj Exp $
+/* $Id: systbl.S,v 1.8 1999/02/11 18:34:02 davem Exp $
  * systbl.S: System call entry point table for Solaris compatibility.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -72,7 +72,7 @@
 	.word CHAIN(dup)		/* dup		d	41	*/
 	.word CHAIN(pipe)		/* pipe			42	*/
 	.word CHAIN(times)		/* times	p	43	*/
-	.word CHAIN(profil)		/* prof		xxxx	44	*/
+	.word 44 /*CHAIN(profil)*/	/* prof		xxxx	44	*/
 	.word solaris_unimplemented	/* lock/plock		45	*/
 	.word CHAIN(setgid)		/* setgid	d	46	*/
 	.word solaris_getgid		/* getgid		47	*/
diff -u --recursive --new-file v2.2.3/linux/drivers/ap1000/apfddi.c linux/drivers/ap1000/apfddi.c
--- v2.2.3/linux/drivers/ap1000/apfddi.c	Fri May  8 23:14:47 1998
+++ linux/drivers/ap1000/apfddi.c	Wed Mar 10 16:51:35 1999
@@ -30,7 +30,6 @@
 #include <asm/io.h>
 
 #include <linux/inet.h>
-#include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/ap1000/ringbuf.c linux/drivers/ap1000/ringbuf.c
--- v2.2.3/linux/drivers/ap1000/ringbuf.c	Wed Aug 26 11:37:34 1998
+++ linux/drivers/ap1000/ringbuf.c	Wed Mar 10 16:51:35 1999
@@ -22,7 +22,6 @@
 #include <asm/segment.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
-#include <asm/segment.h>
 #include <asm/uaccess.h>
 
 #include <asm/ap1000/pgtapmmu.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/block/cmd646.c linux/drivers/block/cmd646.c
--- v2.2.3/linux/drivers/block/cmd646.c	Thu Aug  6 14:06:31 1998
+++ linux/drivers/block/cmd646.c	Mon Mar 15 16:11:29 1999
@@ -1,4 +1,4 @@
-/* $Id: cmd646.c,v 1.10 1998/08/03 15:28:42 davem Exp $
+/* $Id: cmd646.c,v 1.11 1998/12/13 08:36:54 davem Exp $
  * cmd646.c: Enable interrupts at initialization time on Ultra/PCI machines.
  *           Note, this driver is not used at all on other systems because
  *           there the "BIOS" has done all of the following already.
@@ -219,8 +219,11 @@
 
 	hwif->chipset = ide_cmd646;
 
-	/* Set a good latency timer value. */
-	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 240);
+	/* Set a good latency timer and cache line size value. */
+	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+	(void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
 
 	/* Setup interrupts. */
 	(void) pci_read_config_byte(dev, 0x71, &mrdmode);
diff -u --recursive --new-file v2.2.3/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.2.3/linux/drivers/block/genhd.c	Wed Mar 10 15:29:45 1999
+++ linux/drivers/block/genhd.c	Sun Mar 21 07:11:36 1999
@@ -867,19 +867,20 @@
 	int			 nr_sects;
 	int			 blk;
 	int			 part, res;
+	int			 old_blocksize;
+	int			 blocksize;
 
-	/*
-	 *	Don't bother touching M/O 2K media.
-	 */
-	 
-	if (get_ptable_blocksize(dev) != 1024)
-		return 0;
-		
-	set_blocksize(dev,512);
+	old_blocksize = get_ptable_blocksize(dev);
+	if (hardsect_size[MAJOR(dev)] != NULL)
+		blocksize = hardsect_size[MAJOR(dev)][MINOR(dev)];
+	else
+		blocksize = 512;
+
+	set_blocksize(dev,blocksize);
 	res = 0;
 
 	for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) {
-		if(!(bh = bread(dev,blk,512))) {
+		if(!(bh = bread(dev,blk,blocksize))) {
 			printk("Dev %s: unable to read RDB block %d\n",
 			       kdevname(dev),blk);
 			goto rdb_done;
@@ -887,16 +888,25 @@
 		if (*(u32 *)bh->b_data == htonl(IDNAME_RIGIDDISK)) {
 			rdb = (struct RigidDiskBlock *)bh->b_data;
 			if (checksum_block((u32 *)bh->b_data,htonl(rdb->rdb_SummedLongs) & 0x7F)) {
-				printk("Dev %s: RDB in block %d has bad checksum\n",
-				       kdevname(dev),blk);
-				brelse(bh);
-				continue;
+				/* Try again with 0xdc..0xdf zeroed, Windows might have
+				 * trashed it.
+				 */
+				*(u32 *)(&bh->b_data[0xdc]) = 0;
+				if (checksum_block((u32 *)bh->b_data,
+						htonl(rdb->rdb_SummedLongs) & 0x7F)) {
+					brelse(bh);
+					printk("Dev %s: RDB in block %d has bad checksum\n",
+					       kdevname(dev),blk);
+					continue;
+				}
+				printk("Warning: Trashed word at 0xd0 in block %d "
+					"ignored in checksum calculation\n",kdevname(dev),blk);
 			}
 			printk(" RDSK");
 			blk = htonl(rdb->rdb_PartitionList);
 			brelse(bh);
 			for (part = 1; blk > 0 && part <= 16; part++) {
-				if (!(bh = bread(dev,blk, 512))) {
+				if (!(bh = bread(dev,blk,blocksize))) {
 					printk("Dev %s: unable to read partition block %d\n",
 						       kdevname(dev),blk);
 					goto rdb_done;
@@ -929,11 +939,7 @@
 	}
 
 rdb_done:
-	/*
-	 *	FIXME: should restore the original size. Then we could clean
-	 *	up the M/O skip. Amiga people ?
-	 */
-	set_blocksize(dev,BLOCK_SIZE);
+	set_blocksize(dev,old_blocksize);
 	return res;
 }
 #endif /* CONFIG_AMIGA_PARTITION */
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.2.3/linux/drivers/block/ide-cd.c	Wed Jan 20 23:14:04 1999
+++ linux/drivers/block/ide-cd.c	Wed Mar 10 17:01:57 1999
@@ -33,6 +33,7 @@
  *   boot
  * -Integrate DVD-ROM support in driver. Thanks to Merete Gotsæd-Petersen
  *   of Pioneer Denmark for providing me with a drive for testing.
+ * -Implement Features and Profiles.
  *
  *
  * ----------------------------------
@@ -230,9 +231,23 @@
  * 4.52  Jan 19, 1999  -- Jens Axboe <axboe@image.dk>
  *                      - Detect DVD-ROM/RAM drives
  *
+ * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
+ *                         drive in transfer size limit.
+ *                      - Fix the I/O error when doing eject without a medium
+ *                         loaded on some drives.
+ *                      - CDROMREADMODE2 is now implemented through
+ *                         CDROMREADRAW, since many drives don't support
+ *                         MODE2 (even though ATAPI 2.6 says they must).
+ *                      - Added ignore parameter to ide-cd (as a module), eg
+ *                         	insmod ide-cd ignore='hda hdb'
+ *                         Useful when using ide-cd in conjunction with
+ *                         ide-scsi. TODO: non-modular way of doing the
+ *                         same.
+ *                         
+ *
  *************************************************************************/
 
-#define IDECD_VERSION "4.52"
+#define IDECD_VERSION "4.53"
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -252,7 +267,6 @@
 #include "ide.h"
 #include "ide-cd.h"
 
-
 /****************************************************************************
  * Generic packet command support and error handling routines.
  */
@@ -1527,6 +1541,10 @@
 		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
 		stat = 0;
 	}
+	
+	/* no medium, that's alright. */
+	if (stat != 0 && reqbuf->sense_key == NOT_READY && reqbuf->asc == 0x3a)
+		stat = 0;
 
 	if (stat == 0)
 		CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
@@ -1806,7 +1824,6 @@
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-
 /* ATAPI cdrom drives are free to select the speed you request or any slower
    rate :-( Requesting too fast a speed will _not_ produce an error. */
 static int
@@ -2092,22 +2109,17 @@
 		if (cmd == CDROMREADMODE1) {
 			blocksize = CD_FRAMESIZE;
 			format = 2;
-		} else if (cmd == CDROMREADMODE2) {
-			blocksize = CD_FRAMESIZE_RAW0;
-			format = 3;
-		} else {
+		} else {	/* for RAW and MODE2. */
 			blocksize = CD_FRAMESIZE_RAW;
 			format = 0;
 		}
-		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
-		if (stat) return stat;
+		
+		copy_from_user_ret(&msf, (void *)arg, sizeof (msf), -EFAULT); 
 
-		copy_from_user (&msf, (void *)arg, sizeof (msf));
+		lba = msf_to_lba(msf.cdmsf_min0, 
+				 msf.cdmsf_sec0, 
+				 msf.cdmsf_frame0);
 
-		lba = msf_to_lba (msf.cdmsf_min0,
-				  msf.cdmsf_sec0,
-				  msf.cdmsf_frame0);
-	
 		/* Make sure the TOC is up to date. */
 		stat = cdrom_read_toc (drive, NULL);
 		if (stat) return stat;
@@ -2117,14 +2129,21 @@
 		if (lba < 0 || lba >= toc->capacity)
 			return -EINVAL;
 
-		buf = (char *) kmalloc (CD_FRAMESIZE_RAW, GFP_KERNEL);
+		buf = (char *) kmalloc (blocksize, GFP_KERNEL);
 		if (buf == NULL)
 			return -ENOMEM;
 
 		stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize,
 					 NULL);
-		if (stat == 0)
-			copy_to_user ((char *)arg, buf, blocksize);
+
+		if (stat == 0) {
+			if (cmd == CDROMREADMODE2) {
+				/* For Mode2, skip the Sync, Header, and Subheader */
+				copy_to_user_ret((char *)arg, buf+16, CD_FRAMESIZE_RAW0, -EFAULT);
+			} else {
+				copy_to_user_ret((char *)arg, buf, blocksize, -EFAULT);
+			}
+		}
 
 		kfree (buf);
 		return stat;
@@ -2486,14 +2505,12 @@
 static
 int ide_cdrom_reset (struct cdrom_device_info *cdi)
 {
-
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct request req;
 
 	ide_init_drive_cmd (&req);
 	req.cmd = RESET_DRIVE_COMMAND;
 	return ide_do_drive_cmd (drive, &req, ide_wait);
-
 }
 
 
@@ -2501,9 +2518,10 @@
 int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct atapi_request_sense rq;
 
 	if (position) {
-		int stat = cdrom_lockdoor (drive, 0, NULL);
+		int stat = cdrom_lockdoor (drive, 0, &rq);
 		if (stat) return stat;
 	}
 
@@ -2980,12 +2998,17 @@
 	CDROM_CONFIG_FLAGS (drive)->no_eject = 1;
 	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
 	
-	/* limit transfer size per interrupt. currently only one Samsung
-	   drive needs this. */
+	/* limit transfer size per interrupt. */
 	CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0;
-	if (drive->id != NULL)
-		if (strcmp (drive->id->model, "SAMSUNG CD-ROM SCR-2432") == 0)
+	if (drive->id != NULL) {
+		if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430"))
+			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+		else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432"))
 			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+		else if (!strcmp (drive->id->model, "GCD-R580B"))
+			CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1;
+				/* 124/SECTORS_PER_FRAME; ? */
+	}
 
 #if ! STANDARD_ATAPI
 	/* by default Sanyo 3 CD changer support is turned off and
@@ -3155,7 +3178,13 @@
 	NULL
 };
 
+/* options */
+char *ignore = NULL;
+
 #ifdef MODULE
+MODULE_PARM(ignore, "s");
+MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
+
 int init_module (void)
 {
 	return ide_cdrom_init();
@@ -3183,6 +3212,12 @@
 
 	MOD_INC_USE_COUNT;
 	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
+		/* skip drives that we were told to ignore */
+		if (ignore != NULL)
+			if (strstr(ignore, drive->name)) {
+				printk("ide-cd: ignoring drive %s\n", drive->name);
+				continue;
+			}
 		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
 		if (info == NULL) {
 			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.2.3/linux/drivers/block/ide-cd.h	Wed Jan 20 23:14:04 1999
+++ linux/drivers/block/ide-cd.h	Sun Mar 21 14:18:22 1999
@@ -85,6 +85,9 @@
 #define MECHANISM_STATUS        0xbd
 #define READ_CD                 0xbe
 
+/* DVD Opcodes */
+#define DVD_GET_PERFORMANCE	0xac
+
 
 /* Page codes for mode sense/set */
 
@@ -562,7 +565,7 @@
 
 	/* Sector buffer.  If a read request wants only the first part
 	   of a cdrom block, we cache the rest of the block here,
-	   in the expectation that that data is going to be wanted soon.
+	   in the expectation that the data is going to be wanted soon.
 	   SECTOR_BUFFERED is the number of the first buffered sector,
 	   and NSECTORS_BUFFERED is the number of sectors in the buffer.
 	   Before the buffer is allocated, we should have
@@ -656,6 +659,7 @@
 	{ PLAY_CD, "Play CD" },
 	{ MECHANISM_STATUS, "Mechanism Status" },
 	{ READ_CD, "Read CD" },
+	{ DVD_GET_PERFORMANCE, "Get Performance" },
 };
 
 
@@ -776,7 +780,8 @@
 
 	{ 0x6400, "Illegal mode for this track or incompatible medium" },
 
-	{ 0xb900, "Play operation oborted (sic)" },
+	/* Following error is misspelled in ATAPI 2.6 */
+	{ 0xb900, "Play operation oborted [sic]" },
 
 	{ 0xbf00, "Loss of streaming" },
 };
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.2.3/linux/drivers/block/ide-disk.c	Tue Feb 23 15:21:33 1999
+++ linux/drivers/block/ide-disk.c	Wed Mar 10 17:49:43 1999
@@ -670,10 +670,15 @@
 	if (id == NULL)
 		return;
 
-	/* check for removable disks (eg. SYQUEST), ignore 'WD' drives */
-	if (id->config & (1<<7)) {	/* removable disk ? */
+	/*
+	 * CompactFlash cards and their brethern look just like hard drives
+	 * to us, but they are removable and don't have a doorlock mechanism.
+	 */
+	if (drive->removable && !drive_is_flashcard(drive)) {
+		/*
+		 * Removable disks (eg. SYQUEST); ignore 'WD' drives 
+		 */
 		if (id->model[0] != 'W' || id->model[1] != 'D') {
-			drive->removable = 1;
 			drive->doorlocking = 1;
 		}
 	}
@@ -804,12 +809,6 @@
 	
 	MOD_INC_USE_COUNT;
 	while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) {
-
-		/* SunDisk drives: ignore "second" drive;   can mess up non-Sun systems!  FIXME */
-		struct hd_driveid *id = drive->id;
-		if (id && id->model[0] == 'S' && id->model[1] == 'u' && drive->select.b.unit)
-			continue;
-
 		if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) {
 			printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name);
 			continue;
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-pmac.c linux/drivers/block/ide-pmac.c
--- v2.2.3/linux/drivers/block/ide-pmac.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/block/ide-pmac.c	Wed Mar 10 21:48:46 1999
@@ -20,13 +20,19 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/ide.h>
 #include <asm/mediabay.h>
 #include <asm/feature.h>
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/adb.h>
+#include <asm/pmu.h>
+#endif
 #include "ide.h"
+#include "ide_modes.h"
 
 ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
 int pmac_ide_irq[MAX_HWIFS];
@@ -41,6 +47,13 @@
 static int pmac_ide_build_dmatable(ide_drive_t *drive, int wr);
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 
+#ifdef CONFIG_PMAC_PBOOK
+static int idepmac_notify(struct notifier_block *, unsigned long, void *);
+struct notifier_block idepmac_sleep_notifier = {
+	idepmac_notify
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
 /*
  * N.B. this can't be an initfunc, because the media-bay task can
  * call ide_[un]register at any time.
@@ -74,6 +87,23 @@
 	}
 }
 
+void pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
+{
+	ide_pio_data_t d;
+
+	if (_machine != _MACH_Pmac)
+		return;
+	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
+	switch (pio) {
+	case 4:
+		out_le32((unsigned *)(IDE_DATA_REG + 0x200), 0x211025);
+		break;
+	default:
+		out_le32((unsigned *)(IDE_DATA_REG + 0x200), 0x2f8526);
+		break;
+	}
+}
+
 __initfunc(void
 pmac_ide_probe(void))
 {
@@ -145,9 +175,10 @@
 		pmac_ide_init_hwif_ports(hwif->io_ports, base, &hwif->irq);
 		hwif->chipset = ide_generic;
 		hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+		hwif->tuneproc = pmac_ide_tuneproc;
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-		if (np->n_addrs >= 2 && np->n_intrs >= 2) {
+		if (np->n_addrs >= 2) {
 			/* has a DBDMA controller channel */
 			pmac_ide_setup_dma(np, hwif);
 		}
@@ -156,6 +187,10 @@
 		++i;
 	}
 	pmac_ide_count = i;
+
+#ifdef CONFIG_PMAC_PBOOK
+	notifier_chain_register(&sleep_notifier_list, &idepmac_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
@@ -311,5 +346,32 @@
 	}
 	return 0;
 }
-
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
+
+#ifdef CONFIG_PMAC_PBOOK
+static int idepmac_notify(struct notifier_block *this,
+			  unsigned long code, void *p)
+{
+	int i, timeout;
+
+	switch (code) {
+	case PBOOK_SLEEP:
+		/* do anything here?? */
+		break;
+	case PBOOK_WAKE:
+		/* wait for the controller(s) to become ready */
+		timeout = 5000;
+		for (i = 0; i < pmac_ide_count; ++i) {
+			unsigned long base = pmac_ide_regbase[i];
+			if (check_media_bay_by_base(base, MB_CD) == -EINVAL)
+				continue;
+			while ((inb(base + 0x70) & BUSY_STAT) && timeout) {
+				mdelay(1);
+				--timeout;
+			}
+		}
+		break;
+	}
+	return NOTIFY_DONE;
+}
+#endif /* CONFIG_PMAC_PBOOK */
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.2.3/linux/drivers/block/ide-probe.c	Tue Feb 23 15:21:33 1999
+++ linux/drivers/block/ide-probe.c	Wed Mar 10 17:49:43 1999
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-probe.c	Version 1.03  Dec  5, 1997
+ *  linux/drivers/block/ide-probe.c	Version 1.04  March 10, 1999
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -17,6 +17,7 @@
  * Version 1.02		increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot
  *			 by Andrea Arcangeli
  * Version 1.03		fix for (hwif->chipset == ide_4drives)
+ * Version 1.04		fixed buggy treatments of known flash memory cards
  */
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
@@ -42,6 +43,33 @@
 
 #include "ide.h"
 
+/*
+ * CompactFlash cards and their brethern pretend to be removable hard disks, except:
+ *	(1) they never have a slave unit, and
+ *	(2) they don't have doorlock mechanisms.
+ * This test catches them, and is invoked elsewhere when setting appropriate config bits.
+ *
+ * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices,
+ * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way,
+ * and get rid of the model-name tests below (too big of an interface change for 2.2.x).
+ * At that time, we might also consider parameterizing the timeouts and retries,
+ * since these are MUCH faster than mechanical drives.	-M.Lord
+ */
+int drive_is_flashcard (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
+
+	if (drive->removable && id != NULL) {
+		if (!strncmp(id->model, "KODAK ATA_FLASH", 15)	/* Kodak */
+		 || !strncmp(id->model, "Hitachi CV", 10)		/* Hitachi */
+		 || !strncmp(id->model, "SunDisk SDCFB", 13))	/* SunDisk */
+		{
+			return 1;	/* yes, it is a flash memory card */
+		}
+	}
+	return 0;	/* no, it is not a flash memory card */
+}
+
 static inline void do_identify (ide_drive_t *drive, byte cmd)
 {
 	int bswap = 1;
@@ -84,16 +112,6 @@
 	drive->present = 1;
 
 	/*
-	 * Prevent long system lockup probing later for non-existant
-	 * slave drive if the hwif is actually a Kodak CompactFlash card.
-	 */
-	if (!strcmp(id->model, "KODAK ATA_FLASH")) {
-		ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit];
-		mate->present = 0;
-		mate->noprobe = 1;
-	}
-
-	/*
 	 * Check for an ATAPI device
 	 */
 	if (cmd == WIN_PIDENTIFY) {
@@ -137,6 +155,20 @@
 		return;
 	}
 
+	/*
+	 * Not an ATAPI device: looks like a "regular" hard disk
+	 */
+	if (id->config & (1<<7))
+		drive->removable = 1;
+	/*
+	 * Prevent long system lockup probing later for non-existant
+	 * slave drive if the hwif is actually a flash memory card of some variety:
+	 */
+	if (drive_is_flashcard(drive)) {
+		ide_drive_t *mate = &HWIF(drive)->drives[1^drive->select.b.unit];
+		mate->present = 0;
+		mate->noprobe = 1;
+	}
 	drive->media = ide_disk;
 	printk("ATA DISK drive\n");
 	return;
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.2.3/linux/drivers/block/ide.c	Wed Mar 10 15:29:45 1999
+++ linux/drivers/block/ide.c	Wed Mar 10 17:49:43 1999
@@ -2963,6 +2963,7 @@
 /*
  * Probe module
  */
+EXPORT_SYMBOL(drive_is_flashcard);
 EXPORT_SYMBOL(ide_timer_expiry);
 EXPORT_SYMBOL(ide_intr);
 EXPORT_SYMBOL(ide_geninit);
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.2.3/linux/drivers/block/ide.h	Tue Feb 23 15:21:33 1999
+++ linux/drivers/block/ide.h	Sun Mar 21 14:18:31 1999
@@ -674,6 +674,13 @@
  */
 struct request **ide_get_queue (kdev_t dev);
 
+/*
+ * CompactFlash cards and their brethern pretend to be removable hard disks,
+ * but they never have a slave unit, and they don't have doorlock mechanisms.
+ * This test catches them, and is invoked elsewhere when setting appropriate config bits.
+ */
+int drive_is_flashcard (ide_drive_t *drive);
+
 int  ide_spin_wait_hwgroup(ide_drive_t *drive, unsigned long *flags);
 void ide_timer_expiry (unsigned long data);
 void ide_intr (int irq, void *dev_id, struct pt_regs *regs);
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ide_modes.h linux/drivers/block/ide_modes.h
--- v2.2.3/linux/drivers/block/ide_modes.h	Fri Jul 31 17:06:37 1998
+++ linux/drivers/block/ide_modes.h	Sun Mar 21 14:18:21 1999
@@ -15,7 +15,7 @@
  * breaking the fragile cmd640.c support.
  */
 
-#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621)
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) || defined(CONFIG_BLK_DEV_IDE_PMAC)
 
 /*
  * Standard (generic) timings for PIO modes, from ATA2 specification.
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.2.3/linux/drivers/block/ll_rw_blk.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/block/ll_rw_blk.c	Thu Mar 11 23:20:14 1999
@@ -392,8 +392,10 @@
 
 	lock_buffer(bh);
 
-	if (blk_size[major])
-		if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
+	if (blk_size[major]) {
+		unsigned long maxsector = (blk_size[major][MINOR(bh->b_rdev)] << 1) + 1;
+
+		if (maxsector < count || maxsector - count < sector) {
 			bh->b_state &= (1 << BH_Lock);
                         /* This may well happen - the kernel calls bread()
                            without checking the size of the device, e.g.,
@@ -406,6 +408,7 @@
                                blk_size[major][MINOR(bh->b_rdev)]);
 			goto end_io;
 		}
+	}
 
 	rw_ahead = 0;	/* normal case; gets changed below for READA/WRITEA */
 	switch (rw) {
diff -u --recursive --new-file v2.2.3/linux/drivers/block/nbd.c linux/drivers/block/nbd.c
--- v2.2.3/linux/drivers/block/nbd.c	Wed Mar 10 15:29:45 1999
+++ linux/drivers/block/nbd.c	Tue Mar  9 16:58:05 1999
@@ -418,7 +418,7 @@
 		return 0;
 #endif
 	case BLKGETSIZE:
-		return put_user(nbd_bytesizes[dev] << 9, (long *) arg);
+		return put_user(nbd_bytesizes[dev] >> 9, (long *) arg);
 	}
 	return -EINVAL;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c
--- v2.2.3/linux/drivers/block/ns87415.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/block/ns87415.c	Mon Mar 15 16:11:29 1999
@@ -95,6 +95,12 @@
 	byte stat;
 #endif
 
+	/* Set a good latency timer and cache line size value. */
+	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+#ifdef __sparc_v9__
+	(void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
+
 	/*
 	 * We cannot probe for IRQ: both ports share common IRQ on INTA.
 	 * Also, leave IRQ masked during drive probing, to prevent infinite
diff -u --recursive --new-file v2.2.3/linux/drivers/block/sl82c105.c linux/drivers/block/sl82c105.c
--- v2.2.3/linux/drivers/block/sl82c105.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/block/sl82c105.c	Wed Mar 10 21:48:46 1999
@@ -1,3 +1,4 @@
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
@@ -19,7 +20,6 @@
 	struct pci_dev *dev = hwif->pci_dev;
 	unsigned short t16;
 	unsigned int t32;
-
 	pci_read_config_word(dev, PCI_COMMAND, &t16);
 	printk("SL82C105 command word: %x\n",t16);
         t16 |= PCI_COMMAND_IO;
@@ -28,7 +28,9 @@
 	pci_read_config_dword(dev, 0x44, &t32);
 	printk("IDE timing: %08x, resetting to PIO0 timing\n",t32);
 	pci_write_config_dword(dev, 0x44, 0x03e4);
+#ifndef CONFIG_MBX
 	pci_read_config_dword(dev, 0x40, &t32);
 	printk("IDE control/status register: %08x\n",t32);
 	pci_write_config_dword(dev, 0x40, 0x10ff08a1);
+#endif /* CONFIG_MBX */
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/block/swim3.c linux/drivers/block/swim3.c
--- v2.2.3/linux/drivers/block/swim3.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/block/swim3.c	Wed Mar 10 21:48:46 1999
@@ -10,6 +10,12 @@
  * 2 of the License, or (at your option) any later version.
  */
 
+/*
+ * TODO:
+ * handle 2 drives
+ * handle GCR disks
+ */
+
 #include <linux/stddef.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -52,18 +58,18 @@
  */
 struct swim3 {
 	REG(data);
-	REG(usecs);		/* counts down at 1MHz */
+	REG(timer);		/* counts down at 1MHz */
 	REG(error);
 	REG(mode);
 	REG(select);		/* controls CA0, CA1, CA2 and LSTRB signals */
-	REG(reg5);
+	REG(setup);
 	REG(control);		/* writing bits clears them */
 	REG(status);		/* writing bits sets them in control */
 	REG(intr);
 	REG(nseek);		/* # tracks to seek */
 	REG(ctrack);		/* current track number */
 	REG(csect);		/* current sector number */
-	REG(ssize);		/* sector size code?? */
+	REG(gap3);		/* size of gap 3 in track format */
 	REG(sector);		/* sector # to read or write */
 	REG(nsect);		/* # sectors to read or write */
 	REG(intr_enable);
@@ -78,21 +84,46 @@
 
 /* Bits in control register */
 #define DO_SEEK		0x80
+#define FORMAT		0x40
 #define SELECT		0x20
 #define WRITE_SECTORS	0x10
-#define SCAN_TRACK	0x08
+#define DO_ACTION	0x08
+#define DRIVE2_ENABLE	0x04
 #define DRIVE_ENABLE	0x02
 #define INTR_ENABLE	0x01
 
 /* Bits in status register */
+#define FIFO_1BYTE	0x80
+#define FIFO_2BYTE	0x40
+#define ERROR		0x20
 #define DATA		0x08
+#define RDDATA		0x04
+#define INTR_PENDING	0x02
+#define MARK_BYTE	0x01
 
 /* Bits in intr and intr_enable registers */
-#define ERROR		0x20
+#define ERROR_INTR	0x20
 #define DATA_CHANGED	0x10
 #define TRANSFER_DONE	0x08
 #define SEEN_SECTOR	0x04
 #define SEEK_DONE	0x02
+#define TIMER_DONE	0x01
+
+/* Bits in error register */
+#define ERR_DATA_CRC	0x80
+#define ERR_ADDR_CRC	0x40
+#define ERR_OVERRUN	0x04
+#define ERR_UNDERRUN	0x01
+
+/* Bits in setup register */
+#define S_SW_RESET	0x80
+#define S_GCR_WRITE	0x40
+#define S_IBM_DRIVE	0x20
+#define S_TEST_MODE	0x10
+#define S_FCLK_DIV2	0x08
+#define S_GCR		0x04
+#define S_COPY_PROT	0x02
+#define S_INV_WDATA	0x01
 
 /* Select values for swim3_action */
 #define SEEK_POSITIVE	0
@@ -100,14 +131,18 @@
 #define STEP		1
 #define MOTOR_ON	2
 #define MOTOR_OFF	6
+#define INDEX		3
 #define EJECT		7
+#define SETMFM		9
+#define SETGCR		13
 
 /* Select values for swim3_select and swim3_readbit */
 #define STEP_DIR	0
 #define STEPPING	1
 #define MOTOR_ON	2
-#define RELAX		3
+#define RELAX		3	/* also eject in progress */
 #define READ_DATA_0	4
+#define TWOMEG_DRIVE	5
 #define SINGLE_SIDED	6
 #define DRIVE_PRESENT	7
 #define DISK_IN		8
@@ -115,7 +150,25 @@
 #define TRACK_ZERO	10
 #define TACHO		11
 #define READ_DATA_1	12
+#define MFM_MODE	13
 #define SEEK_COMPLETE	14
+#define ONEMEG_MEDIA	15
+
+/* Definitions of values used in writing and formatting */
+#define DATA_ESCAPE	0x99
+#define GCR_SYNC_EXC	0x3f
+#define GCR_SYNC_CONV	0x80
+#define GCR_FIRST_MARK	0xd5
+#define GCR_SECOND_MARK	0xaa
+#define GCR_ADDR_MARK	"\xd5\xaa\x00"
+#define GCR_DATA_MARK	"\xd5\xaa\x0b"
+#define GCR_SLIP_BYTE	"\x27\xaa"
+#define GCR_SELF_SYNC	"\x3f\xbf\x1e\x34\x3c\x3f"
+
+#define DATA_99		"\x99\x99"
+#define MFM_ADDR_MARK	"\x99\xa1\x99\xa1\x99\xa1\x99\xfe"
+#define MFM_INDEX_MARK	"\x99\xc2\x99\xc2\x99\xc2\x99\xfc"
+#define MFM_GAP_LEN	12
 
 struct floppy_state {
 	enum swim_state	state;
@@ -153,7 +206,7 @@
 	0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e,	/* gap field */
 	0, 0, 0, 0, 0, 0,			/* sync field */
 	0x99a1, 0x99a1, 0x99a1, 0x99fb,		/* data address mark */
-	0x990f					/* init CRC generator */
+	0x990f					/* no escape for 512 bytes */
 };
 
 static unsigned short write_postamble[] = {
@@ -217,11 +270,11 @@
 	volatile struct swim3 *sw = fs->swim3;
 
 	swim3_select(fs, action);
-	udelay(10);
-	sw->select |= LSTRB; eieio();
-	udelay(20);
-	sw->select &= ~LSTRB; eieio();
-	udelay(10);
+	udelay(1);
+	out_8(&sw->select, sw->select | LSTRB);
+	udelay(2);
+	out_8(&sw->select, sw->select & ~LSTRB);
+	udelay(1);
 	out_8(&sw->select, RELAX);
 }
 
@@ -328,9 +381,9 @@
 
 	swim3_select(fs, READ_DATA_0);
 	xx = sw->intr;		/* clear SEEN_SECTOR bit */
-	out_8(&sw->control_bis, SCAN_TRACK);
+	out_8(&sw->control_bis, DO_ACTION);
 	/* enable intr when track found */
-	out_8(&sw->intr_enable, ERROR | SEEN_SECTOR);
+	out_8(&sw->intr_enable, ERROR_INTR | SEEN_SECTOR);
 	set_timeout(fs, HZ, scan_timeout);	/* enable timeout */
 }
 
@@ -349,7 +402,7 @@
 	swim3_select(fs, STEP);
 	out_8(&sw->control_bis, DO_SEEK);
 	/* enable intr when seek finished */
-	out_8(&sw->intr_enable, ERROR | SEEK_DONE);
+	out_8(&sw->intr_enable, ERROR_INTR | SEEK_DONE);
 	set_timeout(fs, HZ/2, seek_timeout);	/* enable timeout */
 }
 
@@ -384,7 +437,7 @@
 	swim3_select(fs, fs->head? READ_DATA_1: READ_DATA_0);
 	out_8(&sw->sector, fs->req_sector);
 	out_8(&sw->nsect, n);
-	out_8(&sw->ssize, 0);
+	out_8(&sw->gap3, 0);
 	st_le32(&dr->cmdptr, virt_to_bus(cp));
 	if (CURRENT->cmd == WRITE) {
 		/* Set up 3 dma commands: write preamble, data, postamble */
@@ -400,9 +453,9 @@
 	out_le16(&cp->command, DBDMA_STOP);
 	out_le32(&dr->control, (RUN << 16) | RUN);
 	out_8(&sw->control_bis,
-	      (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | SCAN_TRACK);
+	      (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | DO_ACTION);
 	/* enable intr when transfer complete */
-	out_8(&sw->intr_enable, ERROR | TRANSFER_DONE);
+	out_8(&sw->intr_enable, ERROR_INTR | TRANSFER_DONE);
 	set_timeout(fs, 2*HZ, xfer_timeout);	/* enable timeout */
 }
 
@@ -445,7 +498,7 @@
 			/* wait for SEEK_COMPLETE to become true */
 			swim3_select(fs, SEEK_COMPLETE);
 			udelay(10);
-			out_8(&sw->intr_enable, ERROR | DATA_CHANGED);
+			out_8(&sw->intr_enable, ERROR_INTR | DATA_CHANGED);
 			in_8(&sw->intr);	/* clear DATA_CHANGED */
 			if (in_8(&sw->status) & DATA) {
 				/* seek_complete is not yet true */
@@ -487,7 +540,7 @@
 	volatile struct swim3 *sw = fs->swim3;
 
 	fs->timeout_pending = 0;
-	out_8(&sw->control_bic, SCAN_TRACK);
+	out_8(&sw->control_bic, DO_ACTION);
 	out_8(&sw->select, RELAX);
 	out_8(&sw->intr_enable, 0);
 	fs->cur_cyl = -1;
@@ -537,7 +590,7 @@
 	fs->timeout_pending = 0;
 	st_le32(&dr->control, RUN << 16);
 	out_8(&sw->intr_enable, 0);
-	out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK);
+	out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
 	out_8(&sw->select, RELAX);
 	if (CURRENT->cmd == WRITE)
 		++cp;
@@ -568,13 +621,13 @@
 #if 0
 	printk("swim3 intr state=%d intr=%x err=%x\n", fs->state, intr, err);
 #endif
-	if ((intr & ERROR) && fs->state != do_transfer)
+	if ((intr & ERROR_INTR) && fs->state != do_transfer)
 		printk(KERN_ERR "swim3_interrupt, state=%d, cmd=%x, intr=%x, err=%x\n",
 		       fs->state, CURRENT->cmd, intr, err);
 	switch (fs->state) {
 	case locating:
 		if (intr & SEEN_SECTOR) {
-			out_8(&sw->control_bic, SCAN_TRACK);
+			out_8(&sw->control_bic, DO_ACTION);
 			out_8(&sw->select, RELAX);
 			out_8(&sw->intr_enable, 0);
 			del_timer(&fs->timeout);
@@ -622,13 +675,13 @@
 		act(fs);
 		break;
 	case do_transfer:
-		if ((intr & (ERROR | TRANSFER_DONE)) == 0)
+		if ((intr & (ERROR_INTR | TRANSFER_DONE)) == 0)
 			break;
 		dr = fs->dma;
 		cp = fs->dma_cmd;
 		st_le32(&dr->control, RUN << 16);
 		out_8(&sw->intr_enable, 0);
-		out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK);
+		out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
 		out_8(&sw->select, RELAX);
 		del_timer(&fs->timeout);
 		fs->timeout_pending = 0;
@@ -636,7 +689,7 @@
 			++cp;
 		stat = ld_le16(&cp->xfer_status);
 		resid = ld_le16(&cp->res_count);
-		if (intr & ERROR) {
+		if (intr & ERROR_INTR) {
 			n = fs->scount - 1 - resid / 512;
 			if (n > 0) {
 				CURRENT->sector += n;
@@ -800,6 +853,8 @@
 
 	if (devnum >= floppy_count)
 		return -ENODEV;
+	if (filp == 0)
+		return -EIO;
 		
 	fs = &floppy_states[devnum];
 	sw = fs->swim3;
@@ -809,7 +864,7 @@
 			return -ENXIO;
 		out_8(&sw->mode, 0x95);
 		out_8(&sw->control_bic, 0xff);
-		out_8(&sw->reg5, 0x28);
+		out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2);
 		udelay(10);
 		out_8(&sw->intr_enable, 0);
 		out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
@@ -834,14 +889,14 @@
 	} else if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
 		return -EBUSY;
 
-	if (err == 0 && filp && (filp->f_flags & O_NDELAY) == 0
+	if (err == 0 && (filp->f_flags & O_NDELAY) == 0
 	    && (filp->f_mode & 3)) {
 		check_disk_change(inode->i_rdev);
 		if (fs->ejected)
 			err = -ENXIO;
 	}
 
-	if (err == 0 && filp && (filp->f_flags & (O_WRONLY | O_RDWR))) {
+	if (err == 0 && (filp->f_mode & 2)) {
 		if (fs->write_prot < 0)
 			fs->write_prot = swim3_readbit(fs, WRITE_PROT);
 		if (fs->write_prot)
@@ -977,10 +1032,14 @@
 
 	if (devnum >= floppy_count)
 		return -ENODEV;
-
+	check_disk_change(inode->i_rdev);
 	fs = &floppy_states[devnum];
 	if (fs->ejected)
 		return -ENXIO;
+	if (fs->write_prot < 0)
+		fs->write_prot = swim3_readbit(fs, WRITE_PROT);
+	if (fs->write_prot)
+		return -EROFS;
 	return block_write(filp, buf, count, ppos);
 }
 
diff -u --recursive --new-file v2.2.3/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.2.3/linux/drivers/cdrom/cdrom.c	Tue Jan 19 11:32:51 1999
+++ linux/drivers/cdrom/cdrom.c	Sun Mar 21 18:37:56 1999
@@ -24,8 +24,9 @@
 
  -- Change the CDROMREADMODE1, CDROMREADMODE2, CDROMREADAUDIO, and 
  CDROMREADRAW ioctls so they go through the Uniform CD-ROM driver.
-
-
+ 
+ -- Sync options and capability flags.
+ 
 
 
  Revision History
@@ -97,17 +98,33 @@
   cdi->options in various ioctl.
   -- Added version to proc entry.
   
-  2.52 Jan 16, 1998 - Jens Axboe <axboe@image.dk>
+  2.52 Jan 16, 1999 - Jens Axboe <axboe@image.dk>
   -- Fixed an error in open_for_data where we would sometimes not return
   the correct error value. Thanks Huba Gaspar <huba@softcell.hu>.
   -- Fixed module usage count - usage was based on /proc/sys/dev
   instead of /proc/sys/dev/cdrom. This could lead to an oops when other
-  modules had entries in dev.
+  modules had entries in dev. Feb 02 - real bug was in sysctl.c where
+  dev would be removed even though it was used. cdrom.c just illuminated
+  that bug.
+  
+  2.53 Feb 22, 1999 - Jens Axboe <axboe@image.dk>
+  -- Fixup of several ioctl calls, in particular CDROM_SET_OPTIONS has
+  been "rewritten" because capabilities and options aren't in sync. They
+  should be...
+  -- Added CDROM_LOCKDOOR ioctl. Locks the door and keeps it that way.
+  -- Added CDROM_RESET ioctl.
+  -- Added CDROM_DEBUG ioctl. Enable debug messages on-the-fly.
+  -- Added CDROM_GET_CAPABILITY ioctl. This relieves userspace programs
+  from parsing /proc/sys/dev/cdrom/info.
+  
+  2.54 Mar 15, 1999 - Jens Axboe <axboe@image.dk>
+  -- Check capability mask from low level driver when counting tracks as
+  per suggestion from Corey J. Scotts <cstotts@blue.weeg.uiowa.edu>.
 
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 2.52"
-#define VERSION "Id: cdrom.c 2.52 1999/01/16"
+#define REVISION "Revision: 2.54"
+#define VERSION "Id: cdrom.c 2.54 1999/03/15"
 
 /* I use an error-log mask to give fine grain control over the type of
    messages dumped to the system logs.  The available masks include: */
@@ -144,6 +161,8 @@
 
 /* used to tell the module to turn on full debugging messages */
 static int debug = 0;
+/* used to keep tray locked at all times */
+static int keeplocked = 0;
 /* default compatibility mode */
 static int autoclose=1;
 static int autoeject=0;
@@ -164,13 +183,11 @@
 #endif
 
 /* These are used to simplify getting data in from and back to user land */
-#define IOCTL_IN(arg, type, in) { \
-            if ( copy_from_user(&in, (type *) arg, sizeof in) ) \
-            	return -EFAULT; }
-
-#define IOCTL_OUT(arg, type, out) { \
-            if ( copy_to_user((type *) arg, &out, sizeof out) ) \
-            	return -EFAULT; }
+#define IOCTL_IN(arg, type, in)	\
+	copy_from_user_ret(&in, (type *) arg, sizeof in, -EFAULT)
+
+#define IOCTL_OUT(arg, type, out) \
+	copy_to_user_ret((type *) arg, &out, sizeof out, -EFAULT)
 
 
 #define FM_WRITE	0x2                 /* file mode write bit */
@@ -328,7 +345,7 @@
 	if (fp->f_mode & FM_WRITE)
 		return -EROFS;
 	purpose = purpose || !(cdi->options & CDO_USE_FFLAGS);
-	if (cdi->use_count || purpose)
+	if (purpose)
 		ret = cdi->ops->open(cdi, purpose);
 	else
 		ret = open_for_data(cdi);
@@ -517,10 +534,10 @@
 	if (cdi->use_count == 0)
 		cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
 	if (cdi->use_count == 0 &&      /* last process that closes dev*/
-	    cdo->capability & CDC_LOCK) {
+	    cdo->capability & CDC_LOCK && !keeplocked) {
 		cdinfo(CD_CLOSE, "Unlocking door!\n");
 		cdo->lock_door(cdi, 0);
-		}
+	}
 	opened_for_data = !(cdi->options & CDO_USE_FFLAGS) ||
 		!(fp && fp->f_flags & O_NONBLOCK);
 	cdo->release(cdi);
@@ -588,14 +605,17 @@
 	tracks->xa=0;
 	tracks->error=0;
 	cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); 
-        if (!(cdi->ops->capability & CDC_PLAY_AUDIO)) { 
+        if (!(cdi->ops->capability & ~cdi->mask & CDC_PLAY_AUDIO)) { 
                 tracks->error=CDS_NO_INFO;
                 return;
         }        
 	/* Grab the TOC header so we can see how many tracks there are */
-	ret=cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
+	ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
 	if (ret) {
-		tracks->error=(ret == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO;
+		if (ret == -ENOMEDIUM)
+			tracks->error = CDS_NO_DISC;
+		else
+			tracks->error = CDS_NO_INFO;
 		return;
 	}	
 	/* check what type of tracks are on this disc */
@@ -716,18 +736,18 @@
 		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); 
 		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
 			return -ENOSYS;
-		if (cdi->use_count != 1) 
+		if (cdi->use_count != 1 || keeplocked)
 			return -EBUSY;
-		if (cdo->capability & ~cdi->mask & CDC_LOCK) {
+		if (cdo->capability & ~cdi->mask & CDC_LOCK)
 			if ((ret=cdo->lock_door(cdi, 0)))
 				return ret;
-			}
+
 		return cdo->tray_move(cdi, 1);
 		}
 
 	case CDROMCLOSETRAY:
 		cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); 
-		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
+		if (!(cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY))
 			return -ENOSYS;
 		return cdo->tray_move(cdi, 0);
 
@@ -735,6 +755,8 @@
 		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); 
 		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
 			return -ENOSYS;
+		if (keeplocked)
+			return -EBUSY;
 		cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
 		if (arg)
 			cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
@@ -755,8 +777,23 @@
 
 	case CDROM_SET_OPTIONS:
 		cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); 
-		if (cdo->capability & arg & ~cdi->mask)
-			return -ENOSYS;
+		/* options need to be in sync with capability. too late for
+		   that, so we have to check each one separately... */
+		switch (arg) {
+		case CDO_USE_FFLAGS:
+		case CDO_CHECK_TYPE:
+			break;
+		case CDO_LOCK:
+			if (!(cdo->capability & ~cdi->mask & CDC_LOCK))
+				return -ENOSYS;
+			break;
+		case 0:
+			return cdi->options;
+		/* default is basically CDO_[AUTO_CLOSE|AUTO_EJECT] */
+		default:
+			if (!(cdo->capability & ~cdi->mask & arg))
+				return -ENOSYS;
+		}
 		cdi->options |= (int) arg;
 		return cdi->options;
 
@@ -783,6 +820,36 @@
 		return cdo->select_disc(cdi, arg);
 		}
 
+	case CDROMRESET: {
+		cdinfo(CD_DO_IOCTL, "entering CDROM_RESET\n");
+		if (!(cdo->capability & ~cdi->mask & CDC_RESET))
+			return -ENOSYS;
+		return cdo->reset(cdi);
+		}
+
+	case CDROM_LOCKDOOR: {
+		cdinfo(CD_DO_IOCTL, "%socking door.\n",arg?"L":"Unl");
+		if (!(cdo->capability & ~cdi->mask & CDC_LOCK)) {
+			return -EDRIVE_CANT_DO_THIS;
+		} else {
+			keeplocked = arg ? 1 : 0;
+			return cdo->lock_door(cdi, arg);
+		}
+		}
+
+	case CDROM_DEBUG: {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		cdinfo(CD_DO_IOCTL, "%sabling debug.\n",arg?"En":"Dis");
+		debug = arg ? 1 : 0;
+		return debug;
+		}
+
+	case CDROM_GET_CAPABILITY: {
+		cdinfo(CD_DO_IOCTL, "entering CDROM_GET_CAPABILITY\n");
+		return cdo->capability;
+		}
+
 /* The following function is implemented, although very few audio
  * discs give Universal Product Code information, which should just be
  * the Medium Catalog Number on the box.  Note, that the way the code
@@ -1123,7 +1190,7 @@
 		return;
 
 	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 1);
-	cdrom_root_table->de->fill_inode = &cdrom_procfs_modcount;
+	cdrom_root_table->child->de->fill_inode = &cdrom_procfs_modcount;
 
 	initialized = 1;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.2.3/linux/drivers/cdrom/cm206.c	Thu Sep 17 17:53:35 1998
+++ linux/drivers/cdrom/cm206.c	Wed Mar 10 17:01:57 1999
@@ -186,7 +186,6 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
-#include <linux/interrupt.h>
 
 /* #include <linux/ucdrom.h> */
 
diff -u --recursive --new-file v2.2.3/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.2.3/linux/drivers/char/Makefile	Wed Mar 10 15:29:45 1999
+++ linux/drivers/char/Makefile	Mon Mar 15 16:11:29 1999
@@ -44,12 +44,17 @@
 ifdef CONFIG_VT
 L_OBJS += keyboard.o
 endif
-ifneq ($(ARCH),m68k)
-L_OBJS += pc_keyb.o defkeymap.o
+ ifneq ($(ARCH),m68k)
+   L_OBJS += pc_keyb.o defkeymap.o
+ endif
+else
+ifdef CONFIG_PCI
+L_OBJS += defkeymap.o keyboard.o
 endif
-ifdef CONFIG_MAGIC_SYSRQ
-L_OBJS += sysrq.o
 endif
+
+ifdef CONFIG_MAGIC_SYSRQ
+LX_OBJS += sysrq.o
 endif
 
 ifeq ($(CONFIG_ATARI_DSP56K),y)
diff -u --recursive --new-file v2.2.3/linux/drivers/char/README.epca linux/drivers/char/README.epca
--- v2.2.3/linux/drivers/char/README.epca	Fri Jan  8 22:36:05 1999
+++ linux/drivers/char/README.epca	Tue Mar 16 14:21:35 1999
@@ -504,3 +504,14 @@
 Files affected        : epca.c
 Release version       : 1.1.1  (BETA)
 -----------------------------------------------------------------------
+Programmer            : Daniel Taylor
+Date                  : March 11, 1999
+Description (Verbose) : Updated driver:
+                        1.  Simultaneous data and modem change events were
+			    resulting in the modem change events not being
+			    recognized. Fixed.
+                        2.  Modified pc_info device name to work better
+			    with devfs.
+Files affected        : epca.c
+Release version       : 1.3.0-K 
+-----------------------------------------------------------------------
diff -u --recursive --new-file v2.2.3/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.2.3/linux/drivers/char/bw-qcam.c	Sun Nov  8 14:02:53 1998
+++ linux/drivers/char/bw-qcam.c	Wed Mar 10 17:03:52 1999
@@ -5,6 +5,28 @@
  *
  *	Video4Linux conversion work by Alan Cox.
  *	Parport compatibility by Phil Blundell.
+ *	Busy loop avoidance by Mark Cooke.
+ *
+ *    Module parameters:
+ *
+ *	maxpoll=<1 - 5000>
+ *
+ *	  When polling the QuickCam for a response, busy-wait for a
+ *	  maximum of this many loops. The default of 250 gives little
+ *	  impact on interactive response.
+ *
+ *	  NOTE: If this parameter is set too high, the processor
+ *		will busy wait until this loop times out, and then
+ *		slowly poll for a further 5 seconds before failing
+ *		the transaction. You have been warned.
+ *
+ *	yieldlines=<1 - 250>
+ *
+ *	  When acquiring a frame from the camera, the data gathering
+ *	  loop will yield back to the scheduler after completing
+ *	  this many lines. The default of 4 provides a trade-off
+ *	  between increased frame acquisition time and impact on
+ *	  interactive response.
  */
 
 /* qcam-lib.c -- Library for programming with the Connectix QuickCam.
@@ -58,6 +80,14 @@
 
 #include "bw-qcam.h"
 
+#if LINUX_VERSION_CODE >= 0x020117
+MODULE_PARM(maxpoll,"i");
+MODULE_PARM(yieldlines,"i");   
+#endif
+
+static unsigned int maxpoll=250;   /* Maximum busy-loop count for qcam I/O */
+static unsigned int yieldlines=4;  /* Yield after this many during capture */
+
 extern __inline__ int read_lpstatus(struct qcam_device *q)
 {
 	return parport_read_status(q->pport);
@@ -154,6 +184,7 @@
 	q->top = 1;
 	q->left = 14;
 	q->mode = -1;
+	q->status = QC_PARAM_CHANGE;
 	return q;
 }
 
@@ -209,14 +240,17 @@
 		{
 			/* 1000 is enough spins on the I/O for all normal
 			   cases, at that point we start to poll slowly 
-			   until the camera wakes up */
+			   until the camera wakes up. However, we are
+			   busy blocked until the camera responds, so
+			   setting it lower is much better for interactive
+			   response. */
 			   
-			if(runs++>1000)
+			if(runs++>maxpoll)
 			{
 				current->state=TASK_INTERRUPTIBLE;
-				schedule_timeout(HZ/10);
+				schedule_timeout(HZ/200);
 			}
-			if(runs>1050)
+			if(runs>(maxpoll+1000)) /* 5 seconds */
 				return -1;
 		}
 	}
@@ -226,14 +260,17 @@
 		{
 			/* 1000 is enough spins on the I/O for all normal
 			   cases, at that point we start to poll slowly 
-			   until the camera wakes up */
+			   until the camera wakes up. However, we are
+			   busy blocked until the camera responds, so
+			   setting it lower is much better for interactive
+			   response. */
 			   
-			if(runs++>1000)
+			if(runs++>maxpoll)
 			{
 				current->state=TASK_INTERRUPTIBLE;
-				schedule_timeout(HZ/10);
+				schedule_timeout(HZ/200);
 			}
-			if(runs++>1050)	/* 5 seconds */
+			if(runs++>(maxpoll+1000)) /* 5 seconds */
 				return -1;
 		}
 	}
@@ -256,14 +293,17 @@
 		status = read_lpdata(q);
 		/* 1000 is enough spins on the I/O for all normal
 		   cases, at that point we start to poll slowly 
-		   until the camera wakes up */
+		   until the camera wakes up. However, we are
+		   busy blocked until the camera responds, so
+		   setting it lower is much better for interactive
+		   response. */
 		   
-		if(runs++>1000)
+		if(runs++>maxpoll)
 		{
 			current->state=TASK_INTERRUPTIBLE;
-			schedule_timeout(HZ/10);
+			schedule_timeout(HZ/200);
 		}
-		if(runs++>1050)	/* 5 seconds */
+		if(runs++>(maxpoll+1000)) /* 5 seconds */
 			return 0;
 	}
 	while ((status & 1) != val);
@@ -287,7 +327,7 @@
 
 	lastreg = reg = read_lpstatus(q) & 0xf0;
 
-	for (i = 0; i < 300; i++) 
+	for (i = 0; i < 500; i++) 
 	{
 		reg = read_lpstatus(q) & 0xf0;
 		if (reg != lastreg)
@@ -296,9 +336,20 @@
 		mdelay(2);
 	}
 
-	/* Be liberal in what you accept...  */
 
-	if (count > 30 && count < 200)
+#if 0
+	/* Force camera detection during testing. Sometimes the camera
+	   won't be flashing these bits. Possibly unloading the module
+	   in the middle of a grab? Or some timeout condition?
+	   I've seen this parameter as low as 19 on my 450Mhz box - mpc */
+	printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count);
+	return 1;
+#endif
+
+	/* Be (even more) liberal in what you accept...  */
+
+/*	if (count > 30 && count < 200) */
+	if (count > 20 && count < 300)
 		return 1;	/* found */
 	else
 		return 0;	/* not found */
@@ -352,6 +403,8 @@
 
 static int qc_setscanmode(struct qcam_device *q)
 {
+	int old_mode = q->mode;
+	
 	switch (q->transfer_scale) 
 	{
 		case 1:
@@ -383,6 +436,10 @@
 		case QC_UNIDIR:
 			break;
 	}
+	
+	if (q->mode != old_mode)
+		q->status |= QC_PARAM_CHANGE;
+	
 	return 0;
 }
 
@@ -434,8 +491,11 @@
 	qc_command(q, q->contrast);
 	qc_command(q, 0x1f);
 	qc_command(q, q->whitebal);
-}
 
+	/* Clear flag that we must update the grabbing parameters on the camera
+	   before we grab the next frame */
+	q->status &= (~QC_PARAM_CHANGE);
+}
 
 /* Qc_readbytes reads some bytes from the QC and puts them in
    the supplied buffer.  It returns the number of bytes read,
@@ -539,7 +599,7 @@
 
 long qc_capture(struct qcam_device * q, char *buf, unsigned long len)
 {
-	int i, j, k;
+	int i, j, k, yield;
 	int bytes;
 	int linestotrans, transperline;
 	int divisor;
@@ -575,7 +635,7 @@
 	    q->transfer_scale;
 	transperline = (transperline + divisor - 1) / divisor;
 
-	for (i = 0; i < linestotrans; i++) 
+	for (i = 0, yield = yieldlines; i < linestotrans; i++) 
 	{
 		for (pixels_read = j = 0; j < transperline; j++) 
 		{
@@ -599,6 +659,18 @@
 			pixels_read += bytes;
 		}
 		(void) qc_readbytes(q, 0);	/* reset state machine */
+		
+		/* Grabbing an entire frame from the quickcam is a lengthy
+		   process. We don't (usually) want to busy-block the
+		   processor for the entire frame. yieldlines is a module
+		   parameter. If we yield every line, the minimum frame
+		   time will be 240 / 200 = 1.2 seconds. The compile-time
+		   default is to yield every 4 lines. */
+		if (i >= yield) {
+			current->state=TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ/200);
+			yield = i + yieldlines;
+		}
 	}
 
 	if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) 
@@ -745,9 +817,12 @@
 			qcam->bpp = p.depth;
 			
 			qc_setscanmode(qcam);
-			parport_claim_or_block(qcam->pdev);
+			qcam->status |= QC_PARAM_CHANGE;
+
+/*			parport_claim_or_block(qcam->pdev);
 			qc_set(qcam);
 			parport_release(qcam->pdev);
+*/
 			return 0;
 		}
 		case VIDIOCSWIN:
@@ -779,6 +854,11 @@
 				qcam->transfer_scale = 1;
 			}
 			qc_setscanmode(qcam);
+			
+			/* We must update the camera before we grab. We could
+			   just have changed the grab size */
+			qcam->status |= QC_PARAM_CHANGE;
+			
 			/* Ok we figured out what to use from our wide choice */
 			return 0;
 		}
@@ -824,11 +904,15 @@
 	parport_claim_or_block(qcam->pdev);
 	/* Probably should have a semaphore against multiple users */
 	qc_reset(qcam);
+
+	/* Update the camera parameters if we need to */
+	if (qcam->status & QC_PARAM_CHANGE)
+		qc_set(qcam);
+
 	len=qc_capture(qcam, buf,count);
 	parport_release(qcam->pdev);
 	return len;
 }
-
  
 static struct video_device qcam_template=
 {
@@ -909,6 +993,17 @@
 
 	for (port = parport_enumerate(); port; port=port->next)
 		init_bwqcam(port);
+
+	/* Do some sanity checks on the module parameters. */
+	if (maxpoll > 5000) {
+		printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n");
+		maxpoll = 5000;
+	}
+	
+	if (yieldlines < 1) {
+		printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n");
+		yieldlines = 1;
+	}
 
 	return (num_cams)?0:-ENODEV;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/char/bw-qcam.h linux/drivers/char/bw-qcam.h
--- v2.2.3/linux/drivers/char/bw-qcam.h	Mon Jan 12 14:46:16 1998
+++ linux/drivers/char/bw-qcam.h	Wed Mar 10 17:03:52 1999
@@ -48,6 +48,9 @@
 #define MAX_HEIGHT 243
 #define MAX_WIDTH 336
 
+/* Bit fields for status flags */
+#define QC_PARAM_CHANGE	0x01 /* Camera status change has occurred */
+
 struct qcam_device {
 	struct video_device vdev;
 	struct pardevice *pdev;
@@ -59,6 +62,6 @@
 	int port_mode;
 	int transfer_scale;
 	int top, left;
+	int status;
 	unsigned int saved_bits;
 };
-
diff -u --recursive --new-file v2.2.3/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.2.3/linux/drivers/char/console.c	Wed Mar 10 15:29:45 1999
+++ linux/drivers/char/console.c	Wed Mar 10 16:51:35 1999
@@ -85,7 +85,6 @@
 #include <linux/selection.h>
 #include <linux/console_struct.h>
 #include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
 #include <linux/consolemap.h>
 #include <linux/timer.h>
 #include <linux/interrupt.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/char/epca.c linux/drivers/char/epca.c
--- v2.2.3/linux/drivers/char/epca.c	Wed Jan 13 15:00:41 1999
+++ linux/drivers/char/epca.c	Tue Mar 16 14:21:51 1999
@@ -83,7 +83,6 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
-#include <linux/major.h>
 #include <linux/ioport.h>
 
 #ifdef MODULE
@@ -117,7 +116,7 @@
 
 /* ---------------------- Begin defines ------------------------ */
 
-#define VERSION            "1.1.0"     
+#define VERSION            "1.3.0-K"     
 
 /* This major needs to be submitted to Linux to join the majors list */
 
@@ -1814,9 +1813,10 @@
 	pc_callout.subtype = SERIAL_TYPE_CALLOUT;
 
 	pc_info = pc_driver;
-	pc_info.name = "digiCtl";
+	pc_info.name = "digi_ctl";
 	pc_info.major = DIGIINFOMAJOR;
 	pc_info.minor_start = 0;
+	pc_info.num = 1;
 	pc_info.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
 	pc_info.subtype = SERIAL_TYPE_INFO;
 
@@ -2398,7 +2398,7 @@
 			assertgwinon(ch);
 
 		} /* End DATA_IND */
-		else
+		/* else *//* Fix for DCD transition missed bug */
 		if (event & MODEMCHG_IND) 
 		{ /* Begin MODEMCHG_IND */
 
diff -u --recursive --new-file v2.2.3/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.2.3/linux/drivers/char/lp.c	Wed Dec 16 10:32:55 1998
+++ linux/drivers/char/lp.c	Thu Mar 11 09:27:06 1999
@@ -437,7 +437,7 @@
 	} else if (!(status & LP_PERRORP)) {
 		if (last != LP_PERRORP) {
 			last = LP_PERRORP;
-			printk(KERN_ERR "lp%d on fire!\n", minor);
+			printk(KERN_INFO "lp%d on fire!\n", minor);
 		}
 	}
 	else last = 0;
@@ -664,17 +664,15 @@
 		}
 		if ((i & 1) != 0) {
 			Byte |= (z<<4);
-			if (temp) {
-				if (__put_user (Byte, temp))
-				{
-					count = -EFAULT;
-					temp = NULL;
-				} else {
-					temp++;
+			if (__put_user (Byte, temp))
+			{
+				count = -EFAULT;
+				break;
+			} else {
+				temp++;
 
-					if (++count == length)
-						temp = NULL;
-				}
+				if (++count == length)
+					break;
 			}
 			/* Does the error line indicate end of data? */
 			if ((parport_read_status(port) & LP_PERRORP) == 
@@ -952,16 +950,12 @@
 
 	default:
 		for (i = 0; i < LP_NO; i++) {
-			if (parport_nr[i] >= 0) {
-				char buffer[16];
-				sprintf(buffer, "parport%d", parport_nr[i]);
-				for (port = parport_enumerate(); port; 
-				     port = port->next) {
-					if (!strcmp(port->name, buffer)) {
-						(void) lp_register(i, port);
+			for (port = parport_enumerate(); port; 
+			     port = port->next) {
+				if (port->number == parport_nr[i]) {
+					if (!lp_register(i, port))
 						count++;
-						break;
-					}
+					break;
 				}
 			}
 		}
diff -u --recursive --new-file v2.2.3/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v2.2.3/linux/drivers/char/pcxx.c	Tue Jan 19 11:32:51 1999
+++ linux/drivers/char/pcxx.c	Wed Mar 10 16:51:35 1999
@@ -68,7 +68,6 @@
 #include <linux/serial.h>
 #include <linux/tty_driver.h>
 #include <linux/malloc.h>
-#include <linux/string.h>
 #include <linux/init.h>
 #include <linux/version.h>
 
diff -u --recursive --new-file v2.2.3/linux/drivers/char/rocket.c linux/drivers/char/rocket.c
--- v2.2.3/linux/drivers/char/rocket.c	Sun Nov  8 14:02:55 1998
+++ linux/drivers/char/rocket.c	Wed Mar 10 16:51:35 1999
@@ -80,7 +80,6 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
-#include <linux/major.h>
 #include <linux/ioport.h>
 #ifdef ENABLE_PCI
 #include <linux/pci.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- v2.2.3/linux/drivers/char/tpqic02.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/char/tpqic02.c	Wed Mar 10 16:51:35 1999
@@ -75,7 +75,6 @@
 
 #include <linux/config.h>
 
-#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/Config.in linux/drivers/fc4/Config.in
--- v2.2.3/linux/drivers/fc4/Config.in	Fri Nov 27 13:09:23 1998
+++ linux/drivers/fc4/Config.in	Mon Mar 15 16:11:29 1999
@@ -7,12 +7,21 @@
 tristate 'Fibre Channel and FC4 SCSI support' CONFIG_FC4
 if [ ! "$CONFIG_FC4" = "n" ]; then
   comment 'FC4 drivers'
-  tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC
+  if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
+    tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC
+    tristate 'Sun SOC+ (aka SOCAL)' CONFIG_FC4_SOCAL
+  fi
   comment 'FC4 targets'
   dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO $CONFIG_SCSI
+  if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
+    dep_tristate 'Sun Enterprise Network Array (A5000 and EX500)' CONFIG_SCSI_FCAL $CONFIG_SCSI
+  else
+    dep_tristate 'Generic FC-AL disk driver' CONFIG_SCSI_FCAL $CONFIG_SCSI
+  fi
 else
   define_bool CONFIG_FC4_SOC n
+  define_bool CONFIG_FC4_SOCAL n
   define_bool CONFIG_SCSI_PLUTO n
+  define_bool CONFIG_SCSI_FCAL n
 fi
 endmenu
-
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/Makefile linux/drivers/fc4/Makefile
--- v2.2.3/linux/drivers/fc4/Makefile	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/Makefile	Mon Mar 15 16:11:29 1999
@@ -33,6 +33,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_FC4_SOCAL),y)
+L_OBJS += socal.o
+else
+  ifeq ($(CONFIG_FC4_SOCAL),m)
+  M_OBJS += socal.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
 fc4.o: $(MIX_OBJS) fc.o
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc-al.h linux/drivers/fc4/fc-al.h
--- v2.2.3/linux/drivers/fc4/fc-al.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fc-al.h	Mon Mar 15 16:11:29 1999
@@ -0,0 +1,27 @@
+/* fc-al.h: Definitions for Fibre Channel Arbitrated Loop topology.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Sources:
+ *	Fibre Channel Arbitrated Loop (FC-AL), ANSI, Rev. 4.5, 1995
+ */
+
+#ifndef __FC_AL_H
+#define __FC_AL_H
+
+/* Loop initialization payloads */
+#define	FC_AL_LISM	0x11010000	/* Select Master, 12B payload */
+#define FC_AL_LIFA	0x11020000	/* Fabric Assign AL_PA bitmap, 20B payload */
+#define FC_AL_LIPA	0x11030000	/* Previously Acquired AL_PA bitmap, 20B payload */
+#define FC_AL_LIHA	0x11040000	/* Hard Assigned AL_PA bitmap, 20B payload */
+#define FC_AL_LISA	0x11050000	/* Soft Assigned AL_PA bitmap, 20B payload */
+#define FC_AL_LIRP	0x11060000	/* Report AL_PA position map, 132B payload */
+#define FC_AL_LILP	0x11070000	/* Loop AL_PA position map, 132B payload */
+
+typedef struct {
+	u32	magic;
+	u8	len;
+	u8	alpa[127];
+} fc_al_posmap;
+
+#endif /* !(__FC_H) */
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc.c linux/drivers/fc4/fc.c
--- v2.2.3/linux/drivers/fc4/fc.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/fc4/fc.c	Mon Mar 15 16:11:29 1999
@@ -1,11 +1,24 @@
 /* fc.c: Generic Fibre Channel and FC4 SCSI driver.
  *
- * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
  * Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz)
  *
+ * There are two kinds of Fibre Channel adapters used in Linux. Either
+ * the adapter is "smart" and does all FC bookkeeping by itself and
+ * just presents a standard SCSI interface to the operating system
+ * (that's e.g. the case with Qlogic FC cards), or leaves most of the FC
+ * bookkeeping to the OS (e.g. soc, socal). Drivers for the former adapters
+ * will look like normal SCSI drivers (with the exception of max_id will be
+ * usually 127), the latter on the other side allows SCSI, IP over FC and other
+ * protocols. This driver tree is for the latter adapters.
+ *
+ * This file should support both Point-to-Point and Arbitrated Loop topologies.
+ *
  * Sources:
  *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
  *	dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ *	Fibre Channel Arbitrated Loop (FC-AL), Rev. 4.5, 1995
+ *	Fibre Channel Private Loop SCSI Direct Attach (FC-PLDA), Rev. 2.1, 1997
  */
 
 #include <linux/module.h>
@@ -25,7 +38,7 @@
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/semaphore.h>
-#include "fcp_scsi.h"
+#include "fcp_impl.h"
 #include "../scsi/hosts.h"
 
 /* #define FCDEBUG */
@@ -43,7 +56,7 @@
 #ifdef __sparc__
 static inline void *fc_dma_alloc(long size, char *name, dma_handle *dma)
 {
-	return (void *) sparc_dvma_malloc (size, "FCP SCSI cmd & rsp queues", dma);
+	return (void *) sparc_dvma_malloc (size, name, dma);
 }
 
 static inline dma_handle fc_sync_dma_entry(void *buf, int len, fc_channel *fc)
@@ -73,6 +86,9 @@
 #define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0]))
 #define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp)))
 
+static int fcp_scsi_queue_it(fc_channel *, Scsi_Cmnd *, fcp_cmnd *, int);
+void fcp_queue_empty(fc_channel *);
+
 static void fcp_scsi_insert_queue (fc_channel *fc, fcp_cmnd *fcmd)
 {
 	if (!fc->scsi_que) {
@@ -101,7 +117,7 @@
 
 fc_channel *fc_channels = NULL;
 
-#define LSMAGIC	0x2a3b4d2a
+#define LSMAGIC	620829043
 typedef struct {
 	/* Must be first */
 	struct semaphore sem;
@@ -111,10 +127,10 @@
 	fcp_cmnd *fcmds;
 	atomic_t todo;
 	struct timer_list timer;
-	int grace[1];
+	unsigned char grace[0];
 } ls;
 
-#define LSOMAGIC 0x2a3c4e3c
+#define LSOMAGIC 654907799
 typedef struct {
 	/* Must be first */
 	struct semaphore sem;
@@ -125,6 +141,15 @@
 	struct timer_list timer;
 } lso;
 
+#define LSEMAGIC 84482456
+typedef struct {
+	/* Must be first */
+	struct semaphore sem;
+	int magic;
+	int status;
+	struct timer_list timer;
+} lse;
+
 static void fcp_login_timeout(unsigned long data)
 {
 	ls *l = (ls *)data;
@@ -225,6 +250,97 @@
 	}
 }
 
+static void fcp_report_map_done(fc_channel *fc, int i, int status)
+{
+	fcp_cmnd *fcmd;
+	fc_hdr *fch;
+	unsigned char j;
+	ls *l = (ls *)fc->ls;
+	fc_al_posmap *p;
+	
+	FCD(("Report map done %d %d\n", i, status))
+	switch (status) {
+	case FC_STATUS_OK: /* Ok, let's have a fun on a loop */
+		fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
+		p = (fc_al_posmap *)(l->logi + 3 * i);
+#ifdef FCDEBUG
+		{
+		u32 *u = (u32 *)p;
+		FCD(("%08x\n", u[0]))
+		u ++;
+		FCD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+		}
+#endif		
+		if ((p->magic & 0xffff0000) != FC_AL_LILP || !p->len) {
+			printk ("FC: Bad magic from REPORT_AL_MAP on %s - %08x\n", fc->name, p->magic);
+			fc->state = FC_STATE_OFFLINE;
+		} else {
+			fc->posmap = (fcp_posmap *)kmalloc(sizeof(fcp_posmap)+p->len, GFP_KERNEL);
+			if (!fc->posmap) {
+				printk("FC: Not enough memory, offlining channel\n");
+				fc->state = FC_STATE_OFFLINE;
+			} else {
+				int k;
+				memset(fc->posmap, 0, sizeof(fcp_posmap)+p->len);
+				/* FIXME: This is where SOCAL transfers our AL-PA.
+				   Keep it here till we found out what other cards do... */
+				fc->sid = (p->magic & 0xff);
+				for (i = 0; i < p->len; i++)
+					if (p->alpa[i] == fc->sid)
+						break;
+				k = p->len;
+				if (i == p->len)
+					i = 0;
+				else {
+					p->len--;
+					i++;
+				}
+				fc->posmap->len = p->len;
+				for (j = 0; j < p->len; j++) {
+					if (i == k) i = 0;
+					fc->posmap->list[j] = p->alpa[i++];
+				}
+				fc->state = FC_STATE_ONLINE;
+			}
+		}
+		printk ("%s: ONLINE\n", fc->name);
+		if (atomic_dec_and_test (&l->todo))
+			up(&l->sem);
+		break;
+	case FC_STATUS_POINTTOPOINT: /* We're Point-to-Point, no AL... */
+		FCD(("SID %d DID %d\n", fc->sid, fc->did))
+		fcmd = l->fcmds + i;
+		fc_sync_dma_exit(fcmd->cmd, 3 * sizeof(logi), fc);
+		fch = &fcmd->fch;
+		memset(l->logi + 3 * i, 0, 3 * sizeof(logi));
+		FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
+		FILL_FCHDR_SID(fch, 0);
+		FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+		fch->param = 0;
+		l->logi [3 * i].code = LS_FLOGI;
+		fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc);
+		fcmd->rsp = fcmd->cmd + sizeof(logi);
+		fcmd->cmdlen = sizeof(logi);
+		fcmd->rsplen = sizeof(logi);
+		fcmd->data = (dma_handle)NULL;
+		fcmd->class = FC_CLASS_SIMPLE;
+		fcmd->proto = TYPE_EXTENDED_LS;
+		if (fc->hw_enque (fc, fcmd))
+			printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name);
+		break;
+	case FC_STATUS_ERR_OFFLINE:
+		fc->state = FC_STATE_MAYBEOFFLINE;
+		FCD (("FC is offline %d\n", l->grace[i]))
+		break;
+	default:
+		printk ("FLOGI failed for %s with status %d\n", fc->name, status);
+		/* Do some sort of error recovery here */
+		break;
+	}
+}
+
 void fcp_register(fc_channel *fc, u8 type, int unregister)
 {
 	int size, i;
@@ -247,12 +363,13 @@
 			for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++)
 				set_bit (i, fc->scsi_bitmap);
 			fc->scsi_free = fc->can_queue;
-			fc->token_tab = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
+			fc->cmd_slots = (fcp_cmnd **)kmalloc(slots * sizeof(fcp_cmnd*), GFP_KERNEL);
+			memset(fc->cmd_slots, 0, slots * sizeof(fcp_cmnd*));
 			fc->abort_count = 0;
 		} else {
 			fc->scsi_name[0] = 0;
 			kfree (fc->scsi_bitmap);
-			kfree (fc->token_tab);
+			kfree (fc->cmd_slots);
 			FCND(("Unregistering\n"));
 			if (fc->rst_pkt) {
 				if (fc->rst_pkt->eh_state == SCSI_STATE_UNUSED)
@@ -279,7 +396,7 @@
 	int sense_len;
 	int rsp_status;
 
-	fcmd = fc->token_tab[token];
+	fcmd = fc->cmd_slots[token];
 	if (!fcmd) return;
 	rsp = (fcp_rsp *) (fc->scsi_rsp_pool + fc->rsp_size * token);
 	SCpnt = SC_FCMND(fcmd);
@@ -343,27 +460,47 @@
 	fcmd->done=NULL;
 	clear_bit(token, fc->scsi_bitmap);
 	fc->scsi_free++;
-	FCD(("Calling scsi_done with %08lx\n", SCpnt->result))
+	FCD(("Calling scsi_done with %08x\n", SCpnt->result))
 	SCpnt->scsi_done(SCpnt);
 }
 
 void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch)
 {
+	int magic;
 	FCD(("receive_solicited %d %d %d\n", proto, token, status))
 	switch (proto) {
 	case TYPE_SCSI_FCP:
 		fcp_scsi_receive(fc, token, status, fch); break;
 	case TYPE_EXTENDED_LS:
-		if (fc->ls && ((ls *)(fc->ls))->magic == LSMAGIC) {
+	case PROTO_REPORT_AL_MAP:
+		magic = 0;
+		if (fc->ls)
+			magic = ((ls *)(fc->ls))->magic;
+		if (magic == LSMAGIC) {
 			ls *l = (ls *)fc->ls;
 			int i = (token >= l->count) ? token - l->count : token;
 
 			/* Let's be sure */
 			if ((unsigned)i < l->count && l->fcmds[i].fc == fc) {
-				fcp_login_done(fc, token, status);
+				if (proto == TYPE_EXTENDED_LS)
+					fcp_login_done(fc, token, status);
+				else
+					fcp_report_map_done(fc, token, status);
 				break;
 			}
 		}
+		FCD(("fc %p fc->ls %p fc->cmd_slots %p\n", fc, fc->ls, fc->cmd_slots))
+		if (proto == TYPE_EXTENDED_LS && !fc->ls && fc->cmd_slots) {
+			fcp_cmnd *fcmd;
+			
+			fcmd = fc->cmd_slots[token];
+			if (fcmd && fcmd->ls && ((ls *)(fcmd->ls))->magic == LSEMAGIC) {
+				lse *l = (lse *)fcmd->ls;
+				
+				l->status = status;
+				up (&l->sem);
+			}
+		}
 		break;
 	case PROTO_OFFLINE:
 		if (fc->ls && ((lso *)(fc->ls))->magic == LSOMAGIC) {
@@ -405,12 +542,12 @@
 	FCND(("fcp_inititialize %08lx\n", (long)fcp_init))
 	FCND(("fc_channels %08lx\n", (long)fc_channels))
 	FCND((" SID %d DID %d\n", fcchain->sid, fcchain->did))
-	l = kmalloc(sizeof (ls) + count * sizeof(int), GFP_KERNEL);
+	l = kmalloc(sizeof (ls) + count, GFP_KERNEL);
 	if (!l) {
 		printk ("FC: Cannot allocate memory for initialization\n");
 		return -ENOMEM;
 	}
-	memset (l, 0, sizeof(ls) + count * sizeof(int));
+	memset (l, 0, sizeof(ls) + count);
 	l->magic = LSMAGIC;
 	l->count = count;
 	FCND(("FCP Init for %d channels\n", count))
@@ -429,36 +566,25 @@
 	}
 	memset (l->logi, 0, count * 3 * sizeof(logi));
 	memset (l->fcmds, 0, count * sizeof(fcp_cmnd));
-	FCND(("Initializing FLOGI packets\n"))
 	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
-		fc_hdr *fch;
-
-		FCD(("SID %d DID %d\n", fc->sid, fc->did))
 		fc->state = FC_STATE_UNINITED;
+		fc->rst_pkt = NULL;	/* kmalloc when first used */
+	}
+	/* First try if we are in a AL topology */
+	FCND(("Initializing REPORT_MAP packets\n"))
+	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
 		fcmd = l->fcmds + i;
 		fc->login = fcmd;
 		fc->ls = (void *)l;
-		fc->rst_pkt = NULL;	/* kmalloc when first used */
-		fch = &fcmd->fch;
-		FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
-		FILL_FCHDR_SID(fch, 0);
-		FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
-		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
-		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
-		fch->param = 0;
-		l->logi [3 * i].code = LS_FLOGI;
+		/* Assumes sizeof(fc_al_posmap) < 3 * sizeof(logi), which is true */
 		fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc);
-		fcmd->rsp = fcmd->cmd + sizeof(logi);
-		fcmd->cmdlen = sizeof(logi);
-		fcmd->rsplen = sizeof(logi);
-		fcmd->data = (dma_handle)NULL;
-		fcmd->class = FC_CLASS_SIMPLE;
-		fcmd->proto = TYPE_EXTENDED_LS;
+		fcmd->proto = PROTO_REPORT_AL_MAP;
 		fcmd->token = i;
 		fcmd->fc = fc;
 	}
 	for (retry = 0; retry < 8; retry++) {
-		FCND(("Sending FLOGI/PLOGI packets\n"))
+		int nqueued = 0;
+		FCND(("Sending REPORT_MAP/FLOGI/PLOGI packets\n"))
 		for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
 			if (fc->state == FC_STATE_ONLINE || fc->state == FC_STATE_OFFLINE)
 				continue;
@@ -477,27 +603,56 @@
 			}
 			ret = fc->hw_enque (fc, fc->login);
 			enable_irq(fc->irq);
-			if (ret) printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name);
+			if (!ret) {
+				nqueued++;
+				continue;
+			}
+			if (ret == -ENOSYS && fc->login->proto == PROTO_REPORT_AL_MAP) {
+				/* Oh yes, this card handles Point-to-Point only, so let's try that. */
+				fc_hdr *fch;
+
+				FCD(("SID %d DID %d\n", fc->sid, fc->did))
+				fcmd = l->fcmds + i;
+				fc_sync_dma_exit(fcmd->cmd, 3 * sizeof(logi), fc);
+				fch = &fcmd->fch;
+				FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT);
+				FILL_FCHDR_SID(fch, 0);
+				FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+				FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+				FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+				fch->param = 0;
+				l->logi [3 * i].code = LS_FLOGI;
+				fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc);
+				fcmd->rsp = fcmd->cmd + sizeof(logi);
+				fcmd->cmdlen = sizeof(logi);
+				fcmd->rsplen = sizeof(logi);
+				fcmd->data = (dma_handle)NULL;
+				fcmd->class = FC_CLASS_SIMPLE;
+				fcmd->proto = TYPE_EXTENDED_LS;
+			} else
+				printk ("FC: Cannot enque FLOGI/REPORT_MAP packet on %s\n", fc->name);
 		}
 		
-		l->timer.expires = jiffies + 5 * HZ;
-		add_timer(&l->timer);
-
-		down(&l->sem);
-		if (!atomic_read(&l->todo)) {
-			FCND(("All channels answered in time\n"))
-			break; /* All fc channels have answered us */
+		if (nqueued) {
+			l->timer.expires = jiffies + 5 * HZ;
+			add_timer(&l->timer);
+
+			down(&l->sem);
+			if (!atomic_read(&l->todo)) {
+				FCND(("All channels answered in time\n"))
+				break; /* All fc channels have answered us */
+			}
 		}
 	}
 all_done:
-	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i += 3) {
+	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) {
+		fc->ls = NULL;
 		switch (fc->state) {
 		case FC_STATE_ONLINE: break;
 		case FC_STATE_OFFLINE: break;
 		default: fc_sync_dma_exit (l->fcmds[i].cmd, 3 * sizeof(logi), fc);
 			break;
 		}
-		fc->ls = NULL;
 	}
 	del_timer(&l->timer);
 	kfree (l->logi);
@@ -534,6 +689,7 @@
 		fcmd = l.fcmds + i;
 		fc->login = fcmd;
 		fc->ls = (void *)&l;
+		fcmd->did = fc->did;
 		fcmd->class = FC_CLASS_OFFLINE;
 		fcmd->proto = PROTO_OFFLINE;
 		fcmd->token = i;
@@ -549,6 +705,8 @@
 	down(&l.sem);
 	del_timer(&l.timer);
 	
+	for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++)
+		fc->ls = NULL;
 	kfree (l.fcmds);
 	return 0;
 }
@@ -565,14 +723,14 @@
 	}
 
 	ret = fcp_initialize (fcchain, count);
-
-	if (!ret) {	
-		if (!fc_channels)
-			fc_channels = fcchain;
-		else {
-			for (fc = fc_channels; fc->next; fc = fc->next);
-			fc->next = fcchain;
-		}
+	if (ret)
+		return ret;
+		
+	if (!fc_channels)
+		fc_channels = fcchain;
+	else {
+		for (fc = fc_channels; fc->next; fc = fc->next);
+		fc->next = fcchain;
 	}
 	return ret;
 }
@@ -622,7 +780,7 @@
 		fcmd->token = i;
 		cmd = fc->scsi_cmd_pool + i;
 
-		if (fc->encode_addr (SCpnt, cmd->fcp_addr)) {
+		if (fc->encode_addr (SCpnt, cmd->fcp_addr, fc, fcmd)) {
 			/* Invalid channel/id/lun and couldn't map it into fcp_addr */
 			clear_bit (i, fc->scsi_bitmap);
 			SCpnt->result = (DID_BAD_TARGET << 16);
@@ -630,7 +788,7 @@
 			return 0;
 		}
 		fc->scsi_free--;
-		fc->token_tab[fcmd->token] = fcmd;
+		fc->cmd_slots[fcmd->token] = fcmd;
 
 		if (SCpnt->device->tagged_supported) {
 			if (jiffies - fc->ages[SCpnt->channel * fc->targets + SCpnt->target] > (5 * 60 * HZ)) {
@@ -670,14 +828,14 @@
 		memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len);
 		FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8)))
 	}
-	FCD(("Trying to enque %08x\n", (int)fcmd))
+	FCD(("Trying to enque %p\n", fcmd))
 	if (!fc->scsi_que) {
 		if (!fc->hw_enque (fc, fcmd)) {
-			FCD(("hw_enque succeeded for %08x\n", (int)fcmd))
+			FCD(("hw_enque succeeded for %p\n", fcmd))
 			return 0;
 		}
 	}
-	FCD(("Putting into que1 %08x\n", (int)fcmd))
+	FCD(("Putting into que1 %p\n", fcmd))
 	fcp_scsi_insert_queue (fc, fcmd);
 	return 0;
 }
@@ -687,7 +845,7 @@
 	fcp_cmnd *fcmd = FCP_CMND(SCpnt);
 	fc_channel *fc = FC_SCMND(SCpnt);
 	
-	FCD(("Entering SCSI queuecommand %08x\n", (int)fcmd))
+	FCD(("Entering SCSI queuecommand %p\n", fcmd))
 	if (SCpnt->done != fcp_scsi_done) {
 		fcmd->done = SCpnt->done;
 		SCpnt->done = fcp_scsi_done;
@@ -708,11 +866,12 @@
 void fcp_queue_empty(fc_channel *fc)
 {
 	fcp_cmnd *fcmd;
+	
 	FCD(("Queue empty\n"))
 	while ((fcmd = fc->scsi_que)) {
 		/* The hw told us we can try again queue some packet */
 		if (fc->hw_enque (fc, fcmd))
-			return;
+			break;
 		fcp_scsi_remove_queue (fc, fcmd);
 	}
 }
@@ -725,7 +884,7 @@
 
 int fcp_scsi_abort(Scsi_Cmnd *SCpnt)
 {
-	/* Internal bookkeeping only. Lose 1 token_tab slot. */
+	/* Internal bookkeeping only. Lose 1 cmd_slots slot. */
 	fcp_cmnd *fcmd = FCP_CMND(SCpnt);
 	fc_channel *fc = FC_SCMND(SCpnt);
 	
@@ -733,14 +892,14 @@
 	 * We react to abort requests by simply forgetting
 	 * about the command and pretending everything's sweet.
 	 * This may or may not be silly. We can't, however,
-	 * immediately reuse the command's token_tab slot,
+	 * immediately reuse the command's cmd_slots slot,
 	 * as its result may arrive later and we cannot
 	 * check whether it is the aborted one, can't we?
 	 *
 	 * Therefore, after the first few aborts are done,
 	 * we tell the scsi error handler to do something clever.
 	 * It will eventually call host reset, refreshing
-	 * token_tab for us.
+	 * cmd_slots for us.
 	 *
 	 * There is a theoretical chance that we sometimes allow
 	 * more than can_queue packets to the jungle this way,
@@ -786,13 +945,13 @@
 		fcmd->token = 0;
 		cmd = fc->scsi_cmd_pool + 0;
 		FCD(("Preparing rst packet\n"))
-		if (fc->encode_addr (SCpnt, /*?*/cmd->fcp_addr))
+		fc->encode_addr (SCpnt, cmd->fcp_addr, fc, fcmd);
 		fc->rst_pkt->channel = SCpnt->channel;
 		fc->rst_pkt->target = SCpnt->target;
 		fc->rst_pkt->lun = 0;
 		fc->rst_pkt->cmd_len = 0;
 		
-		fc->token_tab[0] = fcmd;
+		fc->cmd_slots[0] = fcmd;
 
 		cmd->fcp_cntl = FCP_CNTL_QTYPE_ORDERED | FCP_CNTL_RESET;
 		fcmd->data = (dma_handle)NULL;
@@ -859,16 +1018,137 @@
 	printk ("FC: host reset\n");
 
 	for (i=0; i < fc->can_queue; i++) {
-		if (fc->token_tab[i] && SCpnt->result != DID_ABORT) {
+		if (fc->cmd_slots[i] && SCpnt->result != DID_ABORT) {
 			SCpnt->result = DID_RESET;
 			fcmd->done(SCpnt);
-			fc->token_tab[i] = NULL;
+			fc->cmd_slots[i] = NULL;
 		}
 	}
 	fc->reset(fc);
 	fc->abort_count = 0;
 	if (fcp_initialize(fc, 1)) return SUCCESS;
 	else return FAILED;
+}
+
+static int fcp_els_queue_it(fc_channel *fc, fcp_cmnd *fcmd)
+{
+	long i;
+
+	i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end);
+	set_bit (i, fc->scsi_bitmap);
+	fcmd->token = i;
+	fc->scsi_free--;
+	fc->cmd_slots[fcmd->token] = fcmd;
+	return fcp_scsi_queue_it(fc, NULL, fcmd, 0);
+}
+
+static int fc_do_els(fc_channel *fc, unsigned int alpa, void *data, int len)
+{
+	fcp_cmnd _fcmd, *fcmd;
+	fc_hdr *fch;
+	lse l;
+	int i;
+
+	fcmd = &_fcmd;
+	memset(fcmd, 0, sizeof(fcmd));
+	FCD(("PLOGI SID %d DID %d\n", fc->sid, alpa))
+	fch = &fcmd->fch;
+	FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, alpa);
+	FILL_FCHDR_SID(fch, fc->sid);
+	FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+	FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+	FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+	fch->param = 0;
+	fcmd->cmd = fc_sync_dma_entry (data, 2 * len, fc);
+	fcmd->rsp = fcmd->cmd + len;
+	fcmd->cmdlen = len;
+	fcmd->rsplen = len;
+	fcmd->data = (dma_handle)NULL;
+	fcmd->fc = fc;
+	fcmd->class = FC_CLASS_SIMPLE;
+	fcmd->proto = TYPE_EXTENDED_LS;
+	
+	memset (&l, 0, sizeof(lse));
+	l.magic = LSEMAGIC;
+	l.sem = MUTEX_LOCKED;
+	l.timer.function = fcp_login_timeout;
+	l.timer.data = (unsigned long)&l;
+	l.status = FC_STATUS_TIMED_OUT;
+	fcmd->ls = (void *)&l;
+
+	disable_irq(fc->irq);	
+	fcp_els_queue_it(fc, fcmd);
+	enable_irq(fc->irq);
+
+	for (i = 0;;) {
+		l.timer.expires = jiffies + 5 * HZ;
+		add_timer(&l.timer);
+		down(&l.sem);
+		del_timer(&l.timer);
+		if (l.status != FC_STATUS_TIMED_OUT) break;
+		if (++i == 3) break;
+		disable_irq(fc->irq);
+		fcp_scsi_queue_it(fc, NULL, fcmd, 0);
+		enable_irq(fc->irq);
+	}
+	
+	clear_bit(fcmd->token, fc->scsi_bitmap);
+	fc->scsi_free++;
+	fc_sync_dma_exit (fcmd->cmd, 2 * len, fc);
+	return l.status;
+}
+
+int fc_do_plogi(fc_channel *fc, unsigned char alpa, fc_wwn *node, fc_wwn *nport)
+{
+	logi *l;
+	int status;
+
+	l = (logi *)kmalloc(2 * sizeof(logi), GFP_DMA);
+	if (!l) return -ENOMEM;
+	memset(l, 0, 2 * sizeof(logi));
+	l->code = LS_PLOGI;
+	memcpy (&l->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn));
+	memcpy (&l->node_wwn, &fc->wwn_node, sizeof(fc_wwn));
+	memcpy (&l->common, fc->common_svc, sizeof(common_svc_parm));
+	memcpy (&l->class1, fc->class_svcs, 3*sizeof(svc_parm));
+	status = fc_do_els(fc, alpa, l, sizeof(logi));
+	if (status == FC_STATUS_OK) {
+		if (l[1].code == LS_ACC) {
+#ifdef FCDEBUG
+			u32 *u = (u32 *)&l[1].nport_wwn;
+			FCD(("AL-PA %02x: Port WWN %08x%08x Node WWN %08x%08x\n", alpa, 
+				u[0], u[1], u[2], u[3]))
+#endif
+			memcpy(nport, &l[1].nport_wwn, sizeof(fc_wwn));
+			memcpy(node, &l[1].node_wwn, sizeof(fc_wwn));
+		} else
+			status = FC_STATUS_BAD_RSP;
+	}
+	kfree(l);
+	return status;
+}
+
+typedef struct {
+	unsigned int code;
+	unsigned params[4];
+} prli;
+
+int fc_do_prli(fc_channel *fc, unsigned char alpa)
+{
+	prli *p;
+	int status;
+
+	p = (prli *)kmalloc(2 * sizeof(prli), GFP_DMA);
+	if (!p) return -ENOMEM;
+	memset(p, 0, 2 * sizeof(prli));
+	p->code = LS_PRLI;
+	p->params[0] = 0x08002000;
+	p->params[3] = 0x00000022;
+	status = fc_do_els(fc, alpa, p, sizeof(prli));
+	if (status == FC_STATUS_OK && p[1].code != LS_PRLI_ACC && p[1].code != LS_ACC)
+		status = FC_STATUS_BAD_RSP;
+	kfree(p);
+	return status;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc.h linux/drivers/fc4/fc.h
--- v2.2.3/linux/drivers/fc4/fc.h	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/fc.h	Mon Mar 15 16:11:29 1999
@@ -1,6 +1,6 @@
 /* fc.h: Definitions for Fibre Channel Physical and Signaling Interface.
  *
- * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996-1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
  *
  * Sources:
  *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
@@ -11,13 +11,16 @@
 #define __FC_H
 
 /* World Wide Name */
-#define NAAID_IEEE	1
-#define NAAID_IEEE_EXT	2
-#define NAAID_LOCAL	3
-#define NAAID_IP	4
-#define NAAID_CCITT	12
-#define NAAID_CCITT_GRP	14
+#define NAAID_IEEE		1
+#define NAAID_IEEE_EXT		2
+#define NAAID_LOCAL		3
+#define NAAID_IP		4
+#define NAAID_IEEE_REG		5
+#define NAAID_IEEE_REG_EXT	6
+#define NAAID_CCITT		12
+#define NAAID_CCITT_GRP		14
 
+/* This is NAAID_IEEE_EXT scheme */
 typedef struct {
 	u32	naaid:4;
 	u32	nportid:12;
@@ -165,6 +168,7 @@
 /* Extended SVC commands */
 #define LS_RJT			0x01000000
 #define LS_ACC			0x02000000
+#define LS_PRLI_ACC		0x02100014
 #define LS_PLOGI		0x03000000
 #define LS_FLOGI		0x04000000
 #define LS_LOGO			0x05000000
@@ -182,7 +186,11 @@
 #define LS_TEST			0x11000000
 #define LS_RRQ			0x12000000
 #define LS_IDENT		0x20000000
+#define LS_PRLI			0x20100014
 #define LS_DISPLAY		0x21000000
+#define LS_PRLO			0x21100014
+#define LS_PDISC		0x50000000
+#define LS_ADISC		0x52000000
 
 typedef struct {
 	u8	fcph_hi, fcph_lo;
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fc_syms.c linux/drivers/fc4/fc_syms.c
--- v2.2.3/linux/drivers/fc4/fc_syms.c	Thu Apr 23 20:21:33 1998
+++ linux/drivers/fc4/fc_syms.c	Mon Mar 15 16:11:29 1999
@@ -13,7 +13,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-#include "fcp_scsi.h"
+#include "fcp_impl.h"
 
 EXPORT_SYMBOL(fcp_init);
 EXPORT_SYMBOL(fcp_release);
@@ -21,6 +21,8 @@
 EXPORT_SYMBOL(fcp_receive_solicited);
 EXPORT_SYMBOL(fc_channels);
 EXPORT_SYMBOL(fcp_state_change);
+EXPORT_SYMBOL(fc_do_plogi);
+EXPORT_SYMBOL(fc_do_prli);
 
 /* SCSI stuff */
 EXPORT_SYMBOL(fcp_scsi_queuecommand);
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fcp_impl.h linux/drivers/fc4/fcp_impl.h
--- v2.2.3/linux/drivers/fc4/fcp_impl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/fcp_impl.h	Mon Mar 15 16:11:29 1999
@@ -0,0 +1,168 @@
+/* fcp_impl.h: Generic SCSI on top of FC4 - our interface defines.
+ *
+ * Copyright (C) 1997-1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Jirka Hanika (geo@ff.cuni.cz)
+ */
+
+#ifndef _FCP_SCSI_H
+#define _FCP_SCSI_H
+
+#include <linux/types.h>
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+
+#include "fc.h"
+#include "fcp.h"
+#include "fc-al.h"
+
+#ifdef __sparc__
+
+#include <asm/sbus.h>
+typedef u32	dma_handle;
+
+#else
+#error Need to port FC layer to your architecture
+#endif
+
+/* 0 or 1 */
+#define	FCP_SCSI_USE_NEW_EH_CODE	0
+
+#define FC_CLASS_OUTBOUND	0x01
+#define FC_CLASS_INBOUND	0x02
+#define FC_CLASS_SIMPLE		0x03
+#define FC_CLASS_IO_WRITE	0x04
+#define FC_CLASS_IO_READ	0x05
+#define FC_CLASS_UNSOLICITED	0x06
+#define FC_CLASS_OFFLINE	0x08
+
+#define PROTO_OFFLINE		0x02
+#define PROTO_REPORT_AL_MAP	0x03
+#define PROTO_FORCE_LIP		0x06
+
+struct _fc_channel; 
+
+typedef struct fcp_cmnd {
+	struct fcp_cmnd		*next;
+	struct fcp_cmnd		*prev;
+	void			(*done)(Scsi_Cmnd *);
+	unsigned short		proto;
+	unsigned short		token;
+	unsigned int		did;
+	/* FCP SCSI stuff */
+	dma_handle		data;
+	/* From now on this cannot be touched for proto == TYPE_SCSI_FCP */
+	fc_hdr			fch;
+	dma_handle		cmd;
+	dma_handle		rsp;
+	int			cmdlen;
+	int			rsplen;
+	int			class;
+	int			datalen;
+	/* This is just used as a verification during login */
+	struct _fc_channel	*fc;
+	void			*ls;
+} fcp_cmnd;
+
+typedef struct {
+	unsigned int		len;
+	unsigned char		list[0];
+} fcp_posmap;
+
+typedef struct _fc_channel {
+	struct _fc_channel	*next;
+	int			irq;
+	int			state;
+	int			sid;
+	int			did;
+	char			name[16];
+	void			(*fcp_register)(struct _fc_channel *, u8, int);
+	void			(*reset)(struct _fc_channel *);
+	int			(*hw_enque)(struct _fc_channel *, fcp_cmnd *);
+	fc_wwn			wwn_node;
+	fc_wwn			wwn_nport;
+	fc_wwn			wwn_dest;
+	common_svc_parm		*common_svc;
+	svc_parm		*class_svcs;
+#ifdef __sparc__	
+	struct linux_sbus_device *dev;
+#endif
+	struct module		*module;
+	/* FCP SCSI stuff */
+	short			can_queue;
+	short			abort_count;
+	int			rsp_size;
+	fcp_cmd			*scsi_cmd_pool;
+	char			*scsi_rsp_pool;
+	dma_handle		dma_scsi_cmd, dma_scsi_rsp;
+	long			*scsi_bitmap;
+	long			scsi_bitmap_end;
+	int			scsi_free;
+	int			(*encode_addr)(Scsi_Cmnd *, u16 *, struct _fc_channel *, fcp_cmnd *);
+	fcp_cmnd		*scsi_que;
+	char			scsi_name[4];
+	fcp_cmnd		**cmd_slots;
+	int			channels;
+	int			targets;
+	long			*ages;
+	Scsi_Cmnd		*rst_pkt;
+	fcp_posmap		*posmap;
+	/* LOGIN stuff */
+	fcp_cmnd		*login;
+	void			*ls;
+} fc_channel;
+
+extern fc_channel *fc_channels;
+
+#define FC_STATE_UNINITED	0
+#define FC_STATE_ONLINE		1
+#define FC_STATE_OFFLINE	2
+#define FC_STATE_RESETING	3
+#define FC_STATE_FPORT_OK	4
+#define FC_STATE_MAYBEOFFLINE	5
+
+#define FC_STATUS_OK			0
+#define FC_STATUS_P_RJT			2
+#define FC_STATUS_F_RJT			3
+#define FC_STATUS_P_BSY			4
+#define FC_STATUS_F_BSY			5
+#define FC_STATUS_ERR_OFFLINE		0x11
+#define FC_STATUS_TIMEOUT		0x12
+#define FC_STATUS_ERR_OVERRUN		0x13
+#define FC_STATUS_POINTTOPOINT		0x15
+#define FC_STATUS_AL			0x16
+#define FC_STATUS_UNKNOWN_CQ_TYPE	0x20
+#define FC_STATUS_BAD_SEG_CNT		0x21
+#define FC_STATUS_MAX_XCHG_EXCEEDED	0x22
+#define FC_STATUS_BAD_XID		0x23
+#define FC_STATUS_XCHG_BUSY		0x24
+#define FC_STATUS_BAD_POOL_ID		0x25
+#define FC_STATUS_INSUFFICIENT_CQES	0x26
+#define FC_STATUS_ALLOC_FAIL		0x27
+#define FC_STATUS_BAD_SID		0x28
+#define FC_STATUS_NO_SEQ_INIT		0x29
+#define FC_STATUS_TIMED_OUT		-1
+#define FC_STATUS_BAD_RSP		-2
+
+void fcp_queue_empty(fc_channel *);
+int fcp_init(fc_channel *);
+void fcp_release(fc_channel *fc_chain, int count);
+void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *);
+void fcp_state_change(fc_channel *, int);
+int fc_do_plogi(fc_channel *, unsigned char, fc_wwn *, fc_wwn *);
+int fc_do_prli(fc_channel *, unsigned char);
+
+#define for_each_fc_channel(fc)				\
+	for (fc = fc_channels; fc; fc = fc->next)
+	
+#define for_each_online_fc_channel(fc) 			\
+	for_each_fc_channel(fc)				\
+		if (fc->state == FC_STATE_ONLINE)
+
+int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int fcp_old_abort(Scsi_Cmnd *);
+int fcp_scsi_abort(Scsi_Cmnd *);
+int fcp_scsi_dev_reset(Scsi_Cmnd *);
+int fcp_scsi_bus_reset(Scsi_Cmnd *);
+int fcp_scsi_host_reset(Scsi_Cmnd *);
+
+#endif /* !(_FCP_SCSI_H) */
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/fcp_scsi.h linux/drivers/fc4/fcp_scsi.h
--- v2.2.3/linux/drivers/fc4/fcp_scsi.h	Tue Dec 22 14:16:55 1998
+++ linux/drivers/fc4/fcp_scsi.h	Wed Dec 31 16:00:00 1969
@@ -1,151 +0,0 @@
-/* fcp_scsi.h: Generic SCSI on top of FC4 - interface defines.
- *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1998 Jirka Hanika (geo@ff.cuni.cz)
- */
-
-#ifndef _FCP_SCSI_H
-#define _FCP_SCSI_H
-
-#include <linux/types.h>
-#include <linux/blk.h>
-#include "../scsi/scsi.h"
-
-#include "fc.h"
-#include "fcp.h"
-
-#ifdef __sparc__
-
-#include <asm/sbus.h>
-typedef u32	dma_handle;
-
-#else
-#error Need to port FC layer to your architecture
-#endif
-
-/* 0 or 1 */
-#define	FCP_SCSI_USE_NEW_EH_CODE	0
-
-#define FC_CLASS_OUTBOUND	0x01
-#define FC_CLASS_INBOUND	0x02
-#define FC_CLASS_SIMPLE		0x03
-#define FC_CLASS_IO_WRITE	0x04
-#define FC_CLASS_IO_READ	0x05
-#define FC_CLASS_UNSOLICITED	0x06
-#define FC_CLASS_OFFLINE	0x08
-
-#define PROTO_OFFLINE		0x02
-
-struct _fc_channel; 
-
-typedef struct fcp_cmnd {
-	struct fcp_cmnd		*next;
-	struct fcp_cmnd		*prev;
-	void			(*done)(Scsi_Cmnd *);
-	int			proto;
-	int		 	token;
-	/* FCP SCSI stuff */
-	dma_handle		data;
-	/* From now on this cannot be touched for proto == TYPE_SCSI_FCP */
-	fc_hdr			fch;
-	dma_handle		cmd;
-	dma_handle		rsp;
-	int			cmdlen;
-	int			rsplen;
-	int			class;
-	int			datalen;
-	/* This is just used as a verification during login */
-	struct _fc_channel	*fc;
-} fcp_cmnd;
-
-typedef struct _fc_channel {
-	struct _fc_channel	*next;
-	int			irq;
-	int			state;
-	int			sid;
-	int			did;
-	char			name[16];
-	void			(*fcp_register)(struct _fc_channel *, u8, int);
-	void			(*reset)(struct _fc_channel *);
-	int			(*hw_enque)(struct _fc_channel *, fcp_cmnd *);
-	fc_wwn			wwn_node;
-	fc_wwn			wwn_nport;
-	fc_wwn			wwn_dest;
-	common_svc_parm		*common_svc;
-	svc_parm		*class_svcs;
-#ifdef __sparc__	
-	struct linux_sbus_device *dev;
-#endif
-	struct module		*module;
-	/* FCP SCSI stuff */
-	short			can_queue;
-	short			abort_count;
-	int			rsp_size;
-	fcp_cmd			*scsi_cmd_pool;
-	char			*scsi_rsp_pool;
-	dma_handle		dma_scsi_cmd, dma_scsi_rsp;
-	long			*scsi_bitmap;
-	long			scsi_bitmap_end;
-	int			scsi_free;
-	int			(*encode_addr)(Scsi_Cmnd *cmnd, u16 *addr);
-	fcp_cmnd		*scsi_que;
-	char			scsi_name[4];
-	fcp_cmnd		**token_tab;
-	int			channels;
-	int			targets;
-	long			*ages;
-	Scsi_Cmnd		*rst_pkt;
-	/* LOGIN stuff */
-	fcp_cmnd		*login;
-	void			*ls;
-} fc_channel;
-
-extern fc_channel *fc_channels;
-
-#define FC_STATE_UNINITED	0
-#define FC_STATE_ONLINE		1
-#define FC_STATE_OFFLINE	2
-#define FC_STATE_RESETING	3
-#define FC_STATE_FPORT_OK	4
-#define FC_STATE_MAYBEOFFLINE	5
-
-#define FC_STATUS_OK			0
-#define FC_STATUS_P_RJT			2
-#define FC_STATUS_F_RJT			3
-#define FC_STATUS_P_BSY			4
-#define FC_STATUS_F_BSY			5
-#define FC_STATUS_ERR_OFFLINE		0x11
-#define FC_STATUS_TIMEOUT		0x12
-#define FC_STATUS_ERR_OVERRUN		0x13
-#define FC_STATUS_UNKNOWN_CQ_TYPE	0x20
-#define FC_STATUS_BAD_SEG_CNT		0x21
-#define FC_STATUS_MAX_XCHG_EXCEEDED	0x22
-#define FC_STATUS_BAD_XID		0x23
-#define FC_STATUS_XCHG_BUSY		0x24
-#define FC_STATUS_BAD_POOL_ID		0x25
-#define FC_STATUS_INSUFFICIENT_CQES	0x26
-#define FC_STATUS_ALLOC_FAIL		0x27
-#define FC_STATUS_BAD_SID		0x28
-#define FC_STATUS_NO_SEQ_INIT		0x29
-
-void fcp_queue_empty(fc_channel *);
-int fcp_init(fc_channel *);
-void fcp_release(fc_channel *fc_chain, int count);
-void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *);
-void fcp_state_change(fc_channel *, int);
-
-#define for_each_fc_channel(fc)				\
-	for (fc = fc_channels; fc; fc = fc->next)
-	
-#define for_each_online_fc_channel(fc) 			\
-	for_each_fc_channel(fc)				\
-		if (fc->state == FC_STATE_ONLINE)
-
-int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
-int fcp_old_abort(Scsi_Cmnd *);
-int fcp_scsi_abort(Scsi_Cmnd *);
-int fcp_scsi_dev_reset(Scsi_Cmnd *);
-int fcp_scsi_bus_reset(Scsi_Cmnd *);
-int fcp_scsi_host_reset(Scsi_Cmnd *);
-
-#endif /* !(_FCP_SCSI_H) */
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/soc.c linux/drivers/fc4/soc.c
--- v2.2.3/linux/drivers/fc4/soc.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/fc4/soc.c	Mon Mar 15 16:11:29 1999
@@ -1,6 +1,6 @@
 /* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support.
  *
- * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
  * Copyright (C) 1997,1998 Jirka Hanika (geo@ff.cuni.cz)
  *
  * Sources:
@@ -9,8 +9,6 @@
  *
  * Supported hardware:
  *      Tested on SOC sbus card bought with SS1000 in Linux running on SS5 and Ultra1. 
- *      Should run on on-board SOC/SOC+ cards of Ex000 servers as well, but it is not
- *      tested (let us know if you succeed).
  *      For SOC sbus cards, you have to make sure your FCode is 1.52 or later.
  *      If you have older FCode, you should try to upgrade or get SOC microcode from Sun
  *      (the microcode is present in Solaris soc driver as well). In that case you need
@@ -20,7 +18,7 @@
  */
 
 static char *version =
-        "soc.c:v1.2 27/Feb/98 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jirka Hanika (geo@ff.cuni.cz)\n";
+        "soc.c:v1.3 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz), Jirka Hanika (geo@ff.cuni.cz)\n";
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -34,7 +32,6 @@
 #include <linux/malloc.h>
 #include <linux/string.h>
 #include <linux/init.h>
-#include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -51,10 +48,10 @@
 /* #define SOCDEBUG */
 /* #define HAVE_SOC_UCODE */
 
-#include "fcp_scsi.h"
+#include "fcp_impl.h"
 #include "soc.h"
 #ifdef HAVE_SOC_UCODE
-#include "soc_asm.c"
+#include "soc_asm.h"
 #endif
 
 #define soc_printk printk ("soc%d: ", s->soc_no); printk 
@@ -397,6 +394,10 @@
 		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
 		request->shdr.flags = port->flags;
 		break;
+		
+	case PROTO_REPORT_AL_MAP:
+		/* SOC only supports Point-to-Point topology, no FC-AL, sorry... */
+		return -ENOSYS;
 
 	default: 
 		request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token);
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/soc.h linux/drivers/fc4/soc.h
--- v2.2.3/linux/drivers/fc4/soc.h	Mon Jan 12 15:19:36 1998
+++ linux/drivers/fc4/soc.h	Mon Mar 15 16:11:29 1999
@@ -8,7 +8,7 @@
 
 #include "fc.h"
 #include "fcp.h"
-#include "fcp_scsi.h"
+#include "fcp_impl.h"
 
 /* Hardware structures and constants first {{{ */
 
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/socal.c linux/drivers/fc4/socal.c
--- v2.2.3/linux/drivers/fc4/socal.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/socal.c	Mon Mar 15 16:11:29 1999
@@ -0,0 +1,854 @@
+/* socal.c: Sparc SUNW,socal (SOC+) Fibre Channel Sbus adapter support.
+ *
+ * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ *
+ * Sources:
+ *	Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
+ *	dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
+ *	SOC+ Programming Guide 0.1
+ *	Fibre Channel Arbitrated Loop (FC-AL), dpANS rev. 4.5, 1995
+ *
+ * Supported hardware:
+ *      On-board SOC+ adapters of Ultra Enterprise servers and sun4d.
+ */
+
+static char *version =
+        "socal.c: SOC+ driver v1.1 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <asm/byteorder.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/auxio.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+/* #define SOCALDEBUG */
+/* #define HAVE_SOCAL_UCODE */
+/* #define USE_64BIT_MODE */
+
+#include "fcp_impl.h"
+#include "socal.h"
+#ifdef HAVE_SOCAL_UCODE
+#include "socal_asm.h"
+#endif
+
+#define socal_printk printk ("socal%d: ", s->socal_no); printk 
+
+#ifdef SOCALDEBUG
+#define SOD(x)  socal_printk x;
+#else
+#define SOD(x)
+#endif
+
+#define for_each_socal(s) for (s = socals; s; s = s->next)
+struct socal *socals = NULL;
+
+/* I don't think our VIS mem* routines will behave well
+   in IO... */
+static void socal_memcpy(void *d, void *s, int size)
+{
+	u32 *dp = (u32 *)d, *sp = (u32 *)s;
+	while (size) {
+		*dp++ = *sp++;
+		size -= sizeof(u32);
+	}
+}
+
+static void socal_bzero(void *d, int size)
+{
+	u32 *dp = (u32 *)d;
+	while (size) {
+		*dp++ = 0;
+		size -= sizeof(u32);
+	}
+}
+
+static inline void socal_disable(struct socal *s)
+{
+	s->regs->imask = 0; s->regs->cmd = SOCAL_CMD_SOFT_RESET;
+}
+
+static inline void socal_enable(struct socal *s)
+{
+	SOD(("enable %08x\n", s->cfg))
+	s->regs->sae = 0; s->regs->cfg = s->cfg;
+	s->regs->cmd = SOCAL_CMD_RSP_QALL; 
+	SOCAL_SETIMASK(s, SOCAL_IMASK_RSP_QALL | SOCAL_IMASK_SAE);
+	SOD(("imask %08x %08x\n", s->imask, s->regs->imask));
+}
+
+static void socal_reset(fc_channel *fc)
+{
+	socal_port *port = (socal_port *)fc;
+	struct socal *s = port->s;
+	
+	/* FIXME */
+	socal_disable(s);
+	s->req[0].seqno = 1;
+	s->req[1].seqno = 1;
+	s->rsp[0].seqno = 1;
+	s->rsp[1].seqno = 1;
+	s->req[0].in = 0;
+	s->req[1].in = 0;
+	s->rsp[0].in = 0;
+	s->rsp[1].in = 0;
+	s->req[0].out = 0;
+	s->req[1].out = 0;
+	s->rsp[0].out = 0;
+	s->rsp[1].out = 0;
+
+	/* FIXME */
+	socal_enable(s);
+}
+
+static void inline socal_solicited (struct socal *s, int qno)
+{
+	fc_hdr fchdr;
+	socal_rsp *hwrsp;
+	socal_cq *sw_cq;
+	int token;
+	int status;
+	fc_channel *fc;
+
+	sw_cq = &s->rsp[qno];
+
+	if (sw_cq->pool == NULL) {
+		SOD(("address %08x xram %p\n", sw_cq->hw_cq->address, s->xram))
+		sw_cq->pool =
+			(socal_req *)(s->xram + (sw_cq->hw_cq->address & 0xfffe));
+	}
+	/* Finally an improvement against old SOC :) */
+	sw_cq->in = s->regs->respr[qno];
+	SOD (("socal_solicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last))
+	for (;;) {
+		hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out;
+		SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out))
+		
+#if defined(SOCALDEBUG) && 0
+		{
+		u32 *u = (u32 *)hwrsp;
+		SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+		u += 8;
+		SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+		u = (u32 *)s->xram;
+		while (u < ((u32 *)s->regs)) {
+			if (u[0] == 0x00003000 || u[0] == 0x00003801) {
+			SOD(("Found at %04lx\n", (unsigned long)u - (unsigned long)s->xram))
+			SOD(("  %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+			u += 8;
+			SOD(("  %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+			u -= 8;
+			}
+			u++;
+		}
+		}
+#endif
+
+		token = hwrsp->shdr.token;
+		status = hwrsp->status;
+		fc = (fc_channel *)(&s->port[(token >> 11) & 1]);
+		
+		SOD(("Solicited token %08x status %08x\n", token, status))
+		if (status == SOCAL_OK)
+			fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), FC_STATUS_OK, NULL);
+		else {
+			socal_memcpy(&fchdr, &hwrsp->fchdr, sizeof(fchdr));
+			/* We have intentionally defined FC_STATUS_* constants to match SOCAL_* constants, otherwise
+			   we'd have to translate status */
+			fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), status, &fchdr);
+		}
+			
+		if (++sw_cq->out > sw_cq->last) {
+			sw_cq->seqno++;
+			sw_cq->out = 0;
+		}
+		
+		if (sw_cq->out == sw_cq->in) {
+			sw_cq->in = s->regs->respr[qno];
+			if (sw_cq->out == sw_cq->in) {
+				/* Tell the hardware about it */
+				s->regs->cmd = (sw_cq->out << 24) | (SOCAL_CMD_RSP_QALL & ~(SOCAL_CMD_RSP_Q0 << qno));
+				/* Read it, so that we're sure it has been updated */
+				s->regs->cmd;
+				sw_cq->in = s->regs->respr[qno];
+				if (sw_cq->out == sw_cq->in)
+					break;
+			}
+		}
+	}
+}
+
+static void inline socal_request (struct socal *s, u32 cmd)
+{
+	SOCAL_SETIMASK(s, s->imask & ~(cmd & SOCAL_CMD_REQ_QALL));
+	SOD(("imask %08x %08x\n", s->imask, s->regs->imask));
+
+	SOD(("Queues available %08x OUT %X\n", cmd, s->regs->reqpr[0]))
+	if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) {
+		fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port]));
+		if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out))
+			fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
+	} else
+		fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port]));
+	if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE)
+		s->curr_port ^= 1;
+}
+
+static void inline socal_unsolicited (struct socal *s, int qno)
+{
+	socal_rsp *hwrsp, *hwrspc;
+	socal_cq *sw_cq;
+	int count;
+	int status;
+	int flags;
+	fc_channel *fc;
+
+	sw_cq = &s->rsp[qno];
+	if (sw_cq->pool == NULL) {
+		SOD(("address %08x xram %p\n", sw_cq->hw_cq->address, s->xram))
+		sw_cq->pool =
+			(socal_req *)(s->xram + (sw_cq->hw_cq->address & 0xfffe));
+	}
+
+	sw_cq->in = s->regs->respr[qno];
+	SOD (("socal_unsolicited, %d packets arrived, in %d\n", (sw_cq->in - sw_cq->out) & sw_cq->last, sw_cq->in))
+	while (sw_cq->in != sw_cq->out) {
+		/* ...real work per entry here... */
+		hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out;
+		SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out))
+
+#if defined(SOCALDEBUG) && 0
+		{
+		u32 *u = (u32 *)hwrsp;
+		SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+		u += 8;
+		SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
+		}
+#endif
+
+		hwrspc = NULL;
+		flags = hwrsp->shdr.flags;
+		count = hwrsp->count;
+		fc = (fc_channel *)&s->port[flags & SOCAL_PORT_B];
+		SOD(("FC %08lx\n", (long)fc))
+		
+		if (count != 1) {
+			/* Ugh, continuation entries */
+			u8 in;
+
+			if (count != 2) {
+				printk("%s: Too many continuations entries %d\n", fc->name, count);
+				goto update_out;
+			}
+			
+			in = sw_cq->in;
+			if (in < sw_cq->out) in += sw_cq->last + 1;
+			if (in < sw_cq->out + 2) {
+				/* Ask the hardware about it if they haven't arrived yet */
+				s->regs->cmd = (sw_cq->out << 24) | (SOCAL_CMD_RSP_QALL & ~(SOCAL_CMD_RSP_Q0 << qno));
+				/* Read it, so that we're sure it has been updated */
+				s->regs->cmd;
+				sw_cq->in = s->regs->respr[qno];
+				in = sw_cq->in;
+				if (in < sw_cq->out) in += sw_cq->last + 1;
+				if (in < sw_cq->out + 2) /* Nothing came, let us wait */
+					return;
+			}
+			if (sw_cq->out == sw_cq->last)
+				hwrspc = (socal_rsp *)sw_cq->pool;
+			else
+				hwrspc = hwrsp + 1;
+		}
+		
+		switch (flags & ~SOCAL_PORT_B) {
+		case SOCAL_STATUS:
+			status = hwrsp->status;
+			switch (status) {
+			case SOCAL_ONLINE:
+				SOD(("State change to ONLINE\n"));
+				fcp_state_change(fc, FC_STATE_ONLINE);
+				break;
+			case SOCAL_ONLINE_LOOP:
+				SOD(("State change to ONLINE_LOOP\n"));
+				fcp_state_change(fc, FC_STATE_ONLINE);
+				break;
+			case SOCAL_OFFLINE:
+				SOD(("State change to OFFLINE\n"));
+				fcp_state_change(fc, FC_STATE_OFFLINE);
+				break;
+			default:
+				printk ("%s: Unknown STATUS no %d\n", fc->name, status);
+				break;
+			}
+			break;
+		case (SOCAL_UNSOLICITED|SOCAL_FC_HDR):
+			{
+				int r_ctl = *((u8 *)&hwrsp->fchdr);
+				unsigned len;
+				char buf[64];
+				
+				if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) {
+					len = hwrsp->shdr.bytecnt;
+					if (len < 4 || !hwrspc)
+						printk ("%s: Invalid R_CTL %02x continuation entries\n", fc->name, r_ctl);
+					else {
+						if (len > 60) len = 60;
+						socal_memcpy (buf, hwrspc, (len + 3) & ~3);
+						if (*(u32 *)buf == LS_DISPLAY) {
+							int i;
+							
+							for (i = 4; i < len; i++)
+								if (buf[i] == '\n') buf[i] = ' ';
+							buf[len] = 0;
+							printk ("%s message: %s\n", fc->name, buf + 4);
+						} else {
+							printk ("%s: Unknown LS_CMD %08x\n", fc->name, *(u32 *)buf);
+						}
+					}
+				} else
+					printk ("%s: Unsolicited R_CTL %02x not handled\n", fc->name, r_ctl);
+			}
+			break;
+		default:
+			printk ("%s: Unexpected flags %08x\n", fc->name, flags);
+			break;
+		}
+update_out:
+		if (++sw_cq->out > sw_cq->last) {
+			sw_cq->seqno++;
+			sw_cq->out = 0;
+		}
+		
+		if (hwrspc) {
+			if (++sw_cq->out > sw_cq->last) {
+				sw_cq->seqno++;
+				sw_cq->out = 0;
+			}
+		}
+		
+		if (sw_cq->out == sw_cq->in) {
+			sw_cq->in = s->regs->respr[qno];
+			if (sw_cq->out == sw_cq->in) {
+				/* Tell the hardware about it */
+				s->regs->cmd = (sw_cq->out << 24) | (SOCAL_CMD_RSP_QALL & ~(SOCAL_CMD_RSP_Q0 << qno));
+				/* Read it, so that we're sure it has been updated */
+				s->regs->cmd;
+				sw_cq->in = s->regs->respr[qno];
+			}
+		}
+	}
+}
+
+static void socal_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u32 cmd;
+	unsigned long flags;
+	register struct socal *s = (struct socal *)dev_id;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	cmd = s->regs->cmd;
+	for (; (cmd = SOCAL_INTR (s, cmd)); cmd = s->regs->cmd) {
+#ifdef SOCALDEBUG
+		static int cnt = 0;
+		if (cnt++ < 50) printk("soc_intr %08x\n", cmd);
+#endif	
+		if (cmd & SOCAL_CMD_RSP_Q2) socal_unsolicited (s, SOCAL_UNSOLICITED_RSP_Q);
+		if (cmd & SOCAL_CMD_RSP_Q1) socal_unsolicited (s, SOCAL_SOLICITED_BAD_RSP_Q);
+		if (cmd & SOCAL_CMD_RSP_Q0) socal_solicited (s, SOCAL_SOLICITED_RSP_Q);
+		if (cmd & SOCAL_CMD_REQ_QALL) socal_request (s, cmd);
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port))
+
+static int socal_hw_enque (fc_channel *fc, fcp_cmnd *fcmd)
+{
+	socal_port *port = (socal_port *)fc;
+	struct socal *s = port->s;
+	int qno;
+	socal_cq *sw_cq;
+	int cq_next_in;
+	socal_req *request;
+	fc_hdr *fch;
+	int i;
+
+	if (fcmd->proto == TYPE_SCSI_FCP)
+		qno = 1;
+	else
+		qno = 0;
+	SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno))
+	if (s->imask & (SOCAL_IMASK_REQ_Q0 << qno)) {
+		SOD(("EIO %08x\n", s->imask))
+		return -EIO;
+	}
+	sw_cq = s->req + qno;
+	cq_next_in = (sw_cq->in + 1) & sw_cq->last;
+	
+	if (cq_next_in == sw_cq->out 
+		    && cq_next_in == (sw_cq->out = s->regs->reqpr[qno])) {
+		SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last))
+		SOCAL_SETIMASK(s, s->imask | (SOCAL_IMASK_REQ_Q0 << qno));
+		SOD(("imask %08x %08x\n", s->imask, s->regs->imask));
+		/* If queue is full, just say NO */
+		return -EBUSY;
+	}
+	
+	request = sw_cq->pool + sw_cq->in;
+	fch = &request->fchdr;
+	
+	switch (fcmd->proto) {
+	case TYPE_SCSI_FCP:
+		request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token); 
+		request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd);
+		request->data[0].count = sizeof(fcp_cmd);
+		request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size;
+		request->data[1].count = fc->rsp_size;
+		if (fcmd->data) {
+			request->shdr.segcnt = 3;
+			i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len;
+			request->shdr.bytecnt = i;
+			request->data[2].base = fcmd->data;
+			request->data[2].count = i;
+			request->type = (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ?
+				SOCAL_CQTYPE_IO_WRITE : SOCAL_CQTYPE_IO_READ;
+		} else {
+			request->shdr.segcnt = 2;
+			request->shdr.bytecnt = 0;
+			request->data[2].base = 0;
+			request->data[2].count = 0;
+			request->type = SOCAL_CQTYPE_SIMPLE;
+		}
+		FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fcmd->did);
+		FILL_FCHDR_SID(fch, fc->sid);
+		FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+		fch->param = 0;
+		request->shdr.flags = port->flags;
+		request->shdr.class = fc->posmap ? 3 : 2;
+		break;
+		
+	case PROTO_OFFLINE:
+		memset (request, 0, sizeof(*request));
+		request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token); 
+		request->type = SOCAL_CQTYPE_OFFLINE;
+		FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fcmd->did);
+		FILL_FCHDR_SID(fch, fc->sid);
+		FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE);
+		FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0);
+		FILL_FCHDR_OXRX(fch, 0xffff, 0xffff);
+		request->shdr.flags = port->flags;
+		break;
+		
+	case PROTO_REPORT_AL_MAP:
+		memset (request, 0, sizeof(*request));
+		request->shdr.token = TOKEN(PROTO_REPORT_AL_MAP, port->mask, fcmd->token); 
+		request->type = SOCAL_CQTYPE_REPORT_MAP;
+		request->shdr.flags = port->flags;
+		request->shdr.segcnt = 1;
+		request->shdr.bytecnt = sizeof(fc_al_posmap);
+		request->data[0].base = fcmd->cmd;
+		request->data[0].count = sizeof(fc_al_posmap);
+		break;
+
+	default: 
+		request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token);
+		request->shdr.class = fc->posmap ? 3 : 2;
+		request->shdr.flags = port->flags;
+		memcpy (fch, &fcmd->fch, sizeof(fc_hdr));
+		request->data[0].count = fcmd->cmdlen;
+		request->data[1].count = fcmd->rsplen;
+		request->type = fcmd->class;
+		switch (fcmd->class) {
+		case FC_CLASS_OUTBOUND:
+			request->data[0].base = fcmd->cmd;
+			request->data[0].count = fcmd->cmdlen;
+			request->type = SOCAL_CQTYPE_OUTBOUND;
+			request->shdr.bytecnt = fcmd->cmdlen;
+			request->shdr.segcnt = 1;
+			break;
+		case FC_CLASS_INBOUND:
+			request->data[0].base = fcmd->rsp;
+			request->data[0].count = fcmd->rsplen;
+			request->type = SOCAL_CQTYPE_INBOUND;
+			request->shdr.bytecnt = 0;
+			request->shdr.segcnt = 1;
+			break;
+		case FC_CLASS_SIMPLE:
+			request->data[0].base = fcmd->cmd;
+			request->data[1].base = fcmd->rsp;
+			request->data[0].count = fcmd->cmdlen;
+			request->data[1].count = fcmd->rsplen;
+			request->type = SOCAL_CQTYPE_SIMPLE;
+			request->shdr.bytecnt = fcmd->cmdlen;
+			request->shdr.segcnt = 2;
+			break;
+		case FC_CLASS_IO_READ:
+		case FC_CLASS_IO_WRITE:
+			request->data[0].base = fcmd->cmd;
+			request->data[1].base = fcmd->rsp;
+			request->data[0].count = fcmd->cmdlen;
+			request->data[1].count = fcmd->rsplen;
+			request->type = (fcmd->class == FC_CLASS_IO_READ) ? SOCAL_CQTYPE_IO_READ : SOCAL_CQTYPE_IO_WRITE;
+			if (fcmd->data) {
+				request->data[2].base = fcmd->data;
+				request->data[2].count = fcmd->datalen;
+				request->shdr.bytecnt = fcmd->datalen;
+				request->shdr.segcnt = 3;
+			} else {
+				request->shdr.bytecnt = 0;
+				request->shdr.segcnt = 2;
+			}
+			break;
+		}
+		break;
+	}
+
+	request->count = 1;
+	request->flags = 0;
+	request->seqno = sw_cq->seqno;
+	
+	SOD(("queueing token %08x\n", request->shdr.token))
+	
+	/* And now tell the SOCAL about it */
+
+	if (++sw_cq->in > sw_cq->last) {
+		sw_cq->in = 0;
+		sw_cq->seqno++;
+	}
+	
+	SOD(("Putting %08x into cmd\n", SOCAL_CMD_RSP_QALL | (sw_cq->in << 24) | (SOCAL_CMD_REQ_Q0 << qno)))
+	
+	s->regs->cmd = SOCAL_CMD_RSP_QALL | (sw_cq->in << 24) | (SOCAL_CMD_REQ_Q0 << qno);
+	/* Read so that command is completed */	
+	s->regs->cmd;
+	
+	return 0;
+}
+
+static inline void socal_download_fw(struct socal *s)
+{
+#ifdef HAVE_SOCAL_UCODE
+	SOD(("Loading %ld bytes from %p to %p\n", sizeof(socal_ucode), socal_ucode, s->xram))
+	socal_memcpy (s->xram, socal_ucode, sizeof(socal_ucode));
+	SOD(("Clearing the rest of memory\n"))
+	socal_bzero (s->xram + sizeof(socal_ucode), 65536 - sizeof(socal_ucode));
+	SOD(("Done\n"))
+#endif
+}
+
+/* Check for what the best SBUS burst we can use happens
+ * to be on this machine.
+ */
+static inline void socal_init_bursts(struct socal *s, struct linux_sbus_device *sdev)
+{
+	int bsizes, bsizes_more;
+	u32 cfg;
+
+	bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff);
+	bsizes_more = (prom_getintdefault(sdev->my_bus->prom_node, "burst-sizes", 0xff) & 0xff);
+	bsizes &= bsizes_more;
+#ifdef USE_64BIT_MODE
+#ifdef __sparc_v9__
+	mmu_set_sbus64(sdev, bsizes >> 16);
+#endif
+#endif
+	if ((bsizes & 0x7f) == 0x7f)
+		cfg = SOCAL_CFG_BURST_64;
+	else if ((bsizes & 0x3f) == 0x3f) 
+		cfg = SOCAL_CFG_BURST_32;
+	else if ((bsizes & 0x1f) == 0x1f)
+		cfg = SOCAL_CFG_BURST_16;
+	else
+		cfg = SOCAL_CFG_BURST_4;
+#ifdef USE_64BIT_MODE
+#ifdef __sparc_v9__
+	/* What is BURST_128? -jj */
+	if ((bsizes & 0x780000) == 0x780000)
+		cfg |= (SOCAL_CFG_BURST_64 << 8) | SOCAL_CFG_SBUS_ENHANCED;
+	else if ((bsizes & 0x380000) == 0x380000) 
+		cfg |= (SOCAL_CFG_BURST_32 << 8) | SOCAL_CFG_SBUS_ENHANCED;
+	else if ((bsizes & 0x180000) == 0x180000)
+		cfg |= (SOCAL_CFG_BURST_16 << 8) | SOCAL_CFG_SBUS_ENHANCED;
+	else
+		cfg |= (SOCAL_CFG_BURST_8 << 8) | SOCAL_CFG_SBUS_ENHANCED;
+#endif
+#endif		
+	s->cfg = cfg;
+}
+
+static inline void socal_init(struct linux_sbus_device *sdev, int no)
+{
+	unsigned char tmp[60];
+	int propl;
+	struct socal *s;
+	static unsigned version_printed = 0;
+	socal_hw_cq cq[8];
+	int size, i;
+	int irq, node;
+	
+	s = kmalloc (sizeof (struct socal), GFP_KERNEL);
+	if (!s) return;
+	memset (s, 0, sizeof(struct socal));
+	s->socal_no = no;
+
+	SOD(("socals %08lx socal_intr %08lx socal_hw_enque %08lx\n", (long)socals, (long)socal_intr, (long)socal_hw_enque))
+	if (version_printed++ == 0)
+		printk (version);
+#ifdef MODULE
+	s->port[0].fc.module = &__this_module;
+	s->port[1].fc.module = &__this_module;
+#else
+	s->port[0].fc.module = NULL;
+	s->port[1].fc.module = NULL;
+#endif
+	                                	
+	s->next = socals;
+	socals = s;
+	s->port[0].fc.dev = sdev;
+	s->port[1].fc.dev = sdev;
+	s->port[0].s = s;
+	s->port[1].s = s;
+
+	s->port[0].fc.next = &s->port[1].fc;
+
+	/* World Wide Name of SOCAL */
+	propl = prom_getproperty (sdev->prom_node, "wwn", tmp, sizeof(tmp));
+	if (propl != sizeof (fc_wwn)) {
+		s->wwn.naaid = NAAID_IEEE_REG;
+		s->wwn.nportid = 0x123;
+		s->wwn.hi = 0x1234;
+		s->wwn.lo = 0x12345678;
+	} else
+		memcpy (&s->wwn, tmp, sizeof (fc_wwn));
+	
+	memcpy (&s->port[0].fc.wwn_nport, &s->wwn, sizeof (fc_wwn));
+	s->port[0].fc.wwn_nport.lo++;
+	memcpy (&s->port[1].fc.wwn_nport, &s->wwn, sizeof (fc_wwn));
+	s->port[1].fc.wwn_nport.lo+=2;
+	
+	node = prom_getchild (sdev->prom_node);
+	while (node && (node = prom_searchsiblings (node, "sf"))) {
+		int port;
+		
+		port = prom_getintdefault(node, "port#", -1);
+		switch (port) {
+		case 0:
+		case 1:
+			if (prom_getproplen(node, "port-wwn") == sizeof (fc_wwn))
+				prom_getproperty (node, "port-wwn", 
+						  (char *)&s->port[port].fc.wwn_nport,
+						  sizeof (fc_wwn));
+			break;
+		default:
+			break;
+		}
+		node = prom_getsibling(node);
+	}
+
+	memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
+	memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn));
+	SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n", 
+		*(u32 *)&s->port[0].fc.wwn_node, s->port[0].fc.wwn_node.lo,
+		*(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo,
+		*(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo))
+		
+	s->port[0].fc.sid = 1;
+	s->port[1].fc.sid = 17;
+	s->port[0].fc.did = 2;
+	s->port[1].fc.did = 18;
+	
+	s->port[0].fc.reset = socal_reset;
+	s->port[1].fc.reset = socal_reset;
+	
+	/* Setup the reg property for this device. */
+	prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev);
+                                                                                                                                               	
+	if (sdev->num_registers == 1) {
+		s->eeprom = (u8 *)
+			sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0, 
+				sdev->reg_addrs [0].reg_size, "socal_xram",
+				sdev->reg_addrs [0].which_io, 0);
+		if (sdev->reg_addrs [0].reg_size > 0x20000)
+			s->xram = s->eeprom + 0x10000;
+		else
+			s->xram = s->eeprom;
+		s->regs = (struct socal_regs *)(s->xram + 0x10000);
+	} else {
+		/* E.g. starfire presents 3 registers for SOCAL */
+		s->xram = (u8 *)
+			sparc_alloc_io (sdev->reg_addrs [1].phys_addr, 0, 
+				sdev->reg_addrs [1].reg_size, "socal_xram",
+				sdev->reg_addrs [1].which_io, 0);
+		s->regs = (struct socal_regs *)
+			sparc_alloc_io (sdev->reg_addrs [2].phys_addr, 0, 
+				sdev->reg_addrs [2].reg_size, "socal_regs",
+				sdev->reg_addrs [2].which_io, 0);
+	}
+	
+	socal_init_bursts(s, sdev);
+	
+	SOD(("Disabling SOCAL\n"))
+	
+	socal_disable (s);
+	
+	irq = sdev->irqs[0];
+
+	if (request_irq (irq, socal_intr, SA_SHIRQ, "SOCAL", (void *)s)) {
+		socal_printk ("Cannot order irq %d to go\n", irq);
+		socals = s->next;
+		return;
+	}
+
+	SOD(("SOCAL uses IRQ %s\n", __irq_itoa(irq)))
+	
+	s->port[0].fc.irq = irq;
+	s->port[1].fc.irq = irq;
+	
+	sprintf (s->port[0].fc.name, "socal%d port A", no);
+	sprintf (s->port[1].fc.name, "socal%d port B", no);
+	s->port[0].flags = SOCAL_FC_HDR | SOCAL_PORT_A;
+	s->port[1].flags = SOCAL_FC_HDR | SOCAL_PORT_B;
+	s->port[1].mask = (1 << 11);
+	
+	s->port[0].fc.hw_enque = socal_hw_enque;
+	s->port[1].fc.hw_enque = socal_hw_enque;
+	
+	socal_download_fw (s);
+	
+	SOD(("Downloaded firmware\n"))
+
+	/* Now setup xram circular queues */
+	memset (cq, 0, sizeof(cq));
+
+	size = (SOCAL_CQ_REQ0_SIZE + SOCAL_CQ_REQ1_SIZE + SOCAL_CQ_RSP0_SIZE + SOCAL_CQ_RSP1_SIZE + SOCAL_CQ_RSP2_SIZE) * sizeof(socal_req);
+	s->req[0].pool = (socal_req *) sparc_dvma_malloc (size, "SOCAL request queues", &cq[0].address);
+	s->req[1].pool = s->req[0].pool + SOCAL_CQ_REQ0_SIZE;
+	s->rsp[0].pool = s->req[1].pool + SOCAL_CQ_REQ1_SIZE;
+	s->rsp[1].pool = s->rsp[0].pool + SOCAL_CQ_RSP0_SIZE;
+	s->rsp[2].pool = s->rsp[1].pool + SOCAL_CQ_RSP1_SIZE;
+	
+	s->req[0].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_REQ_OFFSET);
+	s->req[1].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_REQ_OFFSET + sizeof(socal_hw_cq));
+	s->rsp[0].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_RSP_OFFSET);
+	s->rsp[1].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_RSP_OFFSET + sizeof(socal_hw_cq));
+	s->rsp[2].hw_cq = (socal_hw_cq *)(s->xram + SOCAL_CQ_RSP_OFFSET + 2 * sizeof(socal_hw_cq));
+	
+	cq[1].address = cq[0].address + (SOCAL_CQ_REQ0_SIZE * sizeof(socal_req));
+	cq[4].address = cq[1].address + (SOCAL_CQ_REQ1_SIZE * sizeof(socal_req));
+	cq[5].address = cq[4].address + (SOCAL_CQ_RSP0_SIZE * sizeof(socal_req));
+	cq[6].address = cq[5].address + (SOCAL_CQ_RSP1_SIZE * sizeof(socal_req));
+
+	cq[0].last = SOCAL_CQ_REQ0_SIZE - 1;
+	cq[1].last = SOCAL_CQ_REQ1_SIZE - 1;
+	cq[4].last = SOCAL_CQ_RSP0_SIZE - 1;
+	cq[5].last = SOCAL_CQ_RSP1_SIZE - 1;
+	cq[6].last = SOCAL_CQ_RSP2_SIZE - 1;
+	for (i = 0; i < 8; i++)
+		cq[i].seqno = 1;
+	
+	s->req[0].last = SOCAL_CQ_REQ0_SIZE - 1;
+	s->req[1].last = SOCAL_CQ_REQ1_SIZE - 1;
+	s->rsp[0].last = SOCAL_CQ_RSP0_SIZE - 1;
+	s->rsp[1].last = SOCAL_CQ_RSP1_SIZE - 1;
+	s->rsp[2].last = SOCAL_CQ_RSP2_SIZE - 1;
+	
+	s->req[0].seqno = 1;
+	s->req[1].seqno = 1;
+	s->rsp[0].seqno = 1;
+	s->rsp[1].seqno = 1;
+	s->rsp[2].seqno = 1;
+	
+	socal_memcpy (s->xram + SOCAL_CQ_REQ_OFFSET, cq, sizeof(cq));
+	
+	SOD(("Setting up params\n"))
+	
+	/* Make our sw copy of SOCAL service parameters */
+	socal_memcpy (s->serv_params, s->xram + 0x280, sizeof (s->serv_params));
+	
+	s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params;
+	s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
+	s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params;
+	s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20);
+	
+	socal_enable (s);
+	
+	SOD(("Enabled SOCAL\n"))
+}
+
+#ifndef MODULE
+__initfunc(int socal_probe(void))
+#else
+int init_module(void)
+#endif
+{
+	struct linux_sbus *bus;
+	struct linux_sbus_device *sdev = 0;
+	struct socal *s;
+	int cards = 0;
+
+	for_each_sbus(bus) {
+		for_each_sbusdev(sdev, bus) {
+			if(!strcmp(sdev->prom_name, "SUNW,socal")) {
+				socal_init(sdev, cards);
+				cards++;
+			}
+		}
+	}
+	if (!cards) return -EIO;
+
+	for_each_socal(s)
+		if (s->next)
+			s->port[1].fc.next = &s->next->port[0].fc;
+			
+	fcp_init (&socals->port[0].fc);
+	return 0;
+}
+
+EXPORT_NO_SYMBOLS;
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct socal *s;
+	int irq;
+	struct linux_sbus_device *sdev;
+	
+	for_each_socal(s) {
+		irq = s->port[0].fc.irq;
+		disable_irq (irq);
+		free_irq (irq, s);
+
+		fcp_release(&(s->port[0].fc), 2);
+
+		sdev = s->port[0].fc.dev;
+		if (sdev->num_registers == 1)
+			sparc_free_io (s->eeprom, sdev->reg_addrs [0].reg_size);
+		else {
+			sparc_free_io (s->xram, sdev->reg_addrs [1].reg_size);
+			sparc_free_io ((char *)s->regs, sdev->reg_addrs [2].reg_size);
+		}
+		/* FIXME: sparc_dvma_free() ??? */
+	}
+}
+#endif
diff -u --recursive --new-file v2.2.3/linux/drivers/fc4/socal.h linux/drivers/fc4/socal.h
--- v2.2.3/linux/drivers/fc4/socal.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/fc4/socal.h	Mon Mar 15 16:11:29 1999
@@ -0,0 +1,319 @@
+/* socal.h: Definitions for Sparc SUNW,socal (SOC+) Fibre Channel Sbus driver.
+ *
+ * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#ifndef __SOCAL_H
+#define __SOCAL_H
+
+#include "fc.h"
+#include "fcp.h"
+#include "fcp_impl.h"
+
+/* Hardware structures and constants first {{{ */
+
+union socal_rq_reg {
+	volatile u8 read[4];
+	volatile u32 write;
+};
+struct socal_regs {
+	volatile u32 cfg;		/* Config Register */
+	volatile u32 sae;		/* Slave Access Error Register */
+	volatile u32 cmd;		/* Command & Status Register */
+	volatile u32 imask;		/* Interrupt Mask Register */
+	union socal_rq_reg reqp;	/* Request Queue Index Register */
+	union socal_rq_reg resp;	/* Response Queue Index Register */
+#define reqpr reqp.read
+#define reqpw reqp.write
+#define respr resp.read
+#define respw resp.write
+};
+
+/* Config Register */
+#define SOCAL_CFG_EXT_RAM_BANK_MASK	0x07000000
+#define SOCAL_CFG_EEPROM_BANK_MASK	0x00030000
+#define SOCAL_CFG_BURST64_MASK		0x00000700
+#define SOCAL_CFG_SBUS_PARITY_TEST	0x00000020
+#define SOCAL_CFG_SBUS_PARITY_CHECK	0x00000010
+#define SOCAL_CFG_SBUS_ENHANCED		0x00000008
+#define SOCAL_CFG_BURST_MASK		0x00000007
+/* Bursts */
+#define SOCAL_CFG_BURST_4		0x00000000
+#define SOCAL_CFG_BURST_8		0x00000003
+#define SOCAL_CFG_BURST_16		0x00000004
+#define SOCAL_CFG_BURST_32		0x00000005
+#define SOCAL_CFG_BURST_64		0x00000006
+#define SOCAL_CFG_BURST_128		0x00000007
+
+/* Slave Access Error Register */
+#define SOCAL_SAE_ALIGNMENT		0x00000004
+#define SOCAL_SAE_UNSUPPORTED		0x00000002
+#define SOCAL_SAE_PARITY		0x00000001
+
+/* Command & Status Register */
+#define SOCAL_CMD_RSP_QALL		0x000f0000
+#define SOCAL_CMD_RSP_Q0		0x00010000
+#define SOCAL_CMD_RSP_Q1		0x00020000
+#define SOCAL_CMD_RSP_Q2		0x00040000
+#define SOCAL_CMD_RSP_Q3		0x00080000
+#define SOCAL_CMD_REQ_QALL		0x00000f00
+#define SOCAL_CMD_REQ_Q0		0x00000100
+#define SOCAL_CMD_REQ_Q1		0x00000200
+#define SOCAL_CMD_REQ_Q2		0x00000400
+#define SOCAL_CMD_REQ_Q3		0x00000800
+#define SOCAL_CMD_SAE			0x00000080
+#define SOCAL_CMD_INTR_PENDING		0x00000008
+#define SOCAL_CMD_NON_QUEUED		0x00000004
+#define SOCAL_CMD_IDLE			0x00000002
+#define SOCAL_CMD_SOFT_RESET		0x00000001
+
+/* Interrupt Mask Register */
+#define SOCAL_IMASK_RSP_QALL		0x000f0000
+#define SOCAL_IMASK_RSP_Q0		0x00010000
+#define SOCAL_IMASK_RSP_Q1		0x00020000
+#define SOCAL_IMASK_RSP_Q2		0x00040000
+#define SOCAL_IMASK_RSP_Q3		0x00080000
+#define SOCAL_IMASK_REQ_QALL		0x00000f00
+#define SOCAL_IMASK_REQ_Q0		0x00000100
+#define SOCAL_IMASK_REQ_Q1		0x00000200
+#define SOCAL_IMASK_REQ_Q2		0x00000400
+#define SOCAL_IMASK_REQ_Q3		0x00000800
+#define SOCAL_IMASK_SAE			0x00000080
+#define SOCAL_IMASK_NON_QUEUED		0x00000004
+
+#define SOCAL_INTR(s, cmd) \
+	(((cmd & SOCAL_CMD_RSP_QALL) | ((~cmd) & SOCAL_CMD_REQ_QALL)) \
+	 & s->imask)
+	 
+#define SOCAL_SETIMASK(s, i) \
+	(s)->imask = (i); (s)->regs->imask = (i)
+	
+#define SOCAL_MAX_EXCHANGES		1024
+
+/* XRAM
+ *
+ * This is a 64KB register area.
+ * From the documentation, it seems like it is finally able to cope
+ * at least with 1,2,4 byte accesses for read and 2,4 byte accesses for write.
+ */
+ 
+/* Circular Queue */
+
+#define SOCAL_CQ_REQ_OFFSET	0x200
+#define SOCAL_CQ_RSP_OFFSET	0x220
+
+typedef struct {
+	u32			address;
+	u8			in;
+	u8			out;
+	u8			last;
+	u8			seqno;
+} socal_hw_cq;
+
+#define SOCAL_PORT_A	0x0000	/* From/To Port A */
+#define SOCAL_PORT_B	0x0001	/* From/To Port A */
+#define SOCAL_FC_HDR	0x0002  /* Contains FC Header */
+#define SOCAL_NORSP	0x0004  /* Don't generate response nor interrupt */
+#define SOCAL_NOINT	0x0008  /* Generate response but not interrupt */
+#define SOCAL_XFERRDY	0x0010  /* Generate XFERRDY */
+#define SOCAL_IGNOREPARAM 0x0020 /* Ignore PARAM field in the FC header */
+#define SOCAL_COMPLETE	0x0040  /* Command completed */
+#define SOCAL_UNSOLICITED 0x0080 /* For request this is the packet to establish unsolicited pools, */
+				/* for rsp this is unsolicited packet */
+#define SOCAL_STATUS	0x0100	/* State change (on/off line) */
+#define SOCAL_RSP_HDR	0x0200	/* Return frame header in any case */
+
+typedef struct {
+	u32			token;
+	u16			flags;
+	u8			class;
+	u8			segcnt;
+	u32			bytecnt;
+} socal_hdr;
+
+typedef struct {
+	u32			base;
+	u32			count;
+} socal_data;
+
+#define SOCAL_CQTYPE_NOP	0x00
+#define SOCAL_CQTYPE_OUTBOUND	0x01
+#define SOCAL_CQTYPE_INBOUND	0x02
+#define SOCAL_CQTYPE_SIMPLE	0x03
+#define SOCAL_CQTYPE_IO_WRITE	0x04
+#define SOCAL_CQTYPE_IO_READ	0x05
+#define SOCAL_CQTYPE_UNSOLICITED 0x06
+#define SOCAL_CQTYPE_BYPASS_DEV	0x06
+#define SOCAL_CQTYPE_DIAG	0x07
+#define SOCAL_CQTYPE_OFFLINE	0x08
+#define SOCAL_CQTYPE_ADD_POOL	0x09
+#define SOCAL_CQTYPE_DELETE_POOL 0x0a
+#define SOCAL_CQTYPE_ADD_BUFFER	0x0b
+#define SOCAL_CQTYPE_ADD_POOL_BUFFER 0x0c
+#define SOCAL_CQTYPE_REQUEST_ABORT 0x0d
+#define SOCAL_CQTYPE_REQUEST_LIP 0x0e
+#define SOCAL_CQTYPE_REPORT_MAP	0x0f
+#define SOCAL_CQTYPE_RESPONSE	0x10
+#define SOCAL_CQTYPE_INLINE	0x20
+
+#define SOCAL_CQFLAGS_CONT	0x01
+#define SOCAL_CQFLAGS_FULL	0x02
+#define SOCAL_CQFLAGS_BADHDR	0x04
+#define SOCAL_CQFLAGS_BADPKT	0x08
+
+typedef struct {
+	socal_hdr		shdr;
+	socal_data		data[3];
+	fc_hdr			fchdr;
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} socal_req;
+
+#define SOCAL_OK		0
+#define SOCAL_P_RJT		2
+#define SOCAL_F_RJT		3
+#define SOCAL_P_BSY		4
+#define SOCAL_F_BSY		5
+#define SOCAL_ONLINE		0x10
+#define SOCAL_OFFLINE		0x11
+#define SOCAL_TIMEOUT		0x12
+#define SOCAL_OVERRUN		0x13
+#define SOCAL_ONLINE_LOOP	0x14
+#define SOCAL_OLD_PORT		0x15
+#define SOCAL_AL_PORT		0x16
+#define SOCAL_UNKOWN_CQ_TYPE	0x20
+#define SOCAL_BAD_SEG_CNT	0x21
+#define SOCAL_MAX_XCHG_EXCEEDED	0x22
+#define SOCAL_BAD_XID		0x23
+#define SOCAL_XCHG_BUSY		0x24
+#define SOCAL_BAD_POOL_ID	0x25
+#define SOCAL_INSUFFICIENT_CQES	0x26
+#define SOCAL_ALLOC_FAIL	0x27
+#define SOCAL_BAD_SID		0x28
+#define SOCAL_NO_SEG_INIT	0x29
+#define SOCAL_BAD_DID		0x2a
+#define SOCAL_ABORTED		0x30
+#define SOCAL_ABORT_FAILED	0x31
+
+typedef struct {
+	socal_hdr		shdr;
+	u32			status;
+	socal_data		data;
+	u8			xxx1[10];
+	u16			ncmds;
+	fc_hdr			fchdr;
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} socal_rsp;
+
+typedef struct {
+	socal_hdr		shdr;
+	u8			xxx1[48];
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} socal_cmdonly;
+
+#define SOCAL_DIAG_NOP		0x00
+#define SOCAL_DIAG_INT_LOOP	0x01
+#define SOCAL_DIAG_EXT_LOOP	0x02
+#define SOCAL_DIAG_REM_LOOP	0x03
+#define SOCAL_DIAG_XRAM_TEST	0x04
+#define SOCAL_DIAG_SOC_TEST	0x05
+#define SOCAL_DIAG_HCB_TEST	0x06
+#define SOCAL_DIAG_SOCLB_TEST	0x07
+#define SOCAL_DIAG_SRDSLB_TEST	0x08
+#define SOCAL_DIAG_EXTOE_TEST	0x09
+
+typedef struct {
+	socal_hdr		shdr;
+	u32			cmd;
+	u8			xxx1[44];
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} socal_diag_req;
+
+#define SOCAL_POOL_MASK_RCTL	0x800000
+#define SOCAL_POOL_MASK_DID	0x700000
+#define SOCAL_POOL_MASK_SID	0x070000
+#define SOCAL_POOL_MASK_TYPE	0x008000
+#define SOCAL_POOL_MASK_F_CTL	0x007000
+#define SOCAL_POOL_MASK_SEQ_ID	0x000800
+#define SOCAL_POOL_MASK_D_CTL	0x000400
+#define SOCAL_POOL_MASK_SEQ_CNT	0x000300
+#define SOCAL_POOL_MASK_OX_ID	0x0000f0
+#define SOCAL_POOL_MASK_PARAM	0x00000f
+
+typedef struct {
+	socal_hdr		shdr;
+	u32			pool_id;
+	u32			header_mask;
+	u32			buf_size;
+	u32			entries;
+	u8			xxx1[8];
+	fc_hdr			fchdr;
+	u8			count;
+	u8			type;
+	u8			flags;
+	u8			seqno;
+} socal_pool_req;
+
+/* }}} */
+
+/* Now our software structures and constants we use to drive the beast {{{ */
+
+#define SOCAL_CQ_REQ0_SIZE	4
+#define SOCAL_CQ_REQ1_SIZE	256
+#define SOCAL_CQ_RSP0_SIZE	8
+#define SOCAL_CQ_RSP1_SIZE	4
+#define SOCAL_CQ_RSP2_SIZE	4
+
+#define SOCAL_SOLICITED_RSP_Q	0
+#define SOCAL_SOLICITED_BAD_RSP_Q 1
+#define SOCAL_UNSOLICITED_RSP_Q	2
+
+struct socal;
+
+typedef struct {
+	/* This must come first */
+	fc_channel		fc;
+	struct socal		*s;
+	u16			flags;
+	u16			mask;
+} socal_port; 
+
+typedef struct {
+	socal_hw_cq		*hw_cq;	/* Related XRAM cq */
+	socal_req		*pool;
+	u8			in;
+	u8			out;
+	u8			last;
+	u8			seqno;
+} socal_cq;
+
+struct socal {
+	socal_port		port[2]; /* Every SOCAL has one or two FC ports */
+	socal_cq		req[4]; /* Request CQs */
+	socal_cq		rsp[4]; /* Response CQs */
+	int			socal_no;
+	struct socal_regs	*regs;
+	u8			*xram;
+	u8			*eeprom;
+	fc_wwn			wwn;
+	u32			imask;	/* Our copy of regs->imask */
+	u32			cfg;	/* Our copy of regs->cfg */
+	char			serv_params[80];
+	struct socal		*next;
+	int			curr_port; /* Which port will have priority to fcp_queue_empty */
+};
+
+/* }}} */
+
+#endif /* !(__SOCAL_H) */
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.2.3/linux/drivers/isdn/Config.in	Thu Dec 31 10:29:00 1998
+++ linux/drivers/isdn/Config.in	Mon Mar 15 16:11:29 1999
@@ -39,7 +39,8 @@
     bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
     if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
       if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
-        bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930
+        bool 'HiSax Support for SPARC Am7930' CONFIG_HISAX_AMD7930
+        bool 'HiSax Support for SPARC DBRI' CONFIG_HISAX_DBRI
       fi
     fi
 fi
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.2.3/linux/drivers/isdn/hisax/Makefile	Wed Apr  1 20:11:50 1998
+++ linux/drivers/isdn/hisax/Makefile	Mon Mar 15 16:11:29 1999
@@ -108,8 +108,10 @@
 	 HFC_2BDS0 := hfc_2bds0.o
 endif
 ifeq ($(CONFIG_HISAX_AMD7930),y)
-	 O_OBJS += amd7930.o
-	 RAWHDLC_OBJ := rawhdlc.o
+	 RAWHDLC_OBJ := foreign.o rawhdlc.o
+endif
+ifeq ($(CONFIG_HISAX_DBRI),y)
+	 RAWHDLC_OBJ := foreign.o rawhdlc.o
 endif
 
 ifeq ($(CONFIG_HISAX_NICCY),y)
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.2.3/linux/drivers/isdn/hisax/config.c	Thu Jan  7 15:11:37 1999
+++ linux/drivers/isdn/hisax/config.c	Mon Mar 15 16:11:30 1999
@@ -193,6 +193,13 @@
 #define DEFAULT_CFG {12,0x3e0,0,0}
 #endif
 
+#ifdef CONFIG_HISAX_DBRI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_DBRI
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
 #ifdef CONFIG_HISAX_NICCY
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
@@ -486,6 +493,7 @@
 			case ISDN_CTYPE_ELSA_PCI:
 			case ISDN_CTYPE_NETJET:
 			case ISDN_CTYPE_AMD7930:
+			case ISDN_CTYPE_DBRI:
 				break;
 		}
 	}
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/foreign.c linux/drivers/isdn/hisax/foreign.c
--- v2.2.3/linux/drivers/isdn/hisax/foreign.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/foreign.c	Mon Mar 15 16:11:30 1999
@@ -0,0 +1,780 @@
+/* $Id: foreign.c,v 1.1 1998/11/09 07:48:48 baccala Exp $
+ *
+ * HiSax ISDN driver - foreign chipset interface
+ *
+ * Author       Brent Baccala (baccala@FreeSoft.org)
+ *
+ *
+ *
+ * $Log: foreign.c,v $
+ * Revision 1.1  1998/11/09 07:48:48  baccala
+ * Initial DBRI ISDN code.  Sometimes works (brings up the link and you
+ * can telnet through it), sometimes doesn't (crashes the machine)
+ *
+ * Revision 1.2  1998/02/12 23:07:10  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.1  1998/02/03 23:20:51  keil
+ * New files for SPARC isdn support
+ *
+ * Revision 1.1  1998/01/08 04:17:12  baccala
+ * ISDN comes to the Sparc.  Key points:
+ *
+ *    - Existing ISDN HiSax driver provides all the smarts
+ *    - it compiles, runs, talks to an isolated phone switch, connects
+ *      to a Cisco, pings go through
+ *    - AMD 7930 support only (no DBRI yet)
+ *    - no US NI-1 support (may not work on US phone system - untested)
+ *    - periodic packet loss, apparently due to lost interrupts
+ *    - ISDN sometimes freezes, requiring reboot before it will work again
+ *
+ * The code is unreliable enough to be consider alpha
+ *
+ *
+ * 
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include "foreign.h"
+#include "rawhdlc.h"
+#include <linux/interrupt.h>
+
+static const char *foreign_revision = "$Revision: 1.1 $";
+
+#define RCV_BUFSIZE	1024	/* Size of raw receive buffer in bytes */
+#define RCV_BUFBLKS	4	/* Number of blocks to divide buffer into
+				 * (must divide RCV_BUFSIZE) */
+
+static void Bchan_fill_fifo(struct BCState *, struct sk_buff *);
+
+static void
+Bchan_xmt_bh(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	if (bcs->hw.foreign.tx_skb != NULL) {
+		dev_kfree_skb(bcs->hw.foreign.tx_skb);
+		bcs->hw.foreign.tx_skb = NULL;
+	}
+
+	if ((skb = skb_dequeue(&bcs->squeue))) {
+		Bchan_fill_fifo(bcs, skb);
+	} else {
+		clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		bcs->event |= 1 << B_XMTBUFREADY;
+		queue_task(&bcs->tqueue, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+static void
+Bchan_xmit_callback(struct BCState *bcs)
+{
+	queue_task(&bcs->hw.foreign.tq_xmt, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/* B channel transmission: two modes (three, if you count L1_MODE_NULL)
+ *
+ * L1_MODE_HDLC - We need to do HDLC encapsulation before transmiting
+ * the packet (i.e. make_raw_hdlc_data).  Since this can be a
+ * time-consuming operation, our completion callback just schedules
+ * a bottom half to do encapsulation for the next packet.  In between,
+ * the link will just idle
+ *
+ * L1_MODE_TRANS - Data goes through, well, transparent.  No HDLC encap,
+ * and we can't just let the link idle, so the "bottom half" actually
+ * gets called during the top half (it's our callback routine in this case),
+ * but it's a lot faster now since we don't call make_raw_hdlc_data
+ */
+
+static void
+Bchan_fill_fifo(struct BCState *bcs, struct sk_buff *skb)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct foreign_hw *hw = &bcs->hw.foreign;
+	int len;
+
+	if ((cs->debug & L1_DEB_HSCX) || (cs->debug & L1_DEB_HSCX_FIFO)) {
+		char tmp[2048];
+		char *t = tmp;
+
+		t += sprintf(t, "Bchan_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', skb->len);
+		if (cs->debug & L1_DEB_HSCX_FIFO)
+			QuickHex(t, skb->data, skb->len);
+		debugl1(cs, tmp);
+	}
+
+	if (hw->doHDLCprocessing) {
+		len = make_raw_hdlc_data(skb->data, skb->len,
+					 bcs->hw.foreign.tx_buff, RAW_BUFMAX);
+		if (len > 0)
+			cs->hw.foreign->bxmit(0, bcs->channel,
+                                              bcs->hw.foreign.tx_buff, len,
+                                              (void *) &Bchan_xmit_callback,
+                                              (void *) bcs);
+		dev_kfree_skb(skb);
+	} else {
+		cs->hw.foreign->bxmit(0, bcs->channel,
+                                      skb->data, skb->len,
+                                      (void *) &Bchan_xmit_callback,
+                                      (void *) bcs);
+		bcs->hw.foreign.tx_skb = skb;
+	}
+}
+
+static void
+Bchan_mode(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "foreign mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+		debugl1(cs, tmp);
+	}
+	bcs->mode = mode;
+}
+
+/* Bchan_l2l1 is the entry point for upper layer routines that want to
+ * transmit on the B channel.  PH_DATA_REQ is a normal packet that
+ * we either start transmitting (if idle) or queue (if busy).
+ * PH_PULL_REQ can be called to request a callback message (PH_PULL_CNF)
+ * once the link is idle.  After a "pull" callback, the upper layer
+ * routines can use PH_PULL_IND to send data.
+ */
+
+static void
+Bchan_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				Bchan_fill_fifo(st->l1.bcs, skb);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {
+				printk(KERN_WARNING "foreign: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			Bchan_fill_fifo(st->l1.bcs, skb);
+			break;
+		case (PH_PULL_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {
+				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+/*
+****************************************************************************
+***************** Receiver callback and bottom half ************************
+****************************************************************************
+*/
+
+/* Bchan_recv_done() is called when a frame has been completely decoded
+ * into hw->rv_skb and we're ready to hand it off to the HiSax upper
+ * layer.  If a "large" packet is received, stick rv_skb on the
+ * receive queue and alloc a new (large) skb to act as buffer for
+ * future receives.  If a small packet is received, leave rv_skb
+ * alone, alloc a new skb of the correct size, and copy the packet
+ * into it.  In any case, flag the channel as B_RCVBUFREADY and
+ * queue the upper layer's task.
+ */
+
+static void
+Bchan_recv_done(struct BCState *bcs, unsigned int len)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct foreign_hw *hw = &bcs->hw.foreign;
+	struct sk_buff *skb;
+
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[2048];
+		char *t = tmp;
+
+		t += sprintf(t, "Bchan_rcv %c cnt %d (%x)", bcs->channel ? 'B' : 'A', len, hw->rv_skb->tail);
+		QuickHex(t, hw->rv_skb->tail, len);
+		debugl1(cs, tmp);
+	}
+
+	if (len > HSCX_BUFMAX/2) {
+		/* Large packet received */
+
+		if (!(skb = dev_alloc_skb(HSCX_BUFMAX))) {
+			printk(KERN_WARNING "foreign: receive out of memory\n");
+		} else {
+			skb_put(hw->rv_skb, len);
+			skb_queue_tail(&bcs->rqueue, hw->rv_skb);
+			hw->rv_skb = skb;
+
+			bcs->event |= 1 << B_RCVBUFREADY;
+			queue_task(&bcs->tqueue, &tq_immediate);
+			mark_bh(IMMEDIATE_BH);
+		}
+	} else {
+		/* Small packet received */
+
+		if (!(skb = dev_alloc_skb(len))) {
+			printk(KERN_WARNING "foreign: receive out of memory\n");
+		} else {
+			memcpy(skb_put(skb, len), hw->rv_skb->tail, len);
+			skb_queue_tail(&bcs->rqueue, skb);
+
+			bcs->event |= 1 << B_RCVBUFREADY;
+			queue_task(&bcs->tqueue, &tq_immediate);
+			mark_bh(IMMEDIATE_BH);
+		}
+	}
+}
+
+/* Bchan_recv_callback()'s behavior depends on whether we're doing local
+ * HDLC processing.  If so, receive into hw->rv_buff and queue Bchan_rcv_bh
+ * to decode the HDLC at leisure.  Otherwise, receive directly into hw->rv_skb
+ * and call Bchan_recv_done().  In either case, prepare a new buffer for
+ * further receives and hand it to the hardware driver.
+ */
+
+static void
+Bchan_recv_callback(struct BCState *bcs, int error, unsigned int len)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct foreign_hw *hw = &bcs->hw.foreign;
+
+	if (hw->doHDLCprocessing) {
+
+		hw->rv_buff_in += RCV_BUFSIZE/RCV_BUFBLKS;
+		hw->rv_buff_in %= RCV_BUFSIZE;
+
+		if (hw->rv_buff_in != hw->rv_buff_out) {
+			cs->hw.foreign->brecv(0, bcs->channel,
+					      hw->rv_buff + hw->rv_buff_in,
+					      RCV_BUFSIZE/RCV_BUFBLKS,
+					      (void *) &Bchan_recv_callback,
+					      (void *) bcs);
+
+		}
+		queue_task(&hw->tq_rcv, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+
+	} else {
+		if (error) {
+			char tmp[256];
+			sprintf(tmp, "B channel %c receive error %x",
+				bcs->channel ? 'B' : 'A', error);
+			debugl1(cs, tmp);
+		} else {
+			Bchan_recv_done(bcs, len);
+		}
+		cs->hw.foreign->brecv(0, bcs->channel,
+				      hw->rv_skb->tail, HSCX_BUFMAX,
+				      (void *) &Bchan_recv_callback,
+				      (void *) bcs);
+
+	}
+}
+
+/* Bchan_rcv_bh() is a "shim" bottom half handler stuck in between
+ * Bchan_recv_callback() and the HiSax upper layer if we need to
+ * do local HDLC processing.
+ */
+
+static void
+Bchan_rcv_bh(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct foreign_hw *hw = &bcs->hw.foreign;
+	struct sk_buff *skb;
+	int len;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[1024];
+
+		sprintf(tmp, "foreign_Bchan_rcv (%d/%d)",
+			hw->rv_buff_in, hw->rv_buff_out);
+		debugl1(cs, tmp);
+	}
+
+	do {
+		if (cs->debug & L1_DEB_HSCX) {
+			char tmp[1024];
+
+			QuickHex(tmp, hw->rv_buff + hw->rv_buff_out,
+				 RCV_BUFSIZE/RCV_BUFBLKS);
+			debugl1(cs, tmp);
+		}
+
+		while ((len = read_raw_hdlc_data(hw->hdlc_state,
+						 hw->rv_buff + hw->rv_buff_out, RCV_BUFSIZE/RCV_BUFBLKS,
+						 hw->rv_skb->tail, HSCX_BUFMAX))) {
+			if (len > 0) {
+				Bchan_recv_done(bcs, len);
+			} else {
+				char tmp[256];
+				sprintf(tmp, "B channel %c receive error",
+					bcs->channel ? 'B' : 'A');
+				debugl1(cs, tmp);
+			}
+		}
+
+		if (hw->rv_buff_in == hw->rv_buff_out) {
+			/* Buffer was filled up - need to restart receiver */
+			cs->hw.foreign->brecv(0, bcs->channel,
+                                              hw->rv_buff + hw->rv_buff_in,
+                                              RCV_BUFSIZE/RCV_BUFBLKS,
+                                              (void *) &Bchan_recv_callback,
+                                              (void *) bcs);
+		}
+
+		hw->rv_buff_out += RCV_BUFSIZE/RCV_BUFBLKS;
+		hw->rv_buff_out %= RCV_BUFSIZE;
+
+	} while (hw->rv_buff_in != hw->rv_buff_out);
+}
+
+static void
+Bchan_close(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct sk_buff *skb;
+
+	Bchan_mode(bcs, 0, 0);
+	cs->hw.foreign->bclose(0, bcs->channel);
+
+	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+	}
+	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+Bchan_open(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct foreign_hw *hw = &bcs->hw.foreign;
+
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+
+	hw->doHDLCprocessing = 0;
+	if (bcs->mode == L1_MODE_HDLC) {
+		if (cs->hw.foreign->bopen(0, bcs->channel, 1, 0xff) == -1) {
+			if (cs->hw.foreign->bopen(0, bcs->channel, 0, 0xff) == -1) {
+				return (-1);
+			}
+			hw->doHDLCprocessing = 1;
+		}
+	} else {
+		if (cs->hw.foreign->bopen(0, bcs->channel, 0, 0xff) == -1) {
+			return (-1);
+		}
+	}
+
+	hw->rv_buff_in = 0;
+	hw->rv_buff_out = 0;
+	hw->tx_skb = NULL;
+	init_hdlc_state(hw->hdlc_state, 0);
+	cs->hw.foreign->brecv(0, bcs->channel,
+                              hw->rv_buff + hw->rv_buff_in,
+                              RCV_BUFSIZE/RCV_BUFBLKS,
+                              (void *) &Bchan_recv_callback, (void *) bcs);
+
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static void
+Bchan_init(struct BCState *bcs)
+{
+	if (!(bcs->hw.foreign.tx_buff = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for foreign.tx_buff\n");
+		return;
+	}
+	if (!(bcs->hw.foreign.rv_buff = kmalloc(RCV_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for foreign.rv_buff\n");
+		return;
+	}
+	if (!(bcs->hw.foreign.rv_skb = dev_alloc_skb(HSCX_BUFMAX))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for foreign.rv_skb\n");
+		return;
+	}
+	if (!(bcs->hw.foreign.hdlc_state = kmalloc(sizeof(struct hdlc_state),
+						   GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for foreign.hdlc_state\n");
+		return;
+	}
+
+	bcs->hw.foreign.tq_rcv.sync = 0;
+	bcs->hw.foreign.tq_rcv.routine = (void (*)(void *)) &Bchan_rcv_bh;
+	bcs->hw.foreign.tq_rcv.data = (void *) bcs;
+
+	bcs->hw.foreign.tq_xmt.sync = 0;
+	bcs->hw.foreign.tq_xmt.routine = (void (*)(void *)) &Bchan_xmt_bh;
+	bcs->hw.foreign.tq_xmt.data = (void *) bcs;
+}
+
+static void
+Bchan_manl1(struct PStack *st, int pr,
+	  void *arg)
+{
+	switch (pr) {
+		case (PH_ACTIVATE_REQ):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			Bchan_mode(st->l1.bcs, st->l1.mode, st->l1.bc);
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			break;
+		case (PH_DEACTIVATE_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
+				Bchan_mode(st->l1.bcs, 0, 0);
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			break;
+	}
+}
+
+int
+setstack_foreign(struct PStack *st, struct BCState *bcs)
+{
+	if (Bchan_open(bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = Bchan_l2l1;
+	st->ma.manl1 = Bchan_manl1;
+	setstack_manager(st);
+	bcs->st = st;
+	return (0);
+}
+
+
+static void
+foreign_drecv_callback(void *arg, int error, unsigned int count)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) arg;
+	static struct tq_struct task = {0, 0, (void *) &DChannel_proc_rcv, 0};
+	struct sk_buff *skb;
+
+        /* NOTE: This function is called directly from an interrupt handler */
+
+	if (1) {
+		if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+			printk(KERN_WARNING "HiSax: D receive out of memory\n");
+		else {
+			memcpy(skb_put(skb, count), cs->rcvbuf, count);
+			skb_queue_tail(&cs->rq, skb);
+		}
+
+		task.data = (void *) cs;
+		queue_task(&task, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "foreign Drecv cnt %d", count);
+		if (error) t += sprintf(t, " ERR %x", error);
+		QuickHex(t, cs->rcvbuf, count);
+		debugl1(cs, tmp);
+	}
+
+	cs->hw.foreign->drecv(0, cs->rcvbuf, MAX_DFRAME_LEN,
+		      &foreign_drecv_callback, cs);
+}
+
+static void
+foreign_dxmit_callback(void *arg, int error)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) arg;
+	static struct tq_struct task = {0, 0, (void *) &DChannel_proc_xmt, 0};
+
+        /* NOTE: This function is called directly from an interrupt handler */
+
+	/* may wish to do retransmission here, if error indicates collision */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "foreign Dxmit cnt %d", cs->tx_skb->len);
+		if (error) t += sprintf(t, " ERR %x", error);
+		QuickHex(t, cs->tx_skb->data, cs->tx_skb->len);
+		debugl1(cs, tmp);
+	}
+
+	cs->tx_skb = NULL;
+
+	task.data = (void *) cs;
+	queue_task(&task, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+foreign_Dchan_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	char str[64];
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((cs->dlogflag) && (!(skb->data[2] & 1))) {
+					/* I-FRAME */
+					LogFrame(cs, skb->data, skb->len);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(cs, skb->data+4, skb->len-4,
+						  str);
+				}
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				cs->hw.foreign->dxmit(0, skb->data, skb->len,
+                                                      &foreign_dxmit_callback, cs);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+				LogFrame(cs, skb->data, skb->len);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(cs, skb->data + 4, skb->len - 4,
+					  str);
+			}
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			cs->hw.foreign->dxmit(0, cs->tx_skb->data, cs->tx_skb->len,
+                                              &foreign_dxmit_callback, cs);
+			break;
+		case (PH_PULL_REQ):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+int
+setDstack_foreign(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l2.l2l1 = foreign_Dchan_l2l1;
+	if (! cs->rcvbuf) {
+		printk("setDstack_foreign: No cs->rcvbuf!\n");
+	} else {
+		cs->hw.foreign->drecv(0, cs->rcvbuf, MAX_DFRAME_LEN,
+                                      &foreign_drecv_callback, cs);
+	}
+	return (0);
+}
+
+static void
+manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		st->ma.manl1(st, msg, arg);
+		st = st->next;
+	}
+}
+
+void
+foreign_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
+{
+	u_char val;
+	char tmp[32];
+	
+	if (cs->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "foreign_l1cmd msg %x", msg);
+		debugl1(cs, tmp);
+	}
+
+	switch(msg) {
+		case PH_RESET_REQ:
+			cs->hw.foreign->liu_deactivate(0);
+			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+			break;
+		case PH_ENABLE_REQ:
+			break;
+		case PH_INFO3_REQ:
+			cs->hw.foreign->liu_activate(0,0);
+			break;
+		case PH_TESTLOOP_REQ:
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN) {
+				sprintf(tmp, "foreign_l1cmd unknown %4x", msg);
+				debugl1(cs, tmp);
+			}
+			break;
+	}
+}
+
+static void
+foreign_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->hw.foreign->get_liu_state(0)) {
+	        case 3:
+			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+                        break;
+
+	        case 7:
+			manl1_msg(cs, PH_I4_P8_IND, NULL);
+			break;
+
+	        case 8:
+			manl1_msg(cs, PH_RSYNC_IND, NULL);
+			break;
+	}
+}
+
+/* LIU state change callback */
+
+static void
+foreign_liu_callback(struct IsdnCardState *cs)
+{
+	static struct tq_struct task = {0, 0, (void *) &foreign_new_ph, 0};
+
+	if (!cs)
+		return;
+
+	if (cs->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "foreign LIU state %d",
+                        cs->hw.foreign->get_liu_state(0));
+		debugl1(cs, tmp);
+	}
+
+	task.data = (void *) cs;
+	queue_task(&task, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void init_foreign(struct IsdnCardState *cs)
+{
+	Bchan_init(&cs->bcs[0]);
+	Bchan_init(&cs->bcs[1]);
+	cs->bcs[0].BC_SetStack = setstack_foreign;
+	cs->bcs[1].BC_SetStack = setstack_foreign;
+	cs->bcs[0].BC_Close = Bchan_close;
+	cs->bcs[1].BC_Close = Bchan_close;
+	Bchan_mode(cs->bcs, 0, 0);
+	Bchan_mode(cs->bcs + 1, 0, 0);
+}
+
+static int
+foreign_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			return(0);
+		case CARD_SETIRQ:
+			return(0);
+		case CARD_INIT:
+			cs->l1cmd = foreign_l1cmd;
+                        cs->setstack_d = setDstack_foreign;
+			cs->hw.foreign->liu_init(0, &foreign_liu_callback,
+                                                (void *)cs);
+			init_foreign(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#if CARD_AMD7930
+extern struct foreign_interface amd7930_foreign_interface;
+#endif
+
+#if CARD_DBRI
+extern struct foreign_interface dbri_foreign_interface;
+#endif
+
+__initfunc(int
+setup_foreign(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, foreign_revision);
+	printk(KERN_INFO "HiSax: Foreign chip driver Rev. %s\n",
+               HiSax_getrev(tmp));
+
+#if CARD_AMD7930
+	if (cs->typ == ISDN_CTYPE_AMD7930) {
+                cs->hw.foreign = &amd7930_foreign_interface;
+                cs->irq = cs->hw.foreign->get_irqnum(0);
+                if (cs->irq == 0)
+                        return (0);
+                cs->cardmsg = &foreign_card_msg;
+                return (1);
+        }
+#endif
+
+#if CARD_DBRI
+	if (cs->typ == ISDN_CTYPE_DBRI) {
+                cs->hw.foreign = &dbri_foreign_interface;
+                cs->irq = cs->hw.foreign->get_irqnum(0);
+                if (cs->irq == 0)
+                        return (0);
+                cs->cardmsg = &foreign_card_msg;
+                return (1);
+        }
+#endif
+
+        return(0);
+}
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/foreign.h linux/drivers/isdn/hisax/foreign.h
--- v2.2.3/linux/drivers/isdn/hisax/foreign.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/foreign.h	Mon Mar 15 16:11:30 1999
@@ -0,0 +1,193 @@
+/* $Id: foreign.h,v 1.1 1998/11/09 07:48:57 baccala Exp $
+ *
+ * HiSax ISDN driver - foreign chipset interface
+ *
+ * Author       Brent Baccala (baccala@FreeSoft.org)
+ *
+ *
+ *
+ * $Log: foreign.h,v $
+ * Revision 1.1  1998/11/09 07:48:57  baccala
+ * Initial DBRI ISDN code.  Sometimes works (brings up the link and you
+ * can telnet through it), sometimes doesn't (crashes the machine)
+ *
+ */
+
+/*
+ *       ISDN operations
+ *
+ * Many of these routines take an "int dev" argument, which is simply
+ * an index into the drivers[] array.  Currently, we only support a
+ * single foreign chip, so the value should always be 0.  B channel
+ * operations require an "int chan", which should be 0 for channel B1
+ * and 1 for channel B2
+ *
+ * int get_irqnum(int dev)
+ *
+ *   returns the interrupt number being used by the chip.  ISDN4linux
+ *   uses this number to watch the interrupt during initialization and
+ *   make sure something is happening.
+ *
+ * int get_liu_state(int dev)
+ *
+ *   returns the current state of the ISDN Line Interface Unit (LIU)
+ *   as a number between 2 (state F2) and 7 (state F7).  0 may also be
+ *   returned if the chip doesn't exist or the LIU hasn't been
+ *   activated.  The meanings of the states are defined in I.430, ISDN
+ *   BRI Physical Layer Interface.  The most important two states are
+ *   F3 (shutdown) and F7 (syncronized).
+ *
+ * void liu_init(int dev, void (*callback)(void *), void *callback_arg)
+ *
+ *   initializes the LIU and optionally registers a callback to be
+ *   signaled upon a change of LIU state.  The callback will be called
+ *   with a single opaque callback_arg.  Once the callback has been
+ *   triggered, get_liu_state can be used to determine the LIU
+ *   current state.
+ *
+ * void liu_activate(int dev, int priority)
+ *
+ *   requests LIU activation at a given D-channel priority.
+ *   Successful activatation is achieved upon entering state F7, which
+ *   will trigger any callback previously registered with
+ *   liu_init.
+ *
+ * void liu_deactivate(int dev)
+ *
+ *   deactivates LIU.  Outstanding D and B channel transactions are
+ *   terminated rudely and without callback notification.  LIU change
+ *   of state callback will be triggered, however.
+ *
+ * void dxmit(int dev, __u8 *buffer, unsigned int count,
+ *            void (*callback)(void *, int), void *callback_arg)
+ *
+ *   transmits a packet - specified with buffer, count - over the D-channel
+ *   interface.  Buffer should begin with the LAPD address field and
+ *   end with the information field.  FCS and flag sequences should not
+ *   be included, nor is bit-stuffing required - all these functions are
+ *   performed by the chip.  The callback function will be called
+ *   DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed
+ *   both the arbitrary callback_arg and an integer error indication:
+ *
+ *       0 - successful transmission; ready for next packet
+ *   non-0 - error value
+ *
+ *   The callback routine should defer any time-consuming operations
+ *   to a bottom-half handler; however, dxmit may be called
+ *   from within the callback to request back-to-back transmission of
+ *   a second packet (without repeating the priority/collision mechanism)
+ *
+ *   A comment about the "collision detect" error, which is signalled
+ *   whenever the echoed D-channel data didn't match the transmitted
+ *   data.  This is part of ISDN's normal multi-drop T-interface
+ *   operation, indicating that another device has attempted simultaneous
+ *   transmission, but can also result from line noise.  An immediate
+ *   requeue via dxmit is suggested, but repeated collision
+ *   errors may indicate a more serious problem.
+ *
+ * void drecv(int dev, __u8 *buffer, unsigned int size,
+ *            void (*callback)(void *, int, unsigned int),
+ *            void *callback_arg)
+ *
+ *   register a buffer - buffer, size - into which a D-channel packet
+ *   can be received.  The callback function will be called DURING
+ *   THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an
+ *   arbitrary callback_arg, an integer error indication and the length
+ *   of the received packet, which will start with the address field,
+ *   end with the information field, and not contain flag or FCS
+ *   bytes.  Bit-stuffing will already have been corrected for.
+ *   Possible values of second callback argument "error":
+ *
+ *       0 - successful reception
+ *   non-0 - error value
+ *
+ * int bopen(int dev, int chan, int hdlcmode, u_char xmit_idle_char)
+ *
+ *   This function should be called before any other operations on a B
+ *   channel.  mode is either non-0 to (de)encapsulate using HDLC or 0
+ *   for transparent operation. In addition to arranging for interrupt
+ *   handling and channel multiplexing, it sets the xmit_idle_char
+ *   which is transmitted on the interface when no data buffer is
+ *   available.  Suggested values are: 0 for ISDN audio; FF for HDLC
+ *   mark idle; 7E for HDLC flag idle.  Returns 0 on a successful
+ *   open; -1 on error.
+ *
+ *   If the chip doesn't support HDLC encapsulation (the Am7930
+ *   doesn't), an error will be returned opening L1_MODE_HDLC; the
+ *   HiSax driver should retry with L1_MODE_TRANS, then be prepared to
+ *   bit-stuff the data before shipping it to the driver.
+ *
+ * void bclose(int dev, int chan)
+ *
+ *   Shuts down a B channel when no longer in use.
+ *
+ * void bxmit(int dev, int chan, __u8 *buffer, unsigned int count,
+ *            void (*callback)(void *, int), void *callback_arg)
+ *
+ *   transmits a data block - specified with buffer, count - over the
+ *   B channel interface specified by dev/chan.  In mode L1_MODE_HDLC,
+ *   a complete HDLC frames should be relayed with a single bxmit.
+ *   The callback function will be called DURING THE TOP HALF OF AN
+ *   INTERRUPT HANDLER and will be passed the arbitrary callback_arg
+ *   and an integer error indication:
+ *
+ *       0 - successful transmission; ready for next packet
+ *   non-0 - error
+ *
+ *   The callback routine should defer any time-consuming operations
+ *   to a bottom-half handler; however, bxmit may be called
+ *   from within the callback to request back-to-back transmission of
+ *   another data block
+ *
+ * void brecv(int dev, int chan, __u8 *buffer, unsigned int size,
+ *            void (*callback)(void *, int, unsigned int), void *callback_arg)
+ *
+ *   receive a raw data block - specified with buffer, size - over the
+ *   B channel interface specified by dev/chan.  The callback function
+ *   will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and
+ *   will be passed the arbitrary callback_arg, an integer error
+ *   indication and the length of the received packet.  In HDLC mode,
+ *   the packet will start with the address field, end with the
+ *   information field, and will not contain flag or FCS bytes.
+ *   Bit-stuffing will already have been corrected for.
+ *
+ *   Possible values of second callback argument "error":
+ *
+ *       0 - successful reception
+ *   non-0 - error value
+ *
+ *   The callback routine should defer any time-consuming operations
+ *   to a bottom-half handler; however, brecv may be called
+ *   from within the callback to register another buffer and ensure
+ *   continuous B channel reception without loss of data
+ * */
+
+struct foreign_interface {
+    int (*get_irqnum)(int dev);
+    int (*get_liu_state)(int dev);
+    void (*liu_init)(int dev, void (*callback)(void *), void *callback_arg);
+    void (*liu_activate)(int dev, int priority);
+    void (*liu_deactivate)(int dev);
+    void (*dxmit)(int dev, __u8 *buffer, unsigned int count,
+                  void (*callback)(void *, int),
+                  void *callback_arg);
+    void (*drecv)(int dev, __u8 *buffer, unsigned int size,
+                  void (*callback)(void *, int, unsigned int),
+                  void *callback_arg);
+    int (*bopen)(int dev, unsigned int chan,
+                 int hdlcmode, u_char xmit_idle_char);
+    void (*bclose)(int dev, unsigned int chan);
+    void (*bxmit)(int dev, unsigned int chan,
+                  __u8 *buffer, unsigned long count,
+                  void (*callback)(void *, int),
+                  void *callback_arg);
+    void (*brecv)(int dev, unsigned int chan,
+                  __u8 *buffer, unsigned long size,
+                  void (*callback)(void *, int, unsigned int),
+                  void *callback_arg);
+
+    struct foreign_interface *next;
+};
+
+extern struct foreign_interface amd7930_foreign_interface;
+extern struct foreign_interface dbri_foreign_interface;
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.2.3/linux/drivers/isdn/hisax/hisax.h	Wed Apr  1 20:11:50 1998
+++ linux/drivers/isdn/hisax/hisax.h	Mon Mar 15 16:11:30 1999
@@ -397,7 +397,8 @@
 	u_char s_state;
 };
 
-struct amd7930_hw {
+struct foreign_hw {
+	int doHDLCprocessing;
 	u_char *tx_buff;
 	u_char *rv_buff;
 	int rv_buff_in;
@@ -437,7 +438,7 @@
 		struct hscx_hw hscx;
 		struct hfcB_hw hfc;
 		struct tiger_hw tiger;
-		struct amd7930_hw  amd7930;
+		struct foreign_hw foreign;
 	} hw;
 };
 
@@ -640,6 +641,7 @@
 		struct njet_hw njet;
 		struct hfcD_hw hfcD;
 		struct ix1_hw niccy;
+                struct foreign_interface *foreign;
 	} hw;
 	int myid;
 	isdn_if iif;
@@ -710,8 +712,9 @@
 #define  ISDN_CTYPE_SEDLBAUER_PCMCIA	22
 #define  ISDN_CTYPE_AMD7930	23
 #define  ISDN_CTYPE_NICCY	24
+#define  ISDN_CTYPE_DBRI	25
 
-#define  ISDN_CTYPE_COUNT	24
+#define  ISDN_CTYPE_COUNT	25
 
 #ifdef ISDN_CHIP_ISAC
 #undef ISDN_CHIP_ISAC
@@ -856,12 +859,18 @@
 #define CARD_NICCY 0
 #endif
 
+#ifdef  CONFIG_HISAX_DBRI
+#define CARD_DBRI (1 << ISDN_CTYPE_DBRI)
+#else
+#define CARD_DBRI 0
+#endif
+
 
 #define  SUPORTED_CARDS  (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
 			 | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \
 			 | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \
 			 | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \
-			 | CARD_NICCY)
+			 | CARD_NICCY | CARD_DBRI)
 
 #define TEI_PER_CARD 0
 
diff -u --recursive --new-file v2.2.3/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.2.3/linux/drivers/isdn/hisax/isdnl1.c	Sun Nov  8 14:02:57 1998
+++ linux/drivers/isdn/hisax/isdnl1.c	Mon Mar 15 16:11:30 1999
@@ -144,8 +144,8 @@
 extern int setup_t163c(struct IsdnCard *card);
 #endif
 
-#if CARD_AMD7930
-extern int setup_amd7930(struct IsdnCard *card);
+#if CARD_AMD7930 || CARD_DBRI
+extern int setup_foreign(struct IsdnCard *card);
 #endif
 
 #if CARD_NICCY
@@ -163,7 +163,7 @@
  "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", 
  "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
  "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
- "AMD 7930", "NICCY"
+ "AMD 7930", "NICCY", "DBRI"
 };
 
 extern struct IsdnCard cards[];
@@ -366,7 +366,7 @@
 void
 debugl1(struct IsdnCardState *cs, char *msg)
 {
-	char tmp[256], tm[32];
+	char tmp[1024], tm[32];
 
 	jiftime(tm, jiffies);
 	sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg);
@@ -842,9 +842,10 @@
 			ret = setup_niccy(card);
 			break;
 #endif
-#if CARD_AMD7930
+#if CARD_AMD7930 || CARD_DBRI
 		case ISDN_CTYPE_AMD7930:
-			ret = setup_amd7930(card);
+		case ISDN_CTYPE_DBRI:
+			ret = setup_foreign(card);
 			break;
 #endif
 		default:
diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- v2.2.3/linux/drivers/macintosh/adb.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/macintosh/adb.c	Wed Mar 10 21:48:46 1999
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 1996 Paul Mackerras.
  */
+
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -184,8 +186,13 @@
 	return adb_send_request(req, flags & ADBREQ_SYNC);
 }
 
-/* Ultimately this should return the number of devices with
-   the given default id. */
+ /* Ultimately this should return the number of devices with
+    the given default id.
+    And it does it now ! Note: changed behaviour: This function
+    will now register if default_id _and_ handler_id both match
+    but handler_id can be left to 0 to match with default_id only.
+    When handler_id is set, this function will try to adjust
+    the handler_id id it doesn't match. */
 int
 adb_register(int default_id, int handler_id, struct adb_ids *ids,
 	     void (*handler)(unsigned char *, int, struct pt_regs *, int))
@@ -194,18 +201,20 @@
 
 	ids->nids = 0;
 	for (i = 1; i < 16; i++) {
-		if (adb_handler[i].original_address == default_id) {
+		if ((adb_handler[i].original_address == default_id) &&
+		    (!handler_id || (handler_id == adb_handler[i].handler_id) || 
+		    adb_try_handler_change(i, handler_id))) {
 			if (adb_handler[i].handler != 0) {
 				printk(KERN_ERR
 				       "Two handlers for ADB device %d\n",
 				       default_id);
-				return 0;
+				continue;
 			}
 			adb_handler[i].handler = handler;
 			ids->id[ids->nids++] = i;
 		}
 	}
-	return 1;
+	return ids->nids;
 }
 
 void
@@ -225,6 +234,41 @@
 		(*adb_handler[id].handler)(buf, nb, regs, autopoll);
 	}
 }
+
+/* Try to change handler to new_id. Will return 1 if successful */
+int
+adb_try_handler_change(int address, int new_id)
+{
+	struct adb_request req;
+
+	if (adb_handler[address].handler_id == new_id)
+	    return 1;
+	adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+    	    ADB_READREG(address,3));
+	if (req.reply_len < 2)
+	    return 0;
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	    ADB_WRITEREG(address, 3), req.reply[1], new_id);	
+	adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+	    ADB_READREG(address, 3));
+	if (req.reply_len < 2)
+	    return 0;
+	if (req.reply[2] != new_id)
+	    return 0;
+	adb_handler[address].handler_id = req.reply[2];
+
+	return 1;
+}
+
+int
+adb_get_infos(int address, int *original_address, int *handler_id)
+{
+	*original_address = adb_handler[address].original_address;
+	*handler_id = adb_handler[address].handler_id;
+	
+	return (*original_address != 0);
+}
+
 
 /*
  * /dev/adb device driver.
diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/mac_keyb.c linux/drivers/macintosh/mac_keyb.c
--- v2.2.3/linux/drivers/macintosh/mac_keyb.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/macintosh/mac_keyb.c	Wed Mar 10 21:48:46 1999
@@ -22,6 +22,7 @@
 #include <asm/bitops.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
+#include <asm/pmu.h>
 #include <asm/init.h>
 
 #include <linux/kbd_kern.h>
@@ -174,6 +175,12 @@
 static void leds_done(struct adb_request *);
 static void mac_put_queue(int);
 
+static void buttons_input(unsigned char *, int, struct pt_regs *, int);
+
+static void init_trackpad(int id);
+static void init_trackball(int id);
+static void init_turbomouse(int id);
+
 #ifdef CONFIG_ADBMOUSE
 /* XXX: Hook for mouse driver */
 void (*adb_mouse_interrupt_hook)(unsigned char *, int);
@@ -191,6 +198,17 @@
 
 static struct adb_ids keyboard_ids;
 static struct adb_ids mouse_ids;
+static struct adb_ids buttons_ids;
+
+/* Kind of mouse  */
+#define ADBMOUSE_STANDARD_100	0	/* Standard 100cpi mouse (handler 1) */
+#define ADBMOUSE_STANDARD_200	1	/* Standard 200cpi mouse (handler 2) */
+#define ADBMOUSE_EXTENDED	2	/* Apple Extended mouse (handler 4) */
+#define ADBMOUSE_TRACKBALL	3	/* TrackBall (handler 4) */
+#define ADBMOUSE_TRACKPAD       4	/* Apple's PowerBook trackpad (handler 4) */
+#define ADBMOUSE_TURBOMOUSE5    5	/* Turbomouse 5 (previously req. mousehack) */
+
+static int adb_mouse_kinds[16];
 
 /* this map indicates which keys shouldn't autorepeat. */
 static unsigned char dont_repeat[128] = {
@@ -198,7 +216,7 @@
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,	/* esc...option */
-	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* num lock */
+	0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* fn, num lock */
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -423,6 +441,16 @@
   */
 	struct kbd_struct *kbd;
 
+	/* If it's a trackpad, we alias the second button to the first.
+	   NOTE: Apple sends an ADB flush command to the trackpad when
+	         the first (the real) button is released. We could do
+		 this here using async flush requests.
+	*/
+	if (adb_mouse_kinds[(data[0]>>4) & 0xf] == ADBMOUSE_TRACKPAD) {
+		data[1] = (data[1] & 0x7f) | ((data[1] & data[2]) & 0x80);
+		data[2] = (data[2] & 0x7f) | 0x80;
+	}
+
 	if (adb_mouse_interrupt_hook)
 		adb_mouse_interrupt_hook(data, nb);
 
@@ -467,6 +495,64 @@
 }
 #endif /* CONFIG_ADBMOUSE */
 
+/* XXX Needs to get rid of this, see comments in pmu.c */
+extern int backlight_level;
+
+static void
+buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll)
+{
+	/*
+	 * XXX: Where is the contrast control for the passive?
+	 *  -- Cort
+	 */
+	 
+	/* Ignore data from register other than 0 */
+	if ((adb_hardware != ADB_VIAPMU) || (data[0] & 0x3) || (nb < 2))
+		return;
+		
+	switch (data[1]&0xf )
+	{
+		/* mute */
+		case 0x8:
+			/* down event */
+			if ( data[1] == (data[1]&0xf) ) {
+			}
+			break;
+		/* contrast decrease */
+		case 0x7:
+			/* down event */
+			if ( data[1] == (data[1]&0xf) ) {
+			}
+			break;
+		/* contrast increase */
+		case 0x6:
+			/* down event */
+			if ( data[1] == (data[1]&0xf) ) {
+			}
+			break;
+		/* brightness decrease */
+		case 0xa:
+			/* down event */
+			if ( data[1] == (data[1]&0xf) ) {
+				if (backlight_level > 2)
+					pmu_set_brightness(backlight_level-2);
+				else
+					pmu_set_brightness(0);
+			}
+			break;
+		/* brightness increase */
+		case 0x9:
+			/* down event */
+			if ( data[1] == (data[1]&0xf) ) {
+				if (backlight_level < 0x1e)
+					pmu_set_brightness(backlight_level+2);
+				else 
+					pmu_set_brightness(0x1f);
+			}
+			break;
+	}
+}
+
 /* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */
 static unsigned char mac_ledmap[8] = {
     0,		/* none */
@@ -546,10 +632,11 @@
 	/* initialize mouse interrupt hook */
 	adb_mouse_interrupt_hook = NULL;
 
-	adb_register(ADB_MOUSE, 1, &mouse_ids, mouse_input);
+	adb_register(ADB_MOUSE, 0, &mouse_ids, mouse_input);
 #endif /* CONFIG_ADBMOUSE */
 
-	adb_register(ADB_KEYBOARD, 5, &keyboard_ids, keyboard_input);
+	adb_register(ADB_KEYBOARD, 0, &keyboard_ids, keyboard_input);
+	adb_register(0x07, 0x1F, &buttons_ids, buttons_input);
 
 	for(i = 0; i < keyboard_ids.nids; i++) {
 	    /* turn off all leds */
@@ -557,54 +644,173 @@
 	    ADB_WRITEREG(keyboard_ids.id[i], KEYB_LEDREG), 0xff, 0xff);
 	}
 
-	/* get the keyboard to send separate codes for
-	   left and right shift, control, option keys. */
+	/* Enable full feature set of the keyboard
+	   ->get it to send separate codes for left and right shift,
+	   control, option keys */
 	for(i = 0;i < keyboard_ids.nids; i++) {
-	    /* get the keyboard to send separate codes for
-	    left and right shift, control, option keys. */
-	    adb_request(&req, NULL, ADBREQ_SYNC, 3,
-	    ADB_WRITEREG(keyboard_ids.id[i], 3), 0, 3);
+	    if (adb_try_handler_change(keyboard_ids.id[i], 5))
+	    	printk("ADB keyboard at %d, handler set to 5\n", keyboard_ids.id[i]);
+	    else if (adb_try_handler_change(keyboard_ids.id[i], 3))
+	    	printk("ADB keyboard at %d, handler set to 3\n", keyboard_ids.id[i]);
+	    else
+	    	printk("ADB keyboard at %d, handler 1\n", keyboard_ids.id[i]);
 	}
 
 	led_request.complete = 1;
 
-	/* Try to switch the mouse (id 3) to handler 4, for three-button
-	   mode. (0x20 is Service Request Enable, 0x03 is Device ID). */
+	/* Try to switch all mice to handler 4, or 2 for three-button
+	   mode and full resolution. */
 	for(i = 0; i < mouse_ids.nids; i++) {
+            if (adb_try_handler_change(mouse_ids.id[i], 4)) {
+                printk("ADB mouse at %d, handler set to 4", mouse_ids.id[i]);
+	        adb_mouse_kinds[mouse_ids.id[i]] = ADBMOUSE_EXTENDED;
+            }
+	    else if (adb_try_handler_change(mouse_ids.id[i], 2)) {
+	        printk("ADB mouse at %d, handler set to 2", mouse_ids.id[i]);
+	        adb_mouse_kinds[mouse_ids.id[i]] = ADBMOUSE_STANDARD_200;
+	    }
+	    else {
+                printk("ADB mouse at %d, handler 1", mouse_ids.id[i]);
+                adb_mouse_kinds[mouse_ids.id[i]] = ADBMOUSE_STANDARD_100;
+            }
+
+	    /* Register 1 is usually used for device identification.
+	       Here, we try to identify a known device and call the
+	       appropriate init function */
 	    adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
 			ADB_READREG(mouse_ids.id[i], 1));
 
-	    if ((req.reply_len) &&
-		(req.reply[1] == 0x9a) && (req.reply[2] == 0x21)) {
+            if ((req.reply_len) &&
+                (req.reply[1] == 0x9a) && (req.reply[2] == 0x21))
+                init_trackball(mouse_ids.id[i]);
+            else if ((req.reply_len >= 4) &&
+		(req.reply[1] == 0x74) && (req.reply[2] == 0x70) &&
+		(req.reply[3] == 0x61) && (req.reply[4] == 0x64))
+		init_trackpad(mouse_ids.id[i]);
+	    else if ((req.reply_len >= 4) &&
+		(req.reply[1] == 0x4b) && (req.reply[2] == 0x4d) &&
+		(req.reply[3] == 0x4c) && (req.reply[4] == 0x31))
+		init_turbomouse(mouse_ids.id[i]);
+            printk("\n");
+        }
+}
+
+__init static void 
+init_trackpad(int id)
+{
+	struct adb_request req;	
+	unsigned char r1_buffer[8];
+
+	printk(" (trackpad)");
+
+	adb_mouse_kinds[id] = ADBMOUSE_TRACKPAD;
+
+	adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+	ADB_READREG(id,1));
+	if (req.reply_len < 8)
+	    printk("bad length for reg. 1\n");
+	else
+	{
+	    memcpy(r1_buffer, &req.reply[1], 8);
+	    adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	        ADB_WRITEREG(id,1),
+	            r1_buffer[0],
+	            r1_buffer[1],
+	            r1_buffer[2],
+	            r1_buffer[3],
+	            r1_buffer[4],
+	            r1_buffer[5],
+	            0x0d, /*r1_buffer[6],*/
+	           r1_buffer[7]);
+
+            adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	        ADB_WRITEREG(id,2),
+	    	    0x99,
+	    	    0x94,
+	    	    0x19,
+	    	    0xff,
+	    	    0xb2,
+	    	    0x8a,
+	    	    0x1b,
+	    	    0x50);
+	    
+	    adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	        ADB_WRITEREG(id,1),
+	            r1_buffer[0],
+	            r1_buffer[1],
+	            r1_buffer[2],
+	            r1_buffer[3],
+	            r1_buffer[4],
+	            r1_buffer[5],
+	            0x03, /*r1_buffer[6],*/
+	            r1_buffer[7]);
+        }
+}
+
+__init static void 
+init_trackball(int id)
+{
+	struct adb_request req;
+	
+	printk(" (trackball)");
+	
+	adb_mouse_kinds[id] = ADBMOUSE_TRACKBALL;
 
-		printk("aha, trackball found at %d\n", mouse_ids.id[i]);
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 00,0x81);
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i], 3), 0x63, 4 );
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 01,0x81);
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 00,0x81);
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 02,0x81);
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 01,0x81);
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 03,0x38);
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 02,0x81);
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 00,0x81);
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 03,0x38);
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 01,0x81);
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 00,0x81);
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 02,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 03,0x38);
+}
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 01,0x81);
+__init static void
+init_turbomouse(int id)
+{
+	struct adb_request req;
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 02,0x81);
+        printk(" (TurboMouse 5)");
 
-		adb_request(&req, NULL, ADBREQ_SYNC, 3,
-		ADB_WRITEREG(mouse_ids.id[i],1), 03,0x38);
-	    }
-	}
+	adb_mouse_kinds[id] = ADBMOUSE_TURBOMOUSE5;
+	
+	adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	ADB_WRITEREG(id,2),
+	    0xe7,
+	    0x8c,
+	    0,
+	    0,
+	    0,
+	    0xff,
+	    0xff,
+	    0x94);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id));
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	ADB_WRITEREG(id,2),
+	    0xa5,
+	    0x14,
+	    0,
+	    0,
+	    0x69,
+	    0xff,
+	    0xff,
+	    0x27);
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
--- v2.2.3/linux/drivers/macintosh/macserial.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/macintosh/macserial.c	Wed Mar 10 21:48:46 1999
@@ -34,6 +34,8 @@
 #include <asm/segment.h>
 #include <asm/bitops.h>
 #include <asm/feature.h>
+#include <asm/adb.h>
+#include <asm/pmu.h>
 #ifdef CONFIG_KGDB
 #include <asm/kgdb.h>
 #endif
@@ -62,6 +64,8 @@
 
 struct tty_struct zs_ttys[NUM_CHANNELS];
 
+static int is_powerbook;
+
 #ifdef CONFIG_SERIAL_CONSOLE
 static struct console sercons;
 #endif
@@ -99,6 +103,10 @@
 #undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_POWER
+#undef SERIAL_DEBUG_THROTTLE
+#undef SERIAL_DEBUG_STOP
+#undef SERIAL_DEBUG_BAUDS
 
 #define RS_STROBE_TIME 10
 #define RS_ISR_PASS_LIMIT 256
@@ -106,8 +114,10 @@
 #define _INLINE_ inline
 
 static void probe_sccs(void);
-static void change_speed(struct mac_serial *info);
+static void change_speed(struct mac_serial *info, struct termios *old);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+static void set_scc_power(struct mac_serial * info, int state);
+static int setup_scc(struct mac_serial * info);
 
 static struct tty_struct *serial_table[NUM_CHANNELS];
 static struct termios *serial_termios[NUM_CHANNELS];
@@ -152,13 +162,6 @@
 	return 0;
 }
 
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
-	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400, 0 };
-
 /* 
  * Reading and writing Z8530 registers.
  */
@@ -320,12 +323,14 @@
 #endif
 		if (!tty)
 			continue;
-		tty_flip_buffer_push(tty);
-
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			tty_flip_buffer_push(tty);
+			
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 			static int flip_buf_ovf;
 			++flip_buf_ovf;
-			continue;
+			printk("FB. overflow: %d\n", flip_buf_ovf);
+			break;
 		}
 		tty->flip.count++;
 		{
@@ -347,6 +352,8 @@
 		*tty->flip.flag_buf_ptr++ = flag;
 		*tty->flip.char_buf_ptr++ = ch;
 	}
+	if (tty)
+		tty_flip_buffer_push(tty);
 }
 
 static void transmit_chars(struct mac_serial *info)
@@ -407,11 +414,17 @@
 		 */
 		if ((status & CTS) == 0) {
 			if (info->tx_stopped) {
+#ifdef SERIAL_DEBUG_FLOW
+				printk("CTS up\n");
+#endif			
 				info->tx_stopped = 0;
 				if (!info->tx_active)
 					transmit_chars(info);
 			}
 		} else {
+#ifdef SERIAL_DEBUG_FLOW
+			printk("CTS down\n");
+#endif			
 			info->tx_stopped = 1;
 		}
 	}
@@ -446,12 +459,17 @@
 	for (;;) {
 		zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
 #ifdef SERIAL_DEBUG_INTR
-//		printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);
+		printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);
 #endif	
 
 		if ((zs_intreg & CHAN_IRQMASK) == 0)
 			break;
 
+		if (!(info->flags & ZILOG_INITIALIZED)) {
+			printk("rs_interrupt: irq %d, port not initialized\n", irq);
+			break;
+		}
+
 		if (zs_intreg & CHBRxIP)
 			receive_chars(info, regs);
 		if (zs_intreg & CHBTxIP)
@@ -478,6 +496,11 @@
 {
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
 
+#ifdef SERIAL_DEBUG_STOP
+	printk("rs_stop %ld....\n",
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
 	if (serial_paranoia_check(info, tty->device, "rs_stop"))
 		return;
 	
@@ -497,6 +520,11 @@
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
 	unsigned long flags;
 	
+#ifdef SERIAL_DEBUG_STOP
+	printk("rs_start %ld....\n", 
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
 	if (serial_paranoia_check(info, tty->device, "rs_start"))
 		return;
 	
@@ -552,10 +580,16 @@
 
 static int startup(struct mac_serial * info)
 {
-	unsigned long flags;
-
-	if (info->flags & ZILOG_INITIALIZED)
-		return 0;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("startup() (ttyS%d, irq %d)\n", info->line, info->irq);
+#endif
+ 
+	if (info->flags & ZILOG_INITIALIZED) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk(" -> already inited\n");
+#endif
+ 		return 0;
+	}
 
 	if (!info->xmit_buf) {
 		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
@@ -563,12 +597,42 @@
 			return -ENOMEM;
 	}
 
-	save_flags(flags); cli();
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);
+#endif
+
+	set_scc_power(info, 1);
+	
+	setup_scc(info);
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq);
+#endif
+
+	info->flags |= ZILOG_INITIALIZED;
+	enable_irq(info->irq);
+
+	return 0;
+}
 
+static int setup_scc(struct mac_serial * info)
+{
+	unsigned long flags;
+	
 #ifdef SERIAL_DEBUG_OPEN
-	printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
+	printk("setting up ttys%d SCC...\n", info->line);
 #endif
 
+	save_flags(flags); cli(); /* Disable interrupts */	
+
+	/*
+	 * Reset the chip.
+	 */
+	write_zsreg(info->zs_channel, 9,
+		    (info->zs_channel == info->zs_chan_a? CHRA: CHRB));
+	udelay(10);
+	write_zsreg(info->zs_channel, 9, 0);
+
 	/*
 	 * Clear the receive FIFO.
 	 */
@@ -608,13 +672,13 @@
 	/*
 	 * Set the speed of the serial port
 	 */
-	change_speed(info);
+	change_speed(info, 0);
 
 	/* Save the current value of RR0 */
 	info->read_reg_zero = read_zsreg(info->zs_channel, 0);
 
-	info->flags |= ZILOG_INITIALIZED;
 	restore_flags(flags);
+
 	return 0;
 }
 
@@ -624,22 +688,20 @@
  */
 static void shutdown(struct mac_serial * info)
 {
-	unsigned long	flags;
-
-	if (!(info->flags & ZILOG_INITIALIZED))
-		return;
-
 #ifdef SERIAL_DEBUG_OPEN
-	printk("Shutting down serial port %d (irq %d)....", info->line,
+	printk("Shutting down serial port %d (irq %d)....\n", info->line,
 	       info->irq);
 #endif
 	
-	save_flags(flags); cli(); /* Disable interrupts */
+	if (!(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("(already shutdown)\n");
+#endif
 	
-	if (info->xmit_buf) {
-		free_page((unsigned long) info->xmit_buf);
-		info->xmit_buf = 0;
+		return;
 	}
+	
+	disable_irq(info->irq);
 
 	info->pendregs[1] = info->curregs[1] = 0;
 	write_zsreg(info->zs_channel, 1, 0);	/* no interrupts */
@@ -657,34 +719,156 @@
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
 
+	set_scc_power(info, 0);
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	memset(info->curregs, 0, sizeof(info->curregs));
+	memset(info->curregs, 0, sizeof(info->pendregs));
+
 	info->flags &= ~ZILOG_INITIALIZED;
-	restore_flags(flags);
 }
 
+static void set_scc_power(struct mac_serial * info, int state)
+{
+	if (feature_test(info->dev_node, FEATURE_Serial_enable) < 0)
+		return;		/* don't have serial power control */
+
+	/* The timings looks strange but that's the ones MacOS seems
+	   to use for the internal modem. I think we can use a lot faster
+	   ones, at least whe not using the modem, this should be tested.
+	 */				
+	if (state) {
+#ifdef SERIAL_DEBUG_POWER
+		printk(KERN_INFO "ttyS%02d: powering up hardware\n", info->line);
+#endif
+		if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) {
+			feature_clear(info->dev_node, FEATURE_Serial_reset);
+			mdelay(5);
+			feature_set(info->dev_node, FEATURE_Serial_enable);
+		}
+		if (info->zs_chan_a == info->zs_channel)
+			feature_set(info->dev_node, FEATURE_Serial_IO_A);
+		else
+			feature_set(info->dev_node, FEATURE_Serial_IO_B);
+		mdelay(1);
+		
+		if (info->is_cobalt_modem){
+			feature_set(info->dev_node, FEATURE_Modem_Reset);
+	   		mdelay(15);
+			feature_clear(info->dev_node, FEATURE_Modem_Reset);
+			/* XXX Note the big 250ms, we should probably replace this 
+			   by something better since we have irqs disabled here
+			 */
+			mdelay(250);
+		}
+		if (info->is_pwbk_ir)
+			pmu_enable_irled(1);
+	} else {
+#ifdef SERIAL_DEBUG_POWER
+		printk(KERN_INFO "ttyS%02d: shutting down hardware\n", info->line);
+#endif
+#ifdef CONFIG_KGDB
+		if (info->kgdb_channel) {
+#ifdef SERIAL_DEBUG_POWER
+			printk(KERN_INFO "        (canceled by KGDB)\n");
+#endif
+			return;
+		}
+#endif
+#ifdef CONFIG_XMON
+		if (!info->is_cobalt_modem) {
+#ifdef SERIAL_DEBUG_POWER
+			printk(KERN_INFO "        (canceled by XMON)\n");
+#endif
+			return;
+		}
+#endif
+		if (info->is_cobalt_modem) {
+#ifdef SERIAL_DEBUG_POWER
+			printk(KERN_INFO "ttyS%02d: shutting down modem\n", info->line);
+#endif
+			feature_set(info->dev_node, FEATURE_Modem_Reset);
+			mdelay(15);
+			feature_clear(info->dev_node, FEATURE_Modem_Reset);
+			mdelay(25);
+		}
+		if (info->is_pwbk_ir)
+			pmu_enable_irled(0);
+			
+		if (info->zs_chan_a == info->zs_channel) {
+#ifdef SERIAL_DEBUG_POWER
+			printk(KERN_INFO "ttyS%02d: shutting down SCC channel A\n", info->line);
+#endif
+			feature_clear(info->dev_node, FEATURE_Serial_IO_A);
+		} else {
+#ifdef SERIAL_DEBUG_POWER
+			printk(KERN_INFO "ttyS%02d: shutting down SCC channel B\n", info->line);
+#endif
+			feature_clear(info->dev_node, FEATURE_Serial_IO_B);
+		}
+		/* XXX for now, shut down SCC core only on powerbooks */
+		if (is_powerbook
+		    && !(feature_test(info->dev_node, FEATURE_Serial_IO_A) ||
+			 feature_test(info->dev_node, FEATURE_Serial_IO_B))) {
+#ifdef SERIAL_DEBUG_POWER
+			printk(KERN_INFO "ttyS%02d: shutting down SCC core\n", info->line);
+#endif
+			feature_set(info->dev_node, FEATURE_Serial_reset);
+			mdelay(10);
+			feature_clear(info->dev_node, FEATURE_Serial_enable);
+			mdelay(5);
+		}
+	}
+}
+
+
 /*
  * This routine is called to set the UART divisor registers to match
  * the specified baud rate for a serial port.
  */
-static void change_speed(struct mac_serial *info)
+static void change_speed(struct mac_serial *info, struct termios *old_termios)
 {
 	unsigned short port;
 	unsigned cflag;
-	int	i;
-	int	brg;
+	int	bits;
+	int	brg, baud;
 	unsigned long flags;
 
 	if (!info->tty || !info->tty->termios)
 		return;
-	cflag = info->tty->termios->c_cflag;
 	if (!(port = info->port))
 		return;
-	i = cflag & CBAUD;
+
+	cflag = info->tty->termios->c_cflag;
+	baud = tty_get_baud_rate(info->tty);
+	if (baud == 0) {
+		if (old_termios) {
+			info->tty->termios->c_cflag &= ~CBAUD;
+			info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+			cflag = info->tty->termios->c_cflag;
+			baud = tty_get_baud_rate(info->tty);
+		}
+		else
+			baud = info->zs_baud;
+	}
+	if (baud > 230400)
+		baud = 230400;
+	else if (baud == 0)
+		baud = 38400;
 
 	save_flags(flags); cli();
-	info->zs_baud = baud_table[i];
+	info->zs_baud = baud;
 	info->clk_divisor = 16;
 
-	switch (info->zs_baud) {
+#ifdef SERIAL_DEBUG_BAUDS
+	printk("set speed to %d bds, ", baud);
+#endif
+
+	switch (baud) {
 	case ZS_CLOCK/16:	/* 230400 */
 		info->curregs[4] = X16CLK;
 		info->curregs[11] = 0;
@@ -696,7 +880,7 @@
 	default:
 		info->curregs[4] = X16CLK;
 		info->curregs[11] = TCBR | RCBR;
-		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+		brg = BPS_TO_BRG(baud, ZS_CLOCK/info->clk_divisor);
 		info->curregs[12] = (brg & 255);
 		info->curregs[13] = ((brg >> 8) & 255);
 		info->curregs[14] = BRENABL;
@@ -709,19 +893,35 @@
 	case CS5:
 		info->curregs[3] |= Rx5;
 		info->curregs[5] |= Tx5;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("5 bits, ");
+#endif
+		bits = 7;
 		break;
 	case CS6:
 		info->curregs[3] |= Rx6;
 		info->curregs[5] |= Tx6;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("6 bits, ");
+#endif
+		bits = 8;
 		break;
 	case CS7:
 		info->curregs[3] |= Rx7;
 		info->curregs[5] |= Tx7;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("7 bits, ");
+#endif
+		bits = 9;
 		break;
 	case CS8:
 	default: /* defaults to 8 bits */
 		info->curregs[3] |= Rx8;
 		info->curregs[5] |= Tx8;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("8 bits, ");
+#endif
+		bits = 10;
 		break;
 	}
 	info->pendregs[3] = info->curregs[3];
@@ -730,11 +930,22 @@
 	info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
 	if (cflag & CSTOPB) {
 		info->curregs[4] |= SB2;
+		bits++;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("2 stop, ");
+#endif
 	} else {
 		info->curregs[4] |= SB1;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("1 stop, ");
+#endif
 	}
 	if (cflag & PARENB) {
-		info->curregs[4] |= PAR_ENA;
+		bits++;
+ 		info->curregs[4] |= PAR_ENA;
+#ifdef SERIAL_DEBUG_BAUDS
+		printk("parity, ");
+#endif
 	}
 	if (!(cflag & PARODD)) {
 		info->curregs[4] |= PAR_EVEN;
@@ -757,6 +968,17 @@
 	}
 	info->pendregs[15] = info->curregs[15];
 
+	/* Calc timeout value. This is pretty broken with high baud rates with HZ=100.
+	   This code would love a larger HZ and a >1 fifo size, but this is not
+	   a priority. The resulting value must be >HZ/2
+	 */
+	info->timeout = ((info->xmit_fifo_size*HZ*bits) / baud);
+	info->timeout += HZ/50+1;	/* Add .02 seconds of slop */
+
+#ifdef SERIAL_DEBUG_BAUDS
+	printk("timeout=%d/%ds, base:%d\n", (int)info->timeout, (int)HZ, (int)info->baud_base);
+#endif
+
 	/* Load up the new values */
 	load_zsregs(info->zs_channel, info->curregs);
 
@@ -877,8 +1099,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 	
-	printk("throttle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
+	printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty));
 #endif
 
 	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
@@ -915,8 +1136,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 	
-	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
+	printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty));
 #endif
 
 	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
@@ -1011,7 +1231,8 @@
 	info->closing_wait = new_serial.closing_wait;
 
 check_and_exit:
-	retval = startup(info);
+	if (info->flags & ZILOG_INITIALIZED)
+		retval = setup_scc(info);
 	return retval;
 }
 
@@ -1180,10 +1401,12 @@
 		return;
 	was_stopped = info->tx_stopped;
 
-	change_speed(info);
+	change_speed(info, old_termios);
 
-	if (was_stopped && !info->tx_stopped)
+	if (was_stopped && !info->tx_stopped) {
+		tty->hw_stopped = 0;
 		rs_start(tty);
+	}
 }
 
 /*
@@ -1246,6 +1469,9 @@
 	 * Now we wait for the transmit buffer to clear; and we notify 
 	 * the line discipline to only process XON/XOFF characters.
 	 */
+#ifdef SERIAL_DEBUG_OPEN
+	printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
+#endif
 	tty->closing = 1;
 	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
 		tty_wait_until_sent(tty, info->closing_wait);
@@ -1265,10 +1491,17 @@
 		 * Before we drop DTR, make sure the SCC transmitter
 		 * has completely drained.
 		 */
+#ifdef SERIAL_DEBUG_OPEN
+		printk("waiting end of Rx...\n");
+#endif
 		rs_wait_until_sent(tty, info->timeout);
 	}
 
 	shutdown(info);
+	/* restore flags now since shutdown() will have disabled this port's
+	   specific irqs */
+	restore_flags(flags);
+
 	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
 	if (tty->ldisc.flush_buffer)
@@ -1277,14 +1510,6 @@
 	info->event = 0;
 	info->tty = 0;
 
-	if (info->is_cobalt_modem) {
-		/* Power down modem */
-		feature_set(info->dev_node, FEATURE_Modem_Reset);
-		mdelay(15);
-		feature_clear(info->dev_node, FEATURE_Modem_PowerOn);
-		mdelay(15);
-	}
-
 	if (info->blocked_open) {
 		if (info->close_delay) {
 			current->state = TASK_INTERRUPTIBLE;
@@ -1295,8 +1520,6 @@
 	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
 			 ZILOG_CLOSING);
 	wake_up_interruptible(&info->close_wait);
-	
-	restore_flags(flags);
 }
 
 /*
@@ -1310,15 +1533,26 @@
 	if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
 		return;
 
+/*	printk("rs_wait_until_sent, timeout:%d, tty_stopped:%d, tx_stopped:%d\n",
+			timeout, tty->stopped, info->tx_stopped);
+*/
 	orig_jiffies = jiffies;
 	/*
 	 * Set the check interval to be 1/5 of the estimated time to
 	 * send a single character, and make it at least 1.  The check
 	 * interval should also be less than the timeout.
 	 */
+	if (info->timeout <= HZ/50) {
+		printk("macserial: invalid info->timeout=%d\n", info->timeout);
+		info->timeout = HZ/50+1;
+	}
+
 	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
 	char_time = char_time / 5;
-	if (char_time == 0)
+	if (char_time > HZ) {
+		printk("macserial: char_time %ld >HZ !!!\n", char_time);
+		char_time = 1;
+	} else if (char_time == 0)
 		char_time = 1;
 	if (timeout)
 		char_time = MIN(char_time, timeout);
@@ -1534,38 +1768,16 @@
 	 * Start up serial port
 	 */
 
-	if (info->is_cobalt_modem) {
-		/* Power up modem */
-		feature_set(info->dev_node, FEATURE_Modem_PowerOn);
-		mdelay(250);
-		feature_clear(info->dev_node, FEATURE_Modem_Reset);
-		mdelay(10);
-	}
 	retval = startup(info);
-	if (retval) {
-		if (info->is_cobalt_modem) {
-			/* Power down modem */
-			feature_set(info->dev_node, FEATURE_Modem_Reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Modem_PowerOn);
-			mdelay(15);
-		}
+	if (retval)
 		return retval;
-	}
 
 	retval = block_til_ready(tty, filp, info);
 	if (retval) {
 #ifdef SERIAL_DEBUG_OPEN
 		printk("rs_open returning after block_til_ready with %d\n",
-		       retval);
+			retval);
 #endif
-		if (info->is_cobalt_modem) {
-			/* Power down modem */
-			feature_set(info->dev_node, FEATURE_Modem_Reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Modem_PowerOn);
-			mdelay(15);
-		}
 		return retval;
 	}
 
@@ -1574,13 +1786,13 @@
 			*tty->termios = info->normal_termios;
 		else 
 			*tty->termios = info->callout_termios;
-		change_speed(info);
+		change_speed(info, 0);
 	}
 #ifdef CONFIG_SERIAL_CONSOLE
 	if (sercons.cflag && sercons.index == line) {
 		tty->termios->c_cflag = sercons.cflag;
 		sercons.cflag = 0;
-		change_speed(info);
+		change_speed(info, 0);
 	}
 #endif
 
@@ -1588,7 +1800,7 @@
 	info->pgrp = current->pgrp;
 
 #ifdef SERIAL_DEBUG_OPEN
-	printk("rs_open ttys%d successful...", info->line);
+	printk("rs_open ttys%d successful...\n", info->line);
 #endif
 	return 0;
 }
@@ -1597,7 +1809,7 @@
 
 static void show_serial_version(void)
 {
-	printk("PowerMac Z8530 serial driver version 1.00\n");
+	printk("PowerMac Z8530 serial driver version 1.01\n");
 }
 
 /* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -1606,7 +1818,7 @@
 {
 	struct device_node *dev, *ch;
 	struct mac_serial **pp;
-	int n;
+	int n, lenp;
 
 	n = 0;
 	pp = &zs_chain;
@@ -1616,12 +1828,6 @@
 			       dev->full_name);
 			continue;
 		}
-		feature_clear(dev, FEATURE_Serial_reset);
-		mdelay(5);
-		feature_set(dev, FEATURE_Serial_enable);
-		feature_set(dev, FEATURE_Serial_IO_A);
-		feature_set(dev, FEATURE_Serial_IO_B);
-		mdelay(5);
 		for (ch = dev->child; ch != 0; ch = ch->sibling) {
 			if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) {
 				printk("Can't use %s: %d addrs %d intrs\n",
@@ -1630,21 +1836,17 @@
 			}
 			zs_channels[n].control = (volatile unsigned char *)
 				ioremap(ch->addrs[0].address, 0x1000);
-			zs_channels[n].data = zs_channels[n].control
-				+ ch->addrs[0].size / 2;
+			zs_channels[n].data = zs_channels[n].control + 0x10;
 			spin_lock_init(&zs_channels[n].lock);
 			zs_soft[n].zs_channel = &zs_channels[n];
 			zs_soft[n].dev_node = ch;
 			zs_soft[n].irq = ch->intrs[0].line;
+			zs_soft[n].zs_channel->parent = &zs_soft[n];
 			zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt");
-			if (zs_soft[n].is_cobalt_modem)
-			{
-				/* Just in case the modem is up, shut it down */
-				feature_set(ch, FEATURE_Modem_Reset);
-				mdelay(15);
-				feature_clear(ch, FEATURE_Modem_PowerOn);
-				mdelay(15);
-			}
+
+			/* XXX tested only with wallstreet PowerBook, should do no harm anyway */	
+			zs_soft[n].is_pwbk_ir = (strcmp(get_property(ch, "AAPL,connector",
+				&lenp), "infrared") == 0);
 
 			/* XXX this assumes the prom puts chan A before B */
 			if (n & 1)
@@ -1677,13 +1879,19 @@
 	if (zs_chain == 0)
 		probe_sccs();
 
+	/* XXX assume it's a powerbook if we have a via-pmu */
+	is_powerbook = find_devices("via-pmu") != 0;
+
 	/* Register the interrupt handler for each one */
+	save_flags(flags); cli();
 	for (i = 0; i < zs_channels_found; ++i) {
 		if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
 				"SCC", &zs_soft[i]))
 			printk(KERN_ERR "macserial: can't get irq %d\n",
 			       zs_soft[i].irq);
+		disable_irq(zs_soft[i].irq);
 	}
+	restore_flags(flags);
 
 	show_serial_version();
 
@@ -1739,8 +1947,6 @@
 	if (tty_register_driver(&callout_driver))
 		panic("Couldn't register callout driver\n");
 
-	save_flags(flags); cli();
-
 	for (channel = 0; channel < zs_channels_found; ++channel) {
 #ifdef CONFIG_KGDB
 		if (zs_soft[channel].kgdb_channel) {
@@ -1749,10 +1955,15 @@
 		}
 #endif
 		zs_soft[channel].clk_divisor = 16;
-		zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+/* -- we are not sure the SCC is powered ON at this point
+ 		zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+*/		
+		zs_soft[channel].zs_baud = 38400;
 
 		/* If console serial line, then enable interrupts. */
 		if (zs_soft[channel].is_cons) {
+			printk("macserial: console line, enabling interrupt %d\n", zs_soft[channel].irq);
+			panic("macserial: console not supported yet !");
 			write_zsreg(zs_soft[channel].zs_channel, R1,
 				    (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
 			write_zsreg(zs_soft[channel].zs_channel, R9,
@@ -1787,6 +1998,7 @@
 		info->normal_termios = serial_driver.init_termios;
 		info->open_wait = 0;
 		info->close_wait = 0;
+		info->timeout = HZ;
 		printk("tty%02d at 0x%08x (irq = %d)", info->line, 
 			info->port, info->irq);
 		printk(" is a Z8530 ESCC");
@@ -1795,10 +2007,21 @@
 			printk(", port = %s", connector);
 		if (info->is_cobalt_modem)
 			printk(" (cobalt modem)");
+		if (info->is_pwbk_ir)
+			printk(" (powerbook IR)");
 		printk("\n");
-	}
 
-	restore_flags(flags);
+#ifdef CONFIG_KGDB
+		if (info->kgdb_channel)
+			continue;
+#endif			
+#ifdef CONFIG_XMON
+		if (!info->is_cobalt_modem)
+			continue;
+#endif			
+		/* By default, disable the port */
+		set_scc_power(info, 0);
+ 	}
 
 	return 0;
 }
@@ -2022,6 +2245,8 @@
 	if (zs_chain == 0)
 		probe_sccs();
 
+	set_scc_power(&zs_soft[n], 1);
+	
 	zs_kgdbchan = zs_soft[tty_num].zs_channel;
 	zs_soft[tty_num].change_needed = 0;
 	zs_soft[tty_num].clk_divisor = 16;
diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/macserial.h linux/drivers/macintosh/macserial.h
--- v2.2.3/linux/drivers/macintosh/macserial.h	Thu Nov 19 09:56:28 1998
+++ linux/drivers/macintosh/macserial.h	Wed Mar 10 21:48:46 1999
@@ -38,27 +38,27 @@
 /*
  * Definitions for ZILOG_struct (and serial_struct) flags field
  */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
-				   on the callout port */
-#define ZILOG_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK	0x0004	/* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK	0x0030
-#define ZILOG_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
-
-#define ZILOG_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
-#define ZILOG_SPD_CUST	0x0030  /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS	0x0FFF	/* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430	/* Legal flags that non-privileged
-				 * users can set or reset */
+#define ZILOG_HUP_NOTIFY	0x0001	/* Notify getty on hangups and closes 
+				   	 * on the callout port */
+#define ZILOG_FOURPORT 		0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK		0x0004	/* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS	0x0008	/* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK		0x0030
+#define ZILOG_SPD_HI		0x0010	/* Use 56000 instead of 38400 bps */
+
+#define ZILOG_SPD_VHI		0x0020  /* Use 115200 instead of 38400 bps */
+#define ZILOG_SPD_CUST		0x0030  /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST		0x0040	/* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ 		0x0080	/* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT	0x0100	/* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT	0x0200	/* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP	0x0400	/* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS		0x0FFF	/* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK		0x0430	/* Legal flags that non-privileged
+					 * users can set or reset */
 
 /* Internal flags used only by kernel/chr_drv/serial.c */
 #define ZILOG_INITIALIZED	0x80000000 /* Serial port was initialized */
@@ -81,10 +81,14 @@
  * For definitions of the flags field, see tty.h
  */
 
+struct mac_serial;
+
 struct mac_zschannel {
-	volatile unsigned char *control;
-	volatile unsigned char *data;
-	spinlock_t	lock;
+	volatile unsigned char*	control;
+	volatile unsigned char*	data;
+	spinlock_t		lock;
+	/* Used for debugging */
+	struct mac_serial*	parent;
 };
 
 struct mac_serial {
@@ -99,6 +103,7 @@
 	char kgdb_channel;  /* Kgdb is running on this channel */
 	char is_cons;       /* Is this our console. */
 	char is_cobalt_modem;	/* is a gatwick-based cobalt modem */
+	char is_pwbk_ir;	/* is connected to an IR led on powerbooks */
 	unsigned char tx_active; /* character is being xmitted */
 	unsigned char tx_stopped; /* output is suspended */
 
diff -u --recursive --new-file v2.2.3/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
--- v2.2.3/linux/drivers/macintosh/via-pmu.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/macintosh/via-pmu.c	Wed Mar 10 21:48:46 1999
@@ -31,6 +31,7 @@
 #include <asm/init.h>
 #include <asm/irq.h>
 #include <asm/feature.h>
+#include <asm/uaccess.h>
 
 /* Misc minor number allocated for /dev/pmu */
 #define PMU_MINOR	154
@@ -89,8 +90,9 @@
 static int adb_int_pending;
 static int pmu_adb_flags;
 static int adb_dev_map = 0;
-static struct adb_request bright_req_1, bright_req_2;
+static struct adb_request bright_req_1, bright_req_2, bright_req_3;
 static struct device_node *vias;
+static int pmu_kind = PMU_UNKNOWN;
 
 int asleep;
 struct notifier_block *sleep_notifier_list;
@@ -108,7 +110,6 @@
 static void pmu_done(struct adb_request *req);
 static void pmu_handle_data(unsigned char *data, int len,
 			    struct pt_regs *regs);
-static void set_brightness(int level);
 static void set_volume(int level);
 
 /*
@@ -118,7 +119,7 @@
  * - the number of response bytes which the PMU will return, or
  *   -1 if it will send a length byte.
  */
-static s8 pmu_data_len[256][2] = {
+static s8 pmu_data_len[256][2] __openfirmwaredata = {
 /*	   0	   1	   2	   3	   4	   5	   6	   7  */
 /*00*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
 /*08*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
@@ -154,9 +155,8 @@
 /*f8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
 };
 
-__openfirmware
 
-void
+void __openfirmware
 find_via_pmu()
 {
 	vias = find_devices("via-pmu");
@@ -185,6 +185,15 @@
 		if (vias->n_addrs < 1 || vias->n_intrs < 1)
 			return;
 	}
+
+	if (vias->parent->name && strcmp(vias->parent->name, "ohare") == 0
+	    || device_is_compatible(vias->parent, "ohare"))
+		pmu_kind = PMU_OHARE_BASED;
+	else if (device_is_compatible(vias->parent, "heathrow"))
+		pmu_kind = PMU_HEATHROW_BASED;
+	else
+		pmu_kind = PMU_UNKNOWN;
+
 	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
 
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
@@ -195,9 +204,15 @@
 		via = NULL;
 
 	adb_hardware = ADB_VIAPMU;
+	
+	if (via)
+		printk(KERN_INFO "PMU driver initialized for %s\n",
+		(pmu_kind == PMU_OHARE_BASED) ? "PowerBook 2400/3400/3500(G3)" :
+		((pmu_kind == PMU_HEATHROW_BASED) ? "PowerBook G3 Series" :
+		"Unknown PowerBook"));
 }
 
-void
+void __openfirmware
 via_pmu_init(void)
 {
 	if (vias == NULL)
@@ -205,6 +220,7 @@
 
 	bright_req_1.complete = 1;
 	bright_req_2.complete = 1;
+	bright_req_3.complete = 1;
 
 	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
 			(void *)0)) {
@@ -220,9 +236,12 @@
 	adb_send_request = pmu_adb_send_request;
 	adb_autopoll = pmu_adb_autopoll;
 	adb_reset_bus = pmu_reset_bus;
+
+	/* Enable backlight */
+	pmu_enable_backlight(1);
 }
 
-static int
+static int __openfirmware
 init_pmu()
 {
 	int timeout;
@@ -232,7 +251,7 @@
 	out_8(&via[DIRB], (via[DIRB] | TREQ) & ~TACK);	/* TACK in, TREQ out */
 
 	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xff);
-	timeout = 100000;
+	timeout =  100000;
 	while (!req.complete) {
 		if (--timeout < 0) {
 			printk(KERN_ERR "init_pmu: no response from PMU\n");
@@ -259,8 +278,14 @@
 	return 1;
 }
 
+int
+pmu_get_model(void)
+{
+	return pmu_kind;
+}
+
 /* Send an ADB command */
-static int
+static int __openfirmware
 pmu_adb_send_request(struct adb_request *req, int sync)
 {
 	int i;
@@ -285,7 +310,7 @@
 }
 
 /* Enable/disable autopolling */
-static int
+static int __openfirmware
 pmu_adb_autopoll(int devs)
 {
 	struct adb_request req;
@@ -305,7 +330,7 @@
 }
 
 /* Reset the ADB bus */
-static int
+static int __openfirmware
 pmu_reset_bus(void)
 {
 	struct adb_request req;
@@ -348,7 +373,7 @@
 }
 
 /* Construct and send a pmu request */
-int
+int __openfirmware
 pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
 	    int nbytes, ...)
 {
@@ -380,7 +405,7 @@
  * first byte is CUDA_PACKET or PMU_PACKET.  For CUDA_PACKET, we
  * emulate a few CUDA requests.
  */
-int
+int __openfirmware
 pmu_send_request(struct adb_request *req)
 {
 	int i;
@@ -426,7 +451,7 @@
 	return -EINVAL;
 }
 
-int
+int __openfirmware
 pmu_queue_request(struct adb_request *req)
 {
 	unsigned long flags;
@@ -465,7 +490,7 @@
 	return 0;
 }
 
-static void
+static void __openfirmware
 send_byte(int x)
 {
 	out_8(&via[ACR], 0x1c);
@@ -473,7 +498,7 @@
 	out_8(&via[B], via[B] & ~0x10);		/* assert TREQ */
 }
 
-static void
+static void __openfirmware
 recv_byte()
 {
 	out_8(&via[ACR], 0x0c);
@@ -481,7 +506,7 @@
 	out_8(&via[B], via[B] & ~0x10);
 }
 
-static void
+static void __openfirmware
 pmu_start()
 {
 	unsigned long flags;
@@ -506,7 +531,7 @@
 	restore_flags(flags);
 }
 
-void
+void __openfirmware
 pmu_poll()
 {
 	int ie;
@@ -517,7 +542,7 @@
 	_enable_interrupts(ie);
 }
 
-static void
+static void __openfirmware
 via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
 	int intr;
@@ -553,7 +578,7 @@
 	}
 }
 
-static void
+static void __openfirmware
 pmu_sr_intr(struct pt_regs *regs)
 {
 	struct adb_request *req;
@@ -649,7 +674,7 @@
 	}
 }
 
-static void
+static void __openfirmware
 pmu_done(struct adb_request *req)
 {
 	req->complete = 1;
@@ -658,7 +683,7 @@
 }
 
 /* Interrupt data could be the result data from an ADB cmd */
-static void
+static void __openfirmware
 pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
 {
 	static int show_pmu_ints = 1;
@@ -689,7 +714,7 @@
 	} else {
 		if (data[0] == 0x08 && len == 3) {
 			/* sound/brightness buttons pressed */
-			set_brightness(data[1]);
+			pmu_set_brightness(data[1] >> 3);
 			set_volume(data[2]);
 		} else if (show_pmu_ints
 			   && !(data[0] == PMU_INT_TICK && len == 1)) {
@@ -702,54 +727,101 @@
 	}
 }
 
-int backlight_bright = -1;
+int backlight_level = -1;
 int backlight_enabled = 0;
 
-#define LEVEL_TO_BRIGHT(lev)	((lev) < 8? 0x7f: 0x4a - ((lev) >> 2))
+#define LEVEL_TO_BRIGHT(lev)	((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
 
-void
+void __openfirmware
 pmu_enable_backlight(int on)
 {
 	struct adb_request req;
 
+	if (adb_hardware != ADB_VIAPMU)
+		return ;
+		
 	if (on) {
-		if (backlight_bright < 0) {
+	    /* first call: get current backlight value */
+	    if (backlight_level < 0) {
+		switch(pmu_kind) {
+		    case PMU_OHARE_BASED:
 			pmu_request(&req, NULL, 2, 0xd9, 0);
 			while (!req.complete)
 				pmu_poll();
-			backlight_bright = LEVEL_TO_BRIGHT(req.reply[1]);
+			backlight_level = req.reply[1] >> 3;
+			printk(KERN_DEBUG "pmu: controls returned bright: %d\n", (int)req.reply[1]);
+			break;
+		    case PMU_HEATHROW_BASED:
+			pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe);
+			while (!req.complete)
+				pmu_poll();
+			printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]);
+			backlight_level = req.reply[1];
+			break;
+		    default:
+		        backlight_enabled = 0;
+		        return;
 		}
-		pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
-			    backlight_bright);
-		while (!req.complete)
-			pmu_poll();
+	    }
+	    pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+	    	LEVEL_TO_BRIGHT(backlight_level));
+	    while (!req.complete)
+		pmu_poll();
 	}
-	pmu_request(&req, NULL, 2, PMU_BACKLIGHT_CTRL, on? 0x81: 1);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+	    PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
 	while (!req.complete)
 		pmu_poll();
 	backlight_enabled = on;
 }
 
-static void
-set_brightness(int level)
+void __openfirmware
+pmu_set_brightness(int level)
 {
-	backlight_bright = LEVEL_TO_BRIGHT(level);
+	int bright;
+
+	if (adb_hardware != ADB_VIAPMU)
+		return ;
+
+	backlight_level = level;
+	bright = LEVEL_TO_BRIGHT(level);
 	if (!backlight_enabled)
 		return;
 	if (bright_req_1.complete)
 		pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
-			    backlight_bright);
+		    bright);
 	if (bright_req_2.complete)
-		pmu_request(&bright_req_2, NULL, 2, PMU_BACKLIGHT_CTRL,
-			    backlight_bright < 0x7f? 0x81: 1);
+		pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL,
+		    PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF));
+
+	/* XXX nvram address is hard-coded and looks ok on wallstreet, please
+	   test on your machine. Note that newer MacOS system software may break
+	   the nvram layout. */
+	if ((pmu_kind == PMU_HEATHROW_BASED) && bright_req_3.complete)
+		pmu_request(&bright_req_3, NULL, 4, PMU_WRITE_NVRAM,
+			    0x14, 0xe, level);
 }
 
-static void
+void __openfirmware
+pmu_enable_irled(int on)
+{
+	struct adb_request req;
+
+	if (adb_hardware != ADB_VIAPMU)
+		return ;
+
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
+	    (on ? PMU_POW_ON : PMU_POW_OFF));
+	while (!req.complete)
+		pmu_poll();
+}
+
+static void __openfirmware
 set_volume(int level)
 {
 }
 
-void
+void __openfirmware
 pmu_restart(void)
 {
 	struct adb_request req;
@@ -768,7 +840,7 @@
 		;
 }
 
-void
+void __openfirmware
 pmu_shutdown(void)
 {
 	struct adb_request req;
@@ -802,7 +874,7 @@
 } *pbook_pci_saves;
 static int n_pbook_pci_saves;
 
-static inline void
+static inline void __openfirmware
 pbook_pci_save(void)
 {
 	int npci;
@@ -829,7 +901,7 @@
 	}
 }
 
-static inline void
+static inline void __openfirmware
 pbook_pci_restore(void)
 {
 	u16 cmd;
@@ -868,7 +940,7 @@
 #define IRQ_ENABLE	((unsigned int *)0xf3000024)
 #define MEM_CTRL	((unsigned int *)0xf8000070)
 
-int powerbook_sleep(void)
+int __openfirmware powerbook_sleep(void)
 {
 	int ret, i, x;
 	static int save_backlight;
@@ -967,29 +1039,44 @@
 /*
  * Support for /dev/pmu device
  */
-static int pmu_open(struct inode *inode, struct file *file)
+static int __openfirmware pmu_open(struct inode *inode, struct file *file)
 {
 	return 0;
 }
 
-static ssize_t pmu_read(struct file *file, char *buf,
+static ssize_t __openfirmware pmu_read(struct file *file, char *buf,
 			size_t count, loff_t *ppos)
 {
 	return 0;
 }
 
-static ssize_t pmu_write(struct file *file, const char *buf,
+static ssize_t __openfirmware pmu_write(struct file *file, const char *buf,
 			 size_t count, loff_t *ppos)
 {
 	return 0;
 }
 
-static int pmu_ioctl(struct inode * inode, struct file *filp,
+/* Note: removed __openfirmware here since it causes link errors */
+static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp,
 		     u_int cmd, u_long arg)
 {
+	int error;
+	__u32 value;
+
 	switch (cmd) {
-	case PMU_IOC_SLEEP:
+	    case PMU_IOC_SLEEP:
+	    	if (pmu_kind != PMU_OHARE_BASED)
+	    		return -ENOSYS;
 		return powerbook_sleep();
+	    case PMU_IOC_GET_BACKLIGHT:
+		return put_user(backlight_level, (__u32 *)arg);
+	    case PMU_IOC_SET_BACKLIGHT:
+		error = get_user(value, (__u32 *)arg);
+		if (!error)
+			pmu_set_brightness(value);
+		return error;
+	    case PMU_IOC_GET_MODEL:
+	    	return put_user(pmu_kind, (__u32 *)arg);
 	}
 	return -EINVAL;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
--- v2.2.3/linux/drivers/misc/parport_ax.c	Fri Jan  8 22:36:06 1999
+++ linux/drivers/misc/parport_ax.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: parport_ax.c,v 1.14 1998/11/16 04:48:02 davem Exp $
+/* $Id: parport_ax.c,v 1.17 1999/01/20 06:18:54 davem Exp $
  * Parallel-port routines for Sun Ultra/AX architecture
  * 
  * Author: Eddie C. Dost <ecd@skynet.be>
@@ -220,13 +220,13 @@
 	/* FIXME check that resources are free */
 	int err;
 
-	if (p->irq != PARPORT_IRQ_NONE)
+	if (p->irq != PARPORT_IRQ_NONE) {
 		if ((err = request_irq(p->irq, parport_ax_interrupt,
 				       0, p->name, p)) != 0)
 			return err;
 		else
 			parport_ax_enable_irq(p);
-
+	}
 	request_region(p->base, p->size, p->name);
 	if (p->modes & PARPORT_MODE_PCECR)
 		request_region(p->base+0x400, 3, p->name);
diff -u --recursive --new-file v2.2.3/linux/drivers/misc/parport_ieee1284.c linux/drivers/misc/parport_ieee1284.c
--- v2.2.3/linux/drivers/misc/parport_ieee1284.c	Tue Feb 23 15:21:33 1999
+++ linux/drivers/misc/parport_ieee1284.c	Thu Mar 11 09:27:06 1999
@@ -69,5 +69,6 @@
 		& ~1) & ~2);
 	udelay(1);
 	/* Data available? */
-	return (parport_wait_peripheral(port, 0x20, 0))?1:2;
+	parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK);
+	return (parport_read_status(port) & PARPORT_STATUS_ERROR)?1:2;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.2.3/linux/drivers/misc/parport_pc.c	Mon Jan  4 15:08:17 1999
+++ linux/drivers/misc/parport_pc.c	Thu Mar 11 09:27:06 1999
@@ -378,34 +378,35 @@
  */
 static int parport_ECR_present(struct parport *pb)
 {
-	unsigned char r, octr = parport_pc_read_control(pb);
-	unsigned char oecr = parport_pc_read_econtrol(pb);
-	unsigned char tmp;
+	unsigned char r;
 
+	parport_pc_write_control (pb, 0xc);
 	r = parport_pc_read_control(pb);	
 	if ((parport_pc_read_econtrol(pb) & 0x3) == (r & 0x3)) {
 		parport_pc_write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */
 
 		r = parport_pc_read_control(pb);	
-		if ((parport_pc_read_econtrol(pb) & 0x2) == (r & 0x2)) {
-			parport_pc_write_control(pb, octr);
-			return 0; /* Sure that no ECR register exists */
-		}
+		if ((parport_pc_read_econtrol(pb) & 0x2) == (r & 0x2))
+			goto no_reg; /* Sure that no ECR register exists */
 	}
 	
 	if ((parport_pc_read_econtrol(pb) & 0x3 ) != 0x1)
-		return 0;
+		goto no_reg;
 
 	parport_pc_write_econtrol(pb, 0x34);
-	tmp = parport_pc_read_econtrol(pb);
+	if (parport_pc_read_econtrol(pb) != 0x35)
+		goto no_reg;
 
-	parport_pc_write_econtrol(pb, oecr);
-	parport_pc_write_control(pb, octr);
-	
-	if (tmp != 0x35)
-		return 0;
+	parport_pc_write_control(pb, 0xc);
 
+	/* Go to mode 000; SPP, reset FIFO */
+	parport_pc_frob_econtrol (pb, 0xe0, 0x00);
+	
 	return PARPORT_MODE_PCECR;
+
+ no_reg:
+	parport_pc_write_control (pb, 0xc);
+	return 0;
 }
 
 static int parport_ECP_supported(struct parport *pb)
@@ -706,14 +707,11 @@
 
 static int probe_one_port(unsigned long int base, int irq, int dma)
 {
-	struct parport tmpport, *p;
+	struct parport *p;
 	int probedirq = PARPORT_IRQ_NONE;
 	if (check_region(base, 3)) return 0;
-	tmpport.base = base;
-	tmpport.ops = &parport_pc_ops;
-	if (!(parport_SPP_supported(&tmpport))) return 0;
-       	if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops))) return 0;
-	p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p);
+	if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops)))
+		return 0;
 	if (p->base != 0x3bc) {
 		if (!check_region(base+0x400,3)) {
 			p->modes |= parport_ECR_present(p);	
@@ -725,6 +723,12 @@
 			p->modes |= parport_ECPEPP_supported(p);
 		}
 	}
+	if (!parport_SPP_supported(p)) {
+		/* No port. */
+		parport_unregister_port (p);
+		return 0;
+	}
+	p->modes |= PARPORT_MODE_PCSPP | parport_PS2_supported(p);
 	p->size = (p->modes & (PARPORT_MODE_PCEPP 
 			       | PARPORT_MODE_PCECPEPP))?8:3;
 	printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
diff -u --recursive --new-file v2.2.3/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.2.3/linux/drivers/net/3c509.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/net/3c509.c	Wed Mar 10 16:51:35 1999
@@ -1,8 +1,8 @@
 /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
 /*
-	Written 1993-1997 by Donald Becker.
+	Written 1993-1998 by Donald Becker.
 
-	Copyright 1994-1997 by Donald Becker.
+	Copyright 1994-1998 by Donald Becker.
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.	 This software may be used and
 	distributed according to the terms of the GNU Public License,
@@ -35,19 +35,24 @@
 				other cleanups.  -djb
 		Andrea Arcangeli:	Upgraded to Donald Becker's version 1.12.
 		Rick Payne:	Fixed SMP race condition
+		v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
+		v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
+		v1.15 1/31/98 Faster recovery for Tx errors. -djb
+		v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
 */
 
-static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.16 (2.2) 2/3/98 becker@cesdis.gsfc.nasa.gov.\n";
 /* A few values that may be tweaked. */
 
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (400*HZ/1000)
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-#define INTR_WORK      10
+static int max_interrupt_work = 10;
 
+#include <linux/config.h>
 #include <linux/module.h>
 
-#include <linux/config.h>	/* for CONFIG_MCA */
+#include <linux/mca.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/interrupt.h>
@@ -129,12 +134,13 @@
 	/* skb send-queue */
 	int head, size;
 	struct sk_buff *queue[SKB_QUEUE_SIZE];
+	char mca_slot;
 };
-static int id_port = 0x100;
+static int id_port = 0x110;		/* Start with 0x110 to avoid new sound cards.*/
 static struct device *el3_root_dev = NULL;
 
 static ushort id_read_eeprom(int index);
-static ushort read_eeprom(short ioaddr, int index);
+static ushort read_eeprom(int ioaddr, int index);
 static int el3_open(struct device *dev);
 static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
 static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
@@ -144,14 +150,29 @@
 static int el3_close(struct device *dev);
 static void set_multicast_list(struct device *dev);
 
-
+#ifdef CONFIG_MCA
+struct el3_mca_adapters_struct {
+	char* name;
+	int id;
+};
+
+struct el3_mca_adapters_struct el3_mca_adapters[] = {
+	{ "3Com 3c529 EtherLink III (10base2)", 0x627c },
+	{ "3Com 3c529 EtherLink III (10baseT)", 0x627d },
+	{ "3Com 3c529 EtherLink III (test mode)", 0x62db },
+	{ "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
+	{ "3Com 3c529 EtherLink III (TP)", 0x62f7 },
+	{ NULL, 0 },
+};
+#endif
 
 int el3_probe(struct device *dev)
 {
 	short lrs_state = 0xff, i;
-	ushort ioaddr, irq, if_port;
-	short phys_addr[3];
+	int ioaddr, irq, if_port;
+	u16 phys_addr[3];
 	static int current_tag = 0;
+	int mca_slot = -1;
 
 	/* First check all slots of the EISA bus.  The next slot address to
 	   probe is kept in 'eisa_addr' to support multiple probe() calls. */
@@ -182,69 +203,72 @@
 	}
 
 #ifdef CONFIG_MCA
-#define MCA_NUMBER_OF_SLOTS 8
-#define MCA_PORT_POS_SEL 0x096
-#define MCA_PORT_ID_REG_0 0x100
-#define MCA_PORT_ID_REG_1 0x101
-#define MCA_SELECT_BIT 0x08
-	if (MCA_bus) 
-	{
-		u_int mca_id;
-		u_char posreg[4];
-		int mca_slot;
-
-		if (el3_debug > 2)
-			printk("3c529: probing...\n");
-		/* This should probably be done once early on and read into
-		 * a structure somewhere... */
-		for (mca_slot = 0; mca_slot < MCA_NUMBER_OF_SLOTS; mca_slot++)
-		{
-			/* Select MCA slot i */
-			outb_p(mca_slot | MCA_SELECT_BIT, MCA_PORT_POS_SEL); 
-			mca_id = ((inb_p(MCA_PORT_ID_REG_1)<<8) 
-					  + inb_p(MCA_PORT_ID_REG_0));
-			if (mca_id == 0x627C     /* 10base2 */
-				|| mca_id == 0x627D  /* 10baseT */
-				|| mca_id == 0x62DB	 /* Test mode */
-				|| mca_id == 0x62F6	 /* TP or coax */
-				|| mca_id == 0x62F7) /* TP only */
-			{
-				if (el3_debug > 1)
-					printk("3c529: Found with id 0x%x at slot %d\n",
-						 mca_id, mca_slot);
-				posreg[0] = inb_p(0x102);  posreg[1] = inb_p(0x103);
-				posreg[2] = inb_p(0x104);  posreg[3] = inb_p(0x105);
-				break;
+	/* Based on Erik Nygren's (nygren@mit.edu) 3c529 patch, heavily
+	 * modified by Chris Beauregard (cpbeaure@csclub.uwaterloo.ca)
+	 * to support standard MCA probing.
+	 *
+	 * redone for multi-card detection by ZP Gu (zpg@castle.net)
+	 * now works as a module
+	 */
+
+	if( MCA_bus ) {
+		int slot, j;
+		u_char pos4, pos5;
+
+		for( j = 0; el3_mca_adapters[j].name != NULL; j ++ ) {
+			slot = 0;
+			while( slot != MCA_NOTFOUND ) {
+				slot = mca_find_unused_adapter(
+					el3_mca_adapters[j].id, slot );
+				if( slot == MCA_NOTFOUND ) break;
+
+				/* if we get this far, an adapter has been
+				 * detected and is enabled
+				 */
+
+				printk("3c509: found %s at slot %d\n",
+					el3_mca_adapters[j].name, slot + 1 );
+
+				pos4 = mca_read_stored_pos( slot, 4 );
+				pos5 = mca_read_stored_pos( slot, 5 );
+
+				ioaddr = ((short)((pos4&0xfc)|0x02)) << 8;
+				irq = pos5 & 0x0f;
+
+				/* probing for a card at a particular IO/IRQ */
+				if(dev && ((dev->irq >= 1 && dev->irq != irq) ||
+			   	(dev->base_addr >= 1 && dev->base_addr != ioaddr))) {
+					slot++;         /* probing next slot */
+					continue;
+				}
+
+				/* claim the slot */
+				mca_set_adapter_name(slot, el3_mca_adapters[j].name);
+				mca_set_adapter_procfn(slot, NULL, NULL);
+				mca_mark_as_used(slot);
+
+				if_port = pos4 & 0x03;
+				if (el3_debug > 2) {
+					printk("3c529: irq %d  ioaddr 0x%x  ifport %d\n", irq, ioaddr, if_port);
+				}
+				for (i = 0; i < 3; i++) {
+					phys_addr[i] = htons(read_eeprom(ioaddr, i));
+				}
+				
+				mca_slot = slot;
+
+				goto found;
 			}
-			mca_id = 0xFFFF;
-		}
-		/* Read values from POS registers so now disable */
-		outb(0,MCA_PORT_POS_SEL);
-		if (mca_id != 0xFFFF && !(posreg[0]&0x01))
-			printk("3c529: Adapter found but disabled in slot %d\n", mca_slot);
-		else if (mca_id != 0xFFFF && posreg[0]&0x01)
-		{
-			/* Found and adapter is enabled */
-			if (el3_debug > 2)
-				printk("3c529: pos registers 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
-					 posreg[0], posreg[1], posreg[2], posreg[3]);
-			ioaddr = ((short)((posreg[2]&0xfc)|0x02)) << 8;
-			irq = posreg[3] & 0x0f;
-			if_port = posreg[2] & 0x03;
-			if (el3_debug > 2)
-				printk("3c529: irq %d  ioaddr 0x%x  ifport %d\n",
-					irq, ioaddr, if_port);
-			for (i = 0; i < 3; i++)
-				phys_addr[i] = htons(read_eeprom(ioaddr, i));
-			goto found;
 		}
+		/* if we get here, we didn't find an MCA adapter */
+		return -ENODEV;
 	}
 #endif
 	/* Reset the ISA PnP mechanism on 3c509b. */
 	outb(0x02, 0x279);           /* Select PnP config control register. */
 	outb(0x02, 0xA79);           /* Return to WaitForKey state. */
 	/* Select an open I/O location at 0x1*0 to do contention select. */
-	for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
+	for ( ; id_port < 0x200; id_port += 0x10) {
 		if (check_region(id_port, 1))
 			continue;
 		outb(0x00, id_port);
@@ -288,18 +312,23 @@
 	}
 
 	{
-		unsigned short iobase = id_read_eeprom(8);
+		unsigned int iobase = id_read_eeprom(8);
 		if_port = iobase >> 14;
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
 	}
-	if (dev  &&  dev->irq > 1  &&  dev->irq < 16)
-		irq = dev->irq;
-	else
-		irq = id_read_eeprom(9) >> 12;
+	irq = id_read_eeprom(9) >> 12;
 
-	if (dev  &&  dev->base_addr != 0
-		&&  dev->base_addr != (unsigned short)ioaddr) {
-		return -ENODEV;
+	if (dev) {					/* Set passed-in IRQ or I/O Addr. */
+		if (dev->irq > 1  &&  dev->irq < 16)
+			irq = dev->irq;
+
+		if (dev->base_addr) {
+			if (dev->mem_end == 0x3c509 			/* Magic key */
+				&& dev->base_addr >= 0x200  &&  dev->base_addr <= 0x3e0)
+				ioaddr = dev->base_addr & 0x3f0;
+			else if (dev->base_addr != ioaddr)
+				return -ENODEV;
+		}
 	}
 
 	/* Set the adaptor tag so that the next card can be found. */
@@ -342,7 +371,8 @@
 	if (dev->priv == NULL)
 		return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct el3_private));
-
+	
+	((struct el3_private *)dev->priv)->mca_slot = mca_slot;
 	((struct el3_private *)dev->priv)->next_dev = el3_root_dev;
 	el3_root_dev = dev;
 
@@ -364,7 +394,7 @@
 /* Read a word from the EEPROM using the regular EEPROM access register.
    Assume that we are in register window zero.
  */
-static ushort read_eeprom(short ioaddr, int index)
+static ushort read_eeprom(int ioaddr, int index)
 {
 	outw(EEPROM_READ + index, ioaddr + 10);
 	/* Pause for at least 162 us. for the read to take place. */
@@ -394,7 +424,6 @@
 }
 
 
-
 static int
 el3_open(struct device *dev)
 {
@@ -464,7 +493,7 @@
 	/* Ack all pending events, and set active indicator mask. */
 	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
 		 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull,
+	outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
 		 ioaddr + EL3_CMD);
 
 	if (el3_debug > 3)
@@ -589,7 +618,7 @@
 	struct device *dev = (struct device *)dev_id;
 	struct el3_private *lp;
 	int ioaddr, status;
-	int i = INTR_WORK;
+	int i = max_interrupt_work;
 
 	if (dev == NULL) {
 		printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
@@ -624,7 +653,7 @@
 			dev->tbusy = 0;
 			mark_bh(NET_BH);
 		}
-		if (status & (AdapterFailure | RxEarly | StatsFull)) {
+		if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
 			/* Handle all uncommon interrupts. */
 			if (status & StatsFull)				/* Empty statistics. */
 				update_stats(dev);
@@ -632,6 +661,18 @@
 				el3_rx(dev);
 				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
 			}
+			if (status & TxComplete) {			/* Really Tx error. */
+				struct el3_private *lp = (struct el3_private *)dev->priv;
+				short tx_status;
+				int i = 4;
+
+				while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+					if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+					if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
+					if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
+					outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+				}
+			}
 			if (status & AdapterFailure) {
 				/* Adapter failure requires Rx reset and reinit. */
 				outw(RxReset, ioaddr + EL3_CMD);
@@ -730,6 +771,8 @@
 	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
 		if (rx_status & 0x4000) { /* Error, update stats. */
 			short error = rx_status & 0x3800;
+
+			outw(RxDiscard, ioaddr + EL3_CMD);
 			lp->stats.rx_errors++;
 			switch (error) {
 			case 0x0000:		lp->stats.rx_over_errors++; break;
@@ -761,19 +804,21 @@
 					 (pkt_len + 3) >> 2);
 #endif
 
+				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				skb->protocol = eth_type_trans(skb,dev);
 				netif_rx(skb);
-				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				lp->stats.rx_packets++;
 				continue;
-			} else if (el3_debug)
+			}
+			outw(RxDiscard, ioaddr + EL3_CMD);
+			lp->stats.rx_dropped++;
+			if (el3_debug)
 				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
 					   dev->name, pkt_len);
 		}
-		lp->stats.rx_dropped++;
-		outw(RxDiscard, ioaddr + EL3_CMD);
+		inw(ioaddr + EL3_STATUS); 				/* Delay. */
 		while (inw(ioaddr + EL3_STATUS) & 0x1000)
-			printk("	Waiting for 3c509 to discard packet, status %x.\n",
+			printk(KERN_DEBUG "	Waiting for 3c509 to discard packet, status %x.\n",
 				   inw(ioaddr + EL3_STATUS) );
 	}
 
@@ -786,7 +831,7 @@
 static void
 set_multicast_list(struct device *dev)
 {
-	short ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 	if (el3_debug > 1) {
 		static int old = 0;
 		if (old != dev->mc_count) {
@@ -844,7 +889,7 @@
 }
 
 #ifdef MODULE
-/* Parameter that may be passed into the module. */
+/* Parameters that may be passed into the module. */
 static int debug = -1;
 static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
 static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};
@@ -880,7 +925,12 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (el3_root_dev) {
-		next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev;
+		struct el3_private *lp = (struct el3_private *)el3_root_dev->priv;
+#ifdef CONFIG_MCA		
+		if(lp->mca_slot!=-1)
+			mca_mark_as_unused(lp->mca_slot);
+#endif			
+		next_dev = lp->next_dev;
 		unregister_netdev(el3_root_dev);
 		release_region(el3_root_dev->base_addr, EL3_IO_EXTENT);
 		kfree(el3_root_dev);
@@ -888,7 +938,7 @@
 	}
 }
 #endif /* MODULE */
-
+
 /*
  * Local variables:
  *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c"
diff -u --recursive --new-file v2.2.3/linux/drivers/net/3c527.c linux/drivers/net/3c527.c
--- v2.2.3/linux/drivers/net/3c527.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/3c527.c	Sun Mar 21 07:11:36 1999
@@ -0,0 +1,1152 @@
+/* 3c527.c: 3Com Etherlink/MC32 driver for Linux
+ *
+ *	(c) Copyright 1998 Red Hat Software Inc
+ *	Written by Alan Cox.
+ *
+ *	Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
+ *	(for the MCA stuff) written by Wim Dumon.
+ *
+ *	Thanks to 3Com for making this possible by providing me with the
+ *	documentation.
+ *
+ *	This software may be used and distributed according to the terms
+ *	of the GNU Public License, incorporated herein by reference.
+ *
+ */
+
+static const char *version =
+	"3c527.c:v0.04 1999/03/16 Alan Cox (alan@redhat.com)\n";
+
+/*
+ *	Things you need
+ *	o	The databook.
+ *
+ *	Traps for the unwary
+ *
+ *	The diagram (Figure 1-1) and the POS summary disagree with the
+ *	"Interrupt Level" section in the manual.
+ *
+ *	The documentation in places seems to miss things. In actual fact
+ *	I've always eventually found everything is documented, it just
+ *	requires careful study.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/mca.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "3c527.h"
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+static const char* cardname = "3c527";
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int mc32_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define NETCARD_IO_EXTENT	8
+
+
+struct mc32_mailbox
+{
+	u16	mbox __attribute((packed));
+	u16	data[1] __attribute((packed));
+};
+
+/* Information that need to be kept for each board. */
+
+#define TX_RING_MAX	16	/* Typically the card supports 37 */
+#define RX_RING_MAX	32	/*        "     "       "         */
+
+struct mc32_local 
+{
+	struct net_device_stats net_stats;
+	int slot;
+	volatile struct mc32_mailbox *rx_box;
+	volatile struct mc32_mailbox *tx_box;
+	volatile struct mc32_mailbox *exec_box;
+	volatile u16 *stats;
+	u16 tx_chain;
+	u16 rx_chain;
+	u16 tx_len;
+	u16 rx_len;
+	u32 base;
+	u16 rx_halted;
+	u16 tx_halted;
+	u16 exec_pending;
+	u16 mc_reload_wait;	/* a multicast load request is pending */
+	atomic_t tx_count;		/* buffers left */
+	struct wait_queue *event;
+	struct sk_buff *tx_skb[TX_RING_MAX];	/* Transmit ring */
+	u16 tx_skb_top;
+	u16 tx_skb_end;
+	struct sk_buff *rx_skb[RX_RING_MAX];	/* Receive ring */
+	void *rx_ptr[RX_RING_MAX];		/* Data pointers */
+};
+
+/* The station (ethernet) address prefix, used for a sanity check. */
+#define SA_ADDR0 0x02
+#define SA_ADDR1 0x60
+#define SA_ADDR2 0xAC
+
+struct mca_adapters_t {
+	unsigned int	id;
+	char		*name;
+};
+
+const struct mca_adapters_t mc32_adapters[] = {
+	{ 0x0041, "3COM EtherLink MC/32" },
+	{ 0x8EF5, "IBM High Performance Lan Adapter" },
+	{ 0x0000, NULL }
+};
+
+
+/* Index to functions, as function prototypes. */
+
+extern int mc32_probe(struct device *dev);
+
+static int	mc32_probe1(struct device *dev, int ioaddr);
+static int	mc32_open(struct device *dev);
+static int	mc32_send_packet(struct sk_buff *skb, struct device *dev);
+static void	mc32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int	mc32_close(struct device *dev);
+static struct	net_device_stats *mc32_get_stats(struct device *dev);
+static void	mc32_set_multicast_list(struct device *dev);
+
+/*
+ * Check for a network adaptor of this type, and return '0' iff one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+
+__initfunc(int mc32_probe(struct device *dev))
+{
+	static int current_mca_slot = -1;
+	int i;
+	int adapter_found = 0;
+
+	/* Do not check any supplied i/o locations. 
+	   POS registers usually don't fail :) */
+
+	/* MCA cards have POS registers.  
+	   Autodetecting MCA cards is extremely simple. 
+	   Just search for the card. */
+
+	for(i = 0; (mc32_adapters[i].name != NULL) && !adapter_found; i++) {
+		current_mca_slot = 
+			mca_find_unused_adapter(mc32_adapters[i].id, 0);
+
+		if((current_mca_slot != MCA_NOTFOUND) && !adapter_found) {
+			if(!mc32_probe1(dev, current_mca_slot))
+			{
+				mca_set_adapter_name(current_mca_slot, 
+						mc32_adapters[i].name);
+				mca_mark_as_used(current_mca_slot);
+				return 0;
+			}
+			
+		}
+	}
+	return -ENODEV;
+}
+
+/*
+ * This is the real probe routine. Linux has a history of friendly device
+ * probes on the ISA bus. A good device probes avoids doing writes, and
+ * verifies that the correct device exists and functions.
+ */
+__initfunc(static int mc32_probe1(struct device *dev, int slot))
+{
+	static unsigned version_printed = 0;
+	int i;
+	u8 POS;
+	u32 base;
+	struct mc32_local *lp;
+	static u16 mca_io_bases[]={
+		0x7280,0x7290,
+		0x7680,0x7690,
+		0x7A80,0x7A90,
+		0x7E80,0x7E90
+	};
+	static u32 mca_mem_bases[]={
+		0x00C0000,
+		0x00C4000,
+		0x00C8000,
+		0x00CC000,
+		0x00D0000,
+		0x00D4000,
+		0x00D8000,
+		0x00DC000
+	};
+	static char *failures[]={
+		"Processor instruction",
+		"Processor data bus",
+		"Processor data bus",
+		"Processor data bus",
+		"Adapter bus",
+		"ROM checksum",
+		"Base RAM",
+		"Extended RAM",
+		"82586 internal loopback",
+		"82586 initialisation failure",
+		"Adapter list configuration error"
+	};
+	
+	/* Time to play MCA games */
+
+	if (mc32_debug  &&  version_printed++ == 0)
+		printk(KERN_DEBUG "%s", version);
+
+	printk(KERN_INFO "%s: %s found in slot %d:", dev->name, cardname, slot);
+
+	POS = mca_read_stored_pos(slot, 2);
+	
+	if(!(POS&1))
+	{
+		printk(" disabled.\n");
+		return -ENODEV;
+	}
+
+	/* Allocate a new 'dev' if needed. */
+	if (dev == NULL) {
+		/*
+		 * Don't allocate the private data here, it is done later
+		 * This makes it easier to free the memory when this driver
+		 * is used as a module.
+		 */
+		dev = init_etherdev(0, 0);
+		if (dev == NULL)
+			return -ENOMEM;
+	}
+
+	/* Fill in the 'dev' fields. */
+	dev->base_addr = mca_io_bases[(POS>>1)&7];
+	dev->mem_start = mca_mem_bases[(POS>>4)&7];
+	
+	POS = mca_read_stored_pos(slot, 4);
+	if(!(POS&1))
+	{
+		printk("memory window disabled.\n");
+		return -ENODEV;
+	}
+
+	POS = mca_read_stored_pos(slot, 5);
+	
+	i=(POS>>4)&3;
+	if(i==3)
+	{
+		printk("invalid memory window.\n");
+		return -ENODEV;
+	}
+	
+	i*=16384;
+	i+=16384;
+	
+	dev->mem_end=dev->mem_start + i;
+	
+	dev->irq = ((POS>>2)&3)+9;
+	
+	printk("io 0x%3lX irq %d mem 0x%lX (%dK)\n",
+		dev->base_addr, dev->irq, dev->mem_start, i/1024);
+	
+	
+	/* We ought to set the cache line size here.. */
+	
+	
+	/*
+	 *	Go PROM browsing
+	 */
+	 
+	printk("%s: Address ", dev->name);
+	 
+	/* Retrieve and print the ethernet address. */
+	for (i = 0; i < 6; i++)
+	{
+		mca_write_pos(slot, 6, i+12);
+		mca_write_pos(slot, 7, 0);
+	
+		printk(" %2.2x", dev->dev_addr[i] = mca_read_pos(slot,3));
+	}
+
+	mca_write_pos(slot, 6, 0);
+	mca_write_pos(slot, 7, 0);
+
+	POS = mca_read_stored_pos(slot, 4);
+	
+	if(POS&2)
+		printk(" : BNC port selected.\n");
+	else 
+		printk(" : AUI port selected.\n");
+		
+	POS=inb(dev->base_addr+HOST_CTRL);
+	POS|=HOST_CTRL_ATTN|HOST_CTRL_RESET;
+	POS&=~HOST_CTRL_INTE;
+	outb(POS, dev->base_addr+HOST_CTRL);
+	/* Reset adapter */
+	udelay(100);
+	/* Reset off */
+	POS&=~(HOST_CTRL_ATTN|HOST_CTRL_RESET);
+	outb(POS, dev->base_addr+HOST_CTRL);
+	
+	udelay(300);
+	
+	/*
+	 *	Grab the IRQ
+	 */
+
+	if(request_irq(dev->irq, &mc32_interrupt, 0, cardname, dev))
+	{
+		printk("%s: unable to get IRQ %d.\n",
+				   dev->name, dev->irq);
+		return -EAGAIN;
+	}
+
+	/* Initialize the device structure. */
+	if (dev->priv == NULL) {
+		dev->priv = kmalloc(sizeof(struct mc32_local), GFP_KERNEL);
+		if (dev->priv == NULL)
+		{
+			free_irq(dev->irq, dev);
+			return -ENOMEM;
+		}
+	}
+
+	memset(dev->priv, 0, sizeof(struct mc32_local));
+	lp = (struct mc32_local *)dev->priv;
+	lp->slot = slot;
+
+	i=0;
+
+	base = inb(dev->base_addr);
+	
+	while(base==0xFF)
+	{
+		i++;
+		if(i==1000)
+		{
+			printk("%s: failed to boot adapter.\n", dev->name);
+			free_irq(dev->irq, dev);
+			return -ENODEV;
+		}
+		udelay(1000);
+		if(inb(dev->base_addr+2)&(1<<5))
+			base = inb(dev->base_addr);
+	}
+
+	if(base>0)
+	{
+		if(base < 0x0C)
+			printk("%s: %s%s.\n", dev->name, failures[base-1],
+				base<0x0A?" test failure":"");
+		else
+			printk("%s: unknown failure %d.\n", dev->name, base);
+		free_irq(dev->irq, dev);
+		return -ENODEV;
+	}
+	
+	base=0;
+	for(i=0;i<4;i++)
+	{
+		int n=0;
+	
+		while(!(inb(dev->base_addr+2)&(1<<5)))
+		{
+			n++;
+			udelay(50);
+			if(n>100)
+			{
+				printk(KERN_ERR "%s: mailbox read fail (%d).\n", dev->name, i);
+				free_irq(dev->irq, dev);
+				return -ENODEV;
+			}
+		}
+
+		base|=(inb(dev->base_addr)<<(8*i));
+	}
+	
+	lp->exec_box=bus_to_virt(dev->mem_start+base);
+	
+	base=lp->exec_box->data[1]<<16|lp->exec_box->data[0];
+	
+	lp->base = dev->mem_start+base;
+	
+	lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]);
+	lp->tx_box=bus_to_virt(lp->base + lp->exec_box->data[3]);
+	
+	lp->stats = bus_to_virt(lp->base + lp->exec_box->data[5]);
+
+	/*
+	 *	Descriptor chains (card relative)
+	 */
+	 
+	lp->tx_chain 		= lp->exec_box->data[8];
+	lp->rx_chain 		= lp->exec_box->data[10];
+	lp->tx_len 		= lp->exec_box->data[9];
+	lp->rx_len 		= lp->exec_box->data[11];
+	
+	printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
+		dev->name, lp->rx_len, lp->tx_len, lp->base);
+	
+	dev->open		= mc32_open;
+	dev->stop		= mc32_close;
+	dev->hard_start_xmit	= mc32_send_packet;
+	dev->get_stats		= mc32_get_stats;
+	dev->set_multicast_list = mc32_set_multicast_list;
+	
+	lp->rx_halted		= 1;
+	lp->tx_halted		= 1;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+	return 0;
+}
+
+
+/*
+ *	Polled command stuff 
+ */
+ 
+static void mc32_ring_poll(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+}
+
+
+/*
+ *	Send exec commands
+ */
+ 
+static int mc32_command(struct device *dev, u16 cmd, void *data, int len)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	unsigned long flags;
+	
+	while(lp->exec_pending)
+		sleep_on(&lp->event);
+		
+	lp->exec_pending=1;
+	lp->exec_box->mbox=0;
+	lp->exec_box->mbox=cmd;
+	memcpy((void *)lp->exec_box->data, data, len);
+	barrier();	/* the memcpy forgot the volatile so be sure */
+
+	/* Send the command */
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	outb(1<<6, ioaddr+HOST_CMD);	
+	
+	save_flags(flags);
+	cli();
+	while(lp->exec_pending!=2)
+		sleep_on(&lp->event);
+	lp->exec_pending=0;
+	restore_flags(flags);
+	
+	/*
+	 *	A multicast set got blocked - do it now
+	 */
+	 
+	if(lp->mc_reload_wait)
+		mc32_set_multicast_list(dev);
+
+	if(lp->exec_box->data[0]&(1<<13))
+		return -1;
+	return 0;
+}
+
+/*
+ *	RX abort
+ */
+ 
+static void mc32_rx_abort(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	
+	lp->rx_box->mbox=0;
+	outb(3<<3, ioaddr+HOST_CMD);	/* Suspend reception */
+}
+
+ 
+/*
+ *	RX enable
+ */
+ 
+static void mc32_rx_begin(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	
+	lp->rx_box->mbox=0;
+	outb(1<<3, ioaddr+HOST_CMD);	/* GO */
+	mc32_ring_poll(dev);	
+	
+	lp->rx_halted=0;
+}
+
+static void mc32_tx_abort(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	
+	lp->tx_box->mbox=0;
+	outb(3, ioaddr+HOST_CMD);	/* Suspend */
+	
+	/* Ring empty */
+	
+	atomic_set(&lp->tx_count, lp->tx_len);
+	
+	/* Flush */
+	if(lp->tx_skb_top!=lp->tx_skb_end)
+	{
+		int i;
+		if(lp->tx_skb_top<=lp->tx_skb_end)
+		{
+			for(i=lp->tx_skb_top;i<lp->tx_skb_end;i++)
+			{
+				dev_kfree_skb(lp->tx_skb[i]);
+				lp->tx_skb[i]=NULL;
+			}
+		}
+		else
+		{
+			for(i=lp->tx_skb_end;i<TX_RING_MAX;i++)
+			{
+				dev_kfree_skb(lp->tx_skb[i]);
+				lp->tx_skb[i]=NULL;
+			}
+			for(i=0;i<lp->tx_skb_top;i++)
+			{
+				dev_kfree_skb(lp->tx_skb[i]);
+				lp->tx_skb[i]=NULL;
+			}
+		}
+	}
+	lp->tx_skb_top=lp->tx_skb_end=0;
+}
+
+/*
+ *	TX enable
+ */
+ 
+static void mc32_tx_begin(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	
+	lp->tx_box->mbox=0;
+#if 0	
+	outb(5, ioaddr+HOST_CMD);	/* GO */
+	printk("TX=>5\n");
+	mc32_ring_poll(dev);	
+	if(lp->tx_box->mbox&(1<<13))
+		printk("TX begin error!\n");
+#endif		
+	lp->tx_halted=0;
+}
+
+	
+/*
+ *	Load the rx ring
+ */
+ 
+static int mc32_load_rx_ring(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int i;
+	u16 base;
+	volatile struct skb_header *p;
+	
+	base = lp->rx_box->data[0];
+	
+	/* Fix me - should use card size - also fix flush ! */ 
+
+	for(i=0;i<RX_RING_MAX;i++)
+	{
+		lp->rx_skb[i]=alloc_skb(1532, GFP_KERNEL);
+		if(lp->rx_skb[i]==NULL)
+		{
+			for(;i>=0;i--)
+				kfree_skb(lp->rx_skb[i]);
+			return -ENOBUFS;
+		}
+		lp->rx_ptr[i]=lp->rx_skb[i]->data+18;
+		
+		p=bus_to_virt(lp->base+base);
+		p->control=0;
+		p->data = virt_to_bus(lp->rx_ptr[i]);
+		p->status=0;
+		p->length = 1532;
+		base = p->next;
+	}
+	p->control = (1<<6);
+	lp->rx_box->mbox = 0;
+	return 0;
+}	
+
+static void mc32_flush_rx_ring(struct mc32_local *lp)
+{
+	int i;
+	for(i=0;i<RX_RING_MAX;i++)
+		kfree_skb(lp->rx_skb[i]);
+}
+
+static void mc32_flush_tx_ring(struct mc32_local *lp)
+{
+	int i;
+	
+	if(lp->tx_skb_top <= lp->tx_skb_end)
+	{
+		for(i=lp->tx_skb_top;i<lp->tx_skb_end;i++)
+			dev_kfree_skb(lp->tx_skb[i]);
+	}
+	else
+	{
+		for(i=0;i<lp->tx_skb_end;i++)
+			dev_kfree_skb(lp->tx_skb[i]);
+		for(i=lp->tx_skb_top;i<TX_RING_MAX;i++)
+			dev_kfree_skb(lp->tx_skb[i]);
+	}
+}
+ 	
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ */
+
+static int mc32_open(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	u16 zero_word=0;
+	u8 one=1;
+	u8 regs;
+	
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	/*
+	 *	Interrupts enabled
+	 */
+
+	regs=inb(ioaddr+HOST_CTRL);
+	regs|=HOST_CTRL_INTE;
+	outb(regs, ioaddr+HOST_CTRL);
+	
+
+	/*
+	 *	Send the indications on command
+	 */
+
+	mc32_command(dev, 4, &one, 2);
+
+	 	
+	/*
+	 *	Send the command sequence "abort, resume" for RX and TX.
+	 *	The abort cleans up the buffer chains if needed.
+	 */
+
+	mc32_rx_abort(dev);
+	mc32_tx_abort(dev);
+	
+	/* Set Network Address */
+	mc32_command(dev, 1, dev->dev_addr, 6);
+	
+	/* Set the filters */
+	mc32_set_multicast_list(dev);
+	
+	/* Issue the 82586 workaround command - this is for "busy lans",
+	   but basically means for all lans now days - has a performance
+	   cost but best set */
+	   
+	mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */
+	
+	/* Load the ring we just initialised */
+	
+	if(mc32_load_rx_ring(dev))
+	{
+		mc32_close(dev);
+		return -ENOBUFS;
+	}
+	
+	/* And the resume command goes last */
+	
+	mc32_rx_begin(dev);
+	mc32_tx_begin(dev);
+	
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int mc32_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+
+	if (dev->tbusy) {
+		/*
+		 * If we get here, some higher level has decided we are broken.
+		 * There should really be a "kick me" function call instead.
+		 */
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5)
+			return 1;
+		printk(KERN_WARNING "%s: transmit timed out?\n", dev->name);
+		/* Try to restart the adaptor. */
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+	}
+
+	/*
+	 * 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 (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
+	{
+		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+		dev_kfree_skb(skb);
+	}
+	else 
+	{
+		unsigned long flags;
+		
+		u16 tx_head;
+		volatile struct skb_header *p, *np;
+
+		save_flags(flags);
+		cli();
+		
+		if(atomic_read(&lp->tx_count)==0)
+		{
+			dev->tbusy=1;
+			restore_flags(flags);
+			return 1;
+		}
+
+		tx_head = lp->tx_box->data[0];
+		atomic_dec(&lp->tx_count);
+
+		/* We will need this to flush the buffer out */
+		
+		lp->tx_skb[lp->tx_skb_end] = skb;
+		lp->tx_skb_end++;
+		lp->tx_skb_end&=(TX_RING_MAX-1);
+		
+		/* P is the last sending/sent buffer as a pointer */
+		p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
+		
+		/* NP is the buffer we will be loading */
+		np=(struct skb_header *)bus_to_virt(lp->base+p->next);
+		
+		np->control	|= (1<<6);	/* EOL */
+		wmb();
+		
+		np->length	= skb->len;
+		np->data	= virt_to_bus(skb->data);
+		np->status	= 0;
+		np->control	= (1<<7)|(1<<6);	/* EOP EOL */
+		wmb();
+		
+		p->status	= 0;
+		p->control	&= ~(1<<6);
+		
+		dev->tbusy	= 0;			/* Keep feeding me */		
+		
+		lp->tx_box->mbox=0;
+		restore_flags(flags);
+	}
+	return 0;
+}
+
+static void mc32_update_stats(struct device *dev)
+{
+}
+
+
+static void mc32_rx_ring(struct device *dev)
+{
+	struct mc32_local *lp=dev->priv;
+	int ioaddr = dev->base_addr;
+	int x=0;
+	volatile struct skb_header *p;
+	u16 base;
+	u16 top;
+	
+	top = base = lp->rx_box->data[0];
+	do
+	{
+		p=(struct skb_header *)bus_to_virt(base+lp->base);
+		if(!(p->status & (1<<7)))
+			break;
+		if(p->status & (1<<6))
+		{
+			u16 length = p->length;
+			struct sk_buff *skb=dev_alloc_skb(length+2);
+			if(skb!=NULL)
+			{
+				skb_reserve(skb,2);
+				/*printk("Frame at %p\n", bus_to_virt(p->data)); */
+				memcpy(skb_put(skb, length),
+					bus_to_virt(p->data), length);
+				skb->protocol=eth_type_trans(skb,dev);
+				skb->dev=dev;
+				lp->net_stats.rx_packets++;
+				lp->net_stats.rx_bytes+=skb->len;
+				netif_rx(skb);
+			}
+			else
+				lp->net_stats.rx_dropped++;
+		}
+		else
+		{
+			lp->net_stats.rx_errors++;
+			switch(p->status&0x0F)
+			{
+				case 1:
+					lp->net_stats.rx_crc_errors++;break;
+				case 2:
+					lp->net_stats.rx_fifo_errors++;break;
+				case 3:
+					lp->net_stats.rx_frame_errors++;break;
+				case 4:
+					lp->net_stats.rx_missed_errors++;break;
+				case 5:
+					lp->net_stats.rx_length_errors++;break;
+			}
+		}
+		p->length = 1532;
+		p->control &= ~(1<<6);
+		p->status = 0;
+		base = p->next;
+	}
+	while(x++<48);
+	
+	/* 
+	 *	This is curious. It seems the receive stop and receive continue
+	 *	commands race against each other, even though we poll for 
+	 *	command ready to be issued. The delay is hackish but is a workaround
+	 *	while I investigate in depth
+	 */
+	
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	lp->rx_box->mbox=0;
+	lp->rx_box->data[0] = top;
+	outb(1<<3, ioaddr+HOST_CMD);	
+}
+
+
+/*
+ * The typical workload of the driver:
+ *   Handle the network interface interrupts.
+ */
+static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct device *dev = dev_id;
+	struct mc32_local *lp;
+	int ioaddr, status, boguscount = 0;
+	int rx_event = 0;
+	
+	if (dev == NULL) {
+		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
+		return;
+	}
+	dev->interrupt = 1;
+
+	ioaddr = dev->base_addr;
+	lp = (struct mc32_local *)dev->priv;
+
+	/* See whats cooking */
+	
+	while((inb(ioaddr+2)&(1<<5)) && boguscount++<2000)
+	{
+		status=inb(ioaddr+HOST_CMD);
+
+#ifdef DEBUG_IRQ		
+		printk("Status TX%d RX%d EX%d OV%d\n",
+			(status&7), (status>>3)&7, (status>>6)&1,
+			(status>>7)&1);
+#endif
+			
+		switch(status&7)
+		{
+			case 0:
+				break;
+			case 6: /* TX fail */
+				lp->net_stats.tx_errors++;
+			case 2:	/* TX ok */
+				lp->net_stats.tx_packets++;
+				/* Packets are sent in order - this is
+				   basically a FIFO queue of buffers matching
+				   the card ring */
+				lp->net_stats.tx_bytes+=lp->tx_skb[lp->tx_skb_top]->len;
+				dev_kfree_skb(lp->tx_skb[lp->tx_skb_top]);
+				lp->tx_skb[lp->tx_skb_top]=NULL;
+				lp->tx_skb_top++;
+				lp->tx_skb_top&=(TX_RING_MAX-1);
+				atomic_inc(&lp->tx_count);
+				dev->tbusy=0;
+				mark_bh(NET_BH);
+				break;
+			case 3: /* Halt */
+			case 4: /* Abort */
+				lp->tx_halted=1;
+				wake_up(&lp->event);
+				break;
+			case 5:
+				lp->tx_halted=0;
+				wake_up(&lp->event);
+				break;
+			default:
+				printk("%s: strange tx ack %d\n", 
+					dev->name, status&7);
+		}
+		status>>=3;
+		switch(status&7)
+		{
+			case 0:
+				break;
+			case 2:	/* RX */
+				rx_event=1;
+				break;
+			case 3:
+			case 4:
+				lp->rx_halted=1;
+				wake_up(&lp->event);
+				break;
+			case 5:
+				lp->rx_halted=0;
+				wake_up(&lp->event);
+				break;
+			case 6:
+				/* Out of RX buffers stat */
+				/* Must restart */
+				lp->net_stats.rx_dropped++;
+				rx_event = 1; 	/* To restart */
+				break;
+			default:
+				printk("%s: strange rx ack %d\n", 
+					dev->name, status&7);
+			
+		}
+		status>>=3;
+		if(status&1)
+		{
+			/* 0=no 1=yes 2=reply clearing */
+			lp->exec_pending=2;
+			wake_up(&lp->event);
+		}
+		if(status&2)
+		{
+			/*
+			 *	Update the stats as soon as
+			 *	we have it flagged and can 
+			 *	send an immediate reply (CRR set)
+			 */
+			 
+			if(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)
+			{
+				mc32_update_stats(dev);
+				outb(0, ioaddr+HOST_CMD);
+			}
+		}
+	}
+	
+	/*
+	 *	Process and restart the receive ring.
+	 */
+	 
+	if(rx_event)
+		mc32_rx_ring(dev);
+	dev->interrupt = 0;
+	return;
+}
+
+
+/* The inverse routine to mc32_open(). */
+
+static int mc32_close(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	u8 regs;
+	u16 one=1;
+
+	/*
+	 *	Send the indications on command (handy debug check)
+	 */
+
+	mc32_command(dev, 4, &one, 2);
+
+	/* Abort RX and Abort TX */
+	
+	mc32_rx_abort(dev);	
+	mc32_tx_abort(dev);
+	
+	/* Catch any waiting commands */
+	
+	while(lp->exec_pending==1)
+		sleep_on(&lp->event);
+		
+	/* Ok the card is now stopping */	
+	
+	regs=inb(ioaddr+HOST_CTRL);
+	regs&=~HOST_CTRL_INTE;
+	outb(regs, ioaddr+HOST_CTRL);
+
+	mc32_flush_rx_ring(lp);
+	mc32_flush_tx_ring(lp);
+	
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	/* Update the statistics here. */
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+
+static struct net_device_stats *mc32_get_stats(struct device *dev)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	return &lp->net_stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+static void mc32_set_multicast_list(struct device *dev)
+{
+	u16 filt;
+	if (dev->flags&IFF_PROMISC)
+	{
+		/* Enable promiscuous mode */
+		filt = 1;
+		mc32_command(dev, 0, &filt, 2);
+	}
+	else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10)
+	{
+		dev->flags|=IFF_PROMISC;
+		filt = 1;
+		mc32_command(dev, 0, &filt, 2);
+	}
+	else if(dev->mc_count)
+	{
+		unsigned char block[62];
+		unsigned char *bp;
+		struct dev_mc_list *dmc=dev->mc_list;
+		
+		int i;
+		
+		filt = 0;
+		block[1]=0;
+		block[0]=dev->mc_count;
+		bp=block+2;
+		
+		for(i=0;i<dev->mc_count;i++)
+		{
+			memcpy(bp, dmc->dmi_addr, 6);
+			bp+=6;
+			dmc=dmc->next;
+		}
+		mc32_command(dev, 2, block, 2+6*dev->mc_count);
+		mc32_command(dev, 0, &filt, 2);
+	}
+	else 
+	{
+		filt = 0;
+		mc32_command(dev, 0, &filt, 2);
+	}
+}
+
+#ifdef MODULE
+
+static char devicename[9] = { 0, };
+static struct device this_device = {
+	devicename, /* will be inserted by linux/drivers/net/mc32_init.c */
+	0, 0, 0, 0,
+	0, 0,  /* I/O address, IRQ */
+	0, 0, 0, NULL, mc32_probe };
+
+int init_module(void)
+{
+	int result;
+
+	if ((result = register_netdev(&this_device)) != 0)
+		return result;
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int slot;
+	
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	unregister_netdev(&this_device);
+
+	/*
+	 * If we don't do this, we can't re-insmod it later.
+	 * Release irq/dma here, when you have jumpered versions and
+	 * allocate them in mc32_probe1().
+	 */
+	 
+	if (this_device.priv)
+	{
+		struct mc32_local *lp=this_device.priv;
+		slot = lp->slot;
+		mca_mark_as_unused(slot);
+		mca_set_adapter_name(slot, NULL);
+		kfree_s(this_device.priv, sizeof(struct mc32_local));
+	}
+	free_irq(this_device.irq, &this_device);
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.2.3/linux/drivers/net/3c527.h linux/drivers/net/3c527.h
--- v2.2.3/linux/drivers/net/3c527.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/3c527.h	Sun Mar 21 07:11:36 1999
@@ -0,0 +1,40 @@
+/*
+ *	3COM "EtherLink MC/32" Descriptions
+ */
+
+/*
+ *	Registers
+ */
+  
+#define HOST_CMD		0
+
+#define HOST_STATUS		2
+#define		HOST_STATUS_CRR	(1<<6)
+#define		HOST_STATUS_CWR	(1<<5)
+
+#define HOST_CTRL		6
+#define		HOST_CTRL_ATTN	(1<<7)
+#define 	HOST_CTRL_RESET	(1<<6)
+#define 	HOST_CTRL_INTE	(1<<2)
+
+#define HOST_RAMPAGE		8
+
+struct skb_header
+{
+	u8	status __attribute((packed));
+	u8	control __attribute((packed));
+	u16	next __attribute((packed));	/* Do not change! */
+	u16	length __attribute((packed));
+	u32	data __attribute((packed));
+};
+
+#define STATUS_MASK	0x0F
+#define COMPLETED	0x80
+#define COMPLETED_OK	0x40
+#define BUFFER_BUSY	0x20
+
+#define CONTROL_EOP	0x80	/* End Of Packet */
+#define CONTROL_EL	0x40	/* End of List */
+
+
+#define MCA_MC32_ID	0x0041	/* Our MCA ident */
\ No newline at end of file
diff -u --recursive --new-file v2.2.3/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.2.3/linux/drivers/net/Config.in	Wed Jan 20 23:14:05 1999
+++ linux/drivers/net/Config.in	Sun Mar 21 07:11:36 1999
@@ -32,8 +32,8 @@
     fi
   fi
   if [ "$CONFIG_PPC" = "y" ]; then
-    bool 'MACE (Power Mac ethernet) support' CONFIG_MACE
-    bool 'BMAC (G3 ethernet) support' CONFIG_BMAC
+    tristate 'MACE (Power Mac ethernet) support' CONFIG_MACE
+    tristate 'BMAC (G3 ethernet) support' CONFIG_BMAC
   fi
   if [ "$CONFIG_ZORRO" = "y" ]; then
     tristate 'Ariadne support' CONFIG_ARIADNE
@@ -53,6 +53,7 @@
       tristate '3c507 support' CONFIG_EL16
       if [ "$CONFIG_MCA" = "y" ]; then
         tristate '3c523 support' CONFIG_ELMC
+        tristate '3c527 support' CONFIG_ELMC_II
       fi
     fi
     tristate '3c509/3c579 support' CONFIG_EL3
diff -u --recursive --new-file v2.2.3/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.2.3/linux/drivers/net/Makefile	Wed Mar 10 15:29:46 1999
+++ linux/drivers/net/Makefile	Sun Mar 21 07:11:36 1999
@@ -430,6 +430,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SUNBMAC),y)
+L_OBJS += sunbmac.o
+else
+  ifeq ($(CONFIG_SUNBMAC),m)
+  M_OBJS += sunbmac.o
+  endif
+endif
+
 ifeq ($(CONFIG_MYRI_SBUS),y)
 L_OBJS += myri_sbus.o
 else
@@ -478,6 +486,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ELMC_II),y)
+L_OBJS += 3c527.o
+else
+  ifeq ($(CONFIG_ELMC_II),m)
+  M_OBJS += 3c527.o
+  endif
+endif
+
 ifeq ($(CONFIG_EL3),y)
 L_OBJS += 3c509.o
 else
@@ -989,10 +1005,18 @@
 
 ifeq ($(CONFIG_MACE),y)
 L_OBJS += mace.o
+else
+  ifeq ($(CONFIG_MACE),m)
+  M_OBJS += mace.o
+  endif
 endif
 
 ifeq ($(CONFIG_BMAC),y)
 L_OBJS += bmac.o
+else
+  ifeq ($(CONFIG_BMAC),m)
+  M_OBJS += bmac.o
+  endif
 endif
 
 ifeq ($(CONFIG_VENDOR_SANGOMA),y)
diff -u --recursive --new-file v2.2.3/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.2.3/linux/drivers/net/Space.c	Tue Feb 23 15:21:33 1999
+++ linux/drivers/net/Space.c	Mon Mar 15 16:11:30 1999
@@ -87,6 +87,7 @@
 extern int sparc_lance_probe(struct device *);
 extern int happy_meal_probe(struct device *);
 extern int qec_probe(struct device *);
+extern int bigmac_probe(struct device *);
 extern int myri_sbus_probe(struct device *);
 extern int sgiseeq_probe(struct device *);
 extern int atarilance_probe(struct device *);
@@ -246,6 +247,9 @@
 #endif
 #ifdef CONFIG_SUNQE
 	{qec_probe, 0},
+#endif
+#ifdef CONFIG_SUNBMAC
+	{bigmac_probe, 0},
 #endif
 #ifdef CONFIG_MYRI_SBUS
 	{myri_sbus_probe, 0},
diff -u --recursive --new-file v2.2.3/linux/drivers/net/a2065.c linux/drivers/net/a2065.c
--- v2.2.3/linux/drivers/net/a2065.c	Wed Mar 10 15:29:46 1999
+++ linux/drivers/net/a2065.c	Sun Mar 21 07:22:00 1999
@@ -135,6 +135,7 @@
 	struct Linux_SBus_DMA *ledma; /* if set this points to ledma and arch=4m */
 	int burst_sizes;	      /* ledma SBus burst sizes */
 #endif
+	struct timer_list         multicast_timer;
 };
 
 #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
@@ -527,6 +528,7 @@
 
 	dev->start = 0;
 	dev->tbusy = 1;
+	del_timer(&lp->multicast_timer);
 
 	/* Stop the card */
 	ll->rap = LE_CSR0;
@@ -706,12 +708,20 @@
 	volatile struct lance_init_block *ib = lp->init_block;
 	volatile struct lance_regs *ll = lp->ll;
 
-	while (dev->tbusy)
-		schedule();
+	if (!dev->start)
+		return;
+
+	if (dev->tbusy) {
+		mod_timer(&lp->multicast_timer, jiffies + 2);
+		return;
+	}
 	set_bit (0, (void *) &dev->tbusy);
 
-	while (lp->tx_old != lp->tx_new)
-		schedule();
+	if (lp->tx_old != lp->tx_new) {
+		mod_timer(&lp->multicast_timer, jiffies + 4);
+		dev->tbusy = 0;
+		return;
+	}
 
 	ll->rap = LE_CSR0;
 	ll->rdp = LE_C0_STOP;
@@ -726,6 +736,7 @@
 	load_csrs (lp);
 	init_restart_lance (lp);
 	dev->tbusy = 0;
+	mark_bh(NET_BH);
 }
 
 
@@ -795,6 +806,11 @@
 			dev->dma = 0;
 
 			ether_setup(dev);
+			init_timer(&priv->multicast_timer);
+			priv->multicast_timer.data = (unsigned long) dev;
+			priv->multicast_timer.function =
+				(void (*)(unsigned long)) &lance_set_multicast;
+
 			zorro_config_board(key, 0);
 			return(0);
 		}
diff -u --recursive --new-file v2.2.3/linux/drivers/net/bsd_comp.c linux/drivers/net/bsd_comp.c
--- v2.2.3/linux/drivers/net/bsd_comp.c	Sat Nov 29 10:33:19 1997
+++ linux/drivers/net/bsd_comp.c	Wed Mar 10 16:51:35 1999
@@ -70,7 +70,6 @@
 #include <linux/vmalloc.h>
 #include <linux/tty.h>
 #include <linux/errno.h>
-#include <linux/sched.h>	/* to get the struct task_struct */
 #include <linux/string.h>	/* used in new tty drivers */
 #include <linux/signal.h>	/* used in new tty drivers */
 
diff -u --recursive --new-file v2.2.3/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- v2.2.3/linux/drivers/net/cs89x0.c	Sun Nov  8 14:03:00 1998
+++ linux/drivers/net/cs89x0.c	Wed Mar 10 16:51:35 1999
@@ -991,22 +991,26 @@
         0, 0,
         0, 0, 0, NULL, NULL };
 
-int io=0;
-int irq=0;
-#endif
-#ifdef MODULE
-int debug=1;
-char *media="auto";
-char *duplex="f";
+static int io=0;
+static int irq=0;
+static int debug=0;
+static char media[8];
+static int duplex=-1;
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(media, "s");
+MODULE_PARM(duplex, "i");
+
+EXPORT_NO_SYMBOLS;
 
 /*
 * media=t             - specify media type
    or media=2
    or media=aui
    or medai=auto
-* duplex=f            - specify forced half/full/autonegotiate duplex
-   or duplex=h
-   or duplex=auto
+* duplex=0            - specify forced half/full/autonegotiate duplex
 * debug=#             - debug level
 
 
@@ -1044,12 +1048,14 @@
         /* boy, they'd better get these right */
         if (!strcmp(media, "rj45"))
 		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
-	if (!strcmp(media, "aui"))
+	else if (!strcmp(media, "aui"))
 		lp->adapter_cnf = A_CNF_MEDIA_AUI   | A_CNF_AUI;
-	if (!strcmp(media, "bnc"))
+	else if (!strcmp(media, "bnc"))
 		lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2;
+	else
+		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
 
-        if (!strcmp(duplex, "auto"))
+        if (duplex==-1)
 		lp->auto_neg_cnf = AUTO_NEG_ENABLE;
 
         if (io == 0)  {
diff -u --recursive --new-file v2.2.3/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.2.3/linux/drivers/net/ibmtr.c	Mon Jan 25 17:44:34 1999
+++ linux/drivers/net/ibmtr.c	Tue Mar 16 14:21:51 1999
@@ -70,6 +70,9 @@
  *      Changes by Joel Sloan (jjs@c-me.com) :
  *      + disable verbose debug messages by default - to enable verbose
  *	  debugging, edit the IBMTR_DEBUG_MESSAGES define below 
+ *
+ *	Changes by Tim Hockin (thockin@isunix.it.ilstu.edu) :
+ *	+ added spinlocks for SMP sanity (10 March 1999)
  */
 
 /* change the define of IBMTR_DEBUG_MESSAGES to a nonzero value 
@@ -144,6 +147,7 @@
 #include <net/checksum.h>
 
 #include <asm/io.h>
+#include <asm/spinlock.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 
@@ -754,6 +758,9 @@
 {
 	struct tok_info *ti=(struct tok_info *)dev->priv;
 
+	/* init the spinlock */
+	ti->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+
 	if (ti->open_status==CLOSED) tok_init_card(dev);
 
 	if (ti->open_status==IN_PROGRESS) sleep_on(&ti->wait_for_reset);
@@ -806,6 +813,7 @@
 	DPRINTK("Int from tok_driver, dev : %p\n",dev);
 #endif
 	ti  = (struct tok_info *) dev->priv;
+	spin_lock(&(ti->lock));
 
       	/* Disable interrupts till processing is finished */
 	dev->interrupt=1;
@@ -832,6 +840,7 @@
     		if (status == 0xFF)
        		{
 		          DPRINTK("PCMCIA card removed.\n");
+			  spin_unlock(&(ti->lock));
         		  dev->interrupt = 0;
           		  return;
        		}
@@ -840,6 +849,7 @@
       	        if ( readb (ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) == 0xFF)
     	        {
          		 DPRINTK("PCMCIA card removed.\n");
+			 spin_unlock(&(ti->lock));
          		 dev->interrupt = 0;
          		 return;
       		 }
@@ -1167,6 +1177,7 @@
 		DPRINTK("Unexpected interrupt from tr adapter\n");
 
 	}
+	spin_unlock(&(ti->lock));
 }
 
 static void initial_tok_int(struct device *dev)
@@ -1598,12 +1609,19 @@
 	if (test_and_set_bit(0,(void *)&dev->tbusy)!=0)
 		DPRINTK("Transmitter access conflict\n");
 	else {
+		int flags;
+
+		/* lock against other CPUs */
+		spin_lock_irqsave(&(ti->lock), flags);
+
 		/* Save skb; we'll need it when the adapter asks for the data */
 		ti->current_skb=skb;
 		writeb(XMIT_UI_FRAME, ti->srb + offsetof(struct srb_xmit, command));
 		writew(ti->exsap_station_id, ti->srb
 		       +offsetof(struct srb_xmit, station_id));
 		writeb(CMD_IN_SRB, (ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD));
+		spin_unlock_irqrestore(&(ti->lock), flags);
+
 		dev->trans_start=jiffies;
 	}
 
diff -u --recursive --new-file v2.2.3/linux/drivers/net/ibmtr.h linux/drivers/net/ibmtr.h
--- v2.2.3/linux/drivers/net/ibmtr.h	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/ibmtr.h	Tue Mar 16 14:21:52 1999
@@ -217,6 +217,7 @@
 	unsigned char ring_speed;
 	__u32 func_addr;
 	unsigned int retry_count;
+	spinlock_t lock;		/* SMP protection */
 };
 
 /* token ring adapter commands */
diff -u --recursive --new-file v2.2.3/linux/drivers/net/mace.c linux/drivers/net/mace.c
--- v2.2.3/linux/drivers/net/mace.c	Mon Oct  5 13:13:39 1998
+++ linux/drivers/net/mace.c	Wed Mar 10 21:48:46 1999
@@ -4,6 +4,12 @@
  *
  * Copyright (C) 1996 Paul Mackerras.
  */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -16,6 +22,10 @@
 #include <asm/pgtable.h>
 #include "mace.h"
 
+#ifdef MODULE
+static struct device *mace_devs = NULL;
+#endif
+
 #define N_RX_RING	8
 #define N_TX_RING	6
 #define MAX_TX_ACTIVE	1
@@ -71,6 +81,9 @@
 static void mace_rxdma_intr(int irq, void *dev_id, struct pt_regs *regs);
 static void mace_set_timeout(struct device *dev);
 static void mace_tx_timeout(unsigned long data);
+static inline void dbdma_reset(volatile struct dbdma_regs *dma);
+static inline void mace_clean_rings(struct mace_data *mp);
+static void __mace_set_address(struct device *dev, void *addr);
 
 /*
  * If we can't get a skbuff when we need it, we use this area for DMA.
@@ -78,7 +91,7 @@
 static unsigned char dummy_buf[RX_BUFLEN+2];
 
 /* Bit-reverse one byte of an ethernet hardware address. */
-static int
+static inline int
 bitrev(int b)
 {
     int d = 0, i;
@@ -144,7 +157,8 @@
 		dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
 		printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
 	}
-	printk("\n");
+	printk(", chip revision %d.%d\n",
+		in_8(&mp->mace->chipid_hi), in_8(&mp->mace->chipid_lo));
 
 	mp = (struct mace_data *) dev->priv;
 	mp->maccc = ENXMT | ENRCV;
@@ -193,6 +207,21 @@
 	return 0;
 }
 
+static void dbdma_reset(volatile struct dbdma_regs *dma)
+{
+    int i;
+
+    out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
+
+    /*
+     * Yes this looks peculiar, but apparently it needs to be this
+     * way on some machines.
+     */
+    for (i = 200; i > 0; --i)
+	if (ld_le32(&dma->control) & RUN)
+	    udelay(1);
+}
+
 static void mace_reset(struct device *dev)
 {
     struct mace_data *mp = (struct mace_data *) dev->priv;
@@ -203,14 +232,14 @@
     i = 200;
     while (--i) {
 	out_8(&mb->biucc, SWRST);
-	if (mb->biucc & SWRST) {
-	    udelay(20);
+	if (in_8(&mb->biucc) & SWRST) {
+	    udelay(10);
 	    continue;
 	}
 	break;
     }
     if (!i) {
-	printk("mace: cannot reset chip!\n");
+	printk(KERN_ERR "mace: cannot reset chip!\n");
 	return;
     }
 
@@ -218,19 +247,14 @@
     i = in_8(&mb->ir);
     out_8(&mb->maccc, 0);	/* turn off tx, rx */
 
-    mb->biucc = XMTSP_64;
-    mb->utr = RTRD;
-    mb->fifocc = RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST;
-    mb->xmtfc = AUTO_PAD_XMIT;	/* auto-pad short frames */
-    mb->rcvfc = 0;
+    out_8(&mb->biucc, XMTSP_64);
+    out_8(&mb->utr, RTRD);
+    out_8(&mb->fifocc, RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST);
+    out_8(&mb->xmtfc, AUTO_PAD_XMIT); /* auto-pad short frames */
+    out_8(&mb->rcvfc, 0);
 
     /* load up the hardware address */
-    out_8(&mb->iac, ADDRCHG | PHYADDR);
-    while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	;
-    for (i = 0; i < 6; ++i) {
-	out_8(&mb->padr, dev->dev_addr[i]);
-    }
+    __mace_set_address(dev, dev->dev_addr);
 
     /* clear the multicast filter */
     out_8(&mb->iac, ADDRCHG | LOGADDR);
@@ -245,23 +269,30 @@
     out_8(&mb->plscc, PORTSEL_GPSI + ENPLSIO);
 }
 
-static int mace_set_address(struct device *dev, void *addr)
+static void __mace_set_address(struct device *dev, void *addr)
 {
+    volatile struct mace *mb = ((struct mace_data *) dev->priv)->mace;
     unsigned char *p = addr;
-    struct mace_data *mp = (struct mace_data *) dev->priv;
-    volatile struct mace *mb = mp->mace;
     int i;
-    unsigned long flags;
-
-    save_flags(flags); cli();
 
     /* load up the hardware address */
     out_8(&mb->iac, ADDRCHG | PHYADDR);
     while ((in_8(&mb->iac) & ADDRCHG) != 0)
 	;
-    for (i = 0; i < 6; ++i) {
+    for (i = 0; i < 6; ++i)
 	out_8(&mb->padr, dev->dev_addr[i] = p[i]);
-    }
+}
+
+static int mace_set_address(struct device *dev, void *addr)
+{
+    struct mace_data *mp = (struct mace_data *) dev->priv;
+    volatile struct mace *mb = mp->mace;
+    unsigned long flags;
+
+    save_flags(flags); cli();
+
+    __mace_set_address(dev, addr);
+
     out_8(&mb->iac, 0);
     /* note: setting ADDRCHG clears ENRCV */
     out_8(&mb->maccc, mp->maccc);
@@ -285,6 +316,7 @@
     mace_reset(dev);
 
     /* initialize list of sk_buffs for receiving and set up recv dma */
+    mace_clean_rings(mp);
     memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd));
     cp = mp->rx_cmds;
     for (i = 0; i < N_RX_RING - 1; ++i) {
@@ -335,25 +367,17 @@
     out_8(&mb->maccc, mp->maccc);
     /* enable all interrupts except receive interrupts */
     out_8(&mb->imr, RCVINT);
+
+#ifdef MOD_INC_USE_COUNT
+    MOD_INC_USE_COUNT;
+#endif
     return 0;
 }
 
-static int mace_close(struct device *dev)
+static inline void mace_clean_rings(struct mace_data *mp)
 {
-    struct mace_data *mp = (struct mace_data *) dev->priv;
-    volatile struct mace *mb = mp->mace;
-    volatile struct dbdma_regs *rd = mp->rx_dma;
-    volatile struct dbdma_regs *td = mp->tx_dma;
     int i;
 
-    /* disable rx and tx */
-    mb->maccc = 0;
-    mb->imr = 0xff;		/* disable all intrs */
-
-    /* disable rx and tx dma */
-    st_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* clear run bit */
-    st_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* clear run bit */
-
     /* free some skb's */
     for (i = 0; i < N_RX_RING; ++i) {
 	if (mp->rx_bufs[i] != 0) {
@@ -366,6 +390,28 @@
 	if (++i >= N_TX_RING)
 	    i = 0;
     }
+}
+
+static int mace_close(struct device *dev)
+{
+    struct mace_data *mp = (struct mace_data *) dev->priv;
+    volatile struct mace *mb = mp->mace;
+    volatile struct dbdma_regs *rd = mp->rx_dma;
+    volatile struct dbdma_regs *td = mp->tx_dma;
+
+    /* disable rx and tx */
+    out_8(&mb->maccc, 0);
+    out_8(&mb->imr, 0xff);		/* disable all intrs */
+
+    /* disable rx and tx dma */
+    st_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
+    st_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
+
+    mace_clean_rings(mp);
+
+#ifdef MOD_DEC_USE_COUNT
+    MOD_DEC_USE_COUNT;
+#endif
 
     return 0;
 }
@@ -517,10 +563,10 @@
 
     if (intr & MPCO)
 	mp->stats.rx_missed_errors += 256;
-    mp->stats.rx_missed_errors += mb->mpc;	/* reading clears it */
+    mp->stats.rx_missed_errors += in_8(&mb->mpc);   /* reading clears it */
     if (intr & RNTPCO)
 	mp->stats.rx_length_errors += 256;
-    mp->stats.rx_length_errors += mb->rntpc;	/* reading clears it */
+    mp->stats.rx_length_errors += in_8(&mb->rntpc); /* reading clears it */
     if (intr & CERR)
 	++mp->stats.tx_heartbeat_errors;
     if (intr & BABBLE)
@@ -547,7 +593,7 @@
     mace_handle_misc_intrs(mp, intr);
 
     i = mp->tx_empty;
-    while (mb->pr & XMTSV) {
+    while (in_8(&mb->pr) & XMTSV) {
 	del_timer(&mp->tx_timeout);
 	mp->timeout_active = 0;
 	/*
@@ -555,13 +601,13 @@
 	 * word.  This appears to unlatch any error indication from
 	 * the DMA controller.
 	 */
-	intr = mb->ir;
+	intr = in_8(&mb->ir);
 	if (intr != 0)
 	    mace_handle_misc_intrs(mp, intr);
 	if (mp->tx_bad_runt) {
 	    fs = in_8(&mb->xmtfs);
 	    mp->tx_bad_runt = 0;
-	    mb->xmtfc = AUTO_PAD_XMIT; 
+	    out_8(&mb->xmtfc, AUTO_PAD_XMIT);
 	    continue;
 	}
 	dstat = ld_le32(&td->status);
@@ -571,7 +617,7 @@
 	 * xcount is the number of complete frames which have been
 	 * written to the fifo but for which status has not been read.
 	 */
-	xcount = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK;
+	xcount = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK;
 	if (xcount == 0 || (dstat & DEAD)) {
 	    /*
 	     * If a packet was aborted before the DMA controller has
@@ -586,11 +632,15 @@
 	     */
 	    out_8(&mb->xmtfc, DXMTFCS);
 	}
-	fs = mb->xmtfs;
+	fs = in_8(&mb->xmtfs);
 	if ((fs & XMTSV) == 0) {
 	    printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n",
 		   fs, xcount, dstat);
-	    return;
+	    mace_reset(dev);
+		/*
+		 * XXX mace likes to hang the machine after a xmtfs error.
+		 * This is hard to reproduce, reseting *may* help
+		 */
 	}
 	cp = mp->tx_cmds + NCMDS_TX * i;
 	stat = ld_le16(&cp->xfer_status);
@@ -600,7 +650,7 @@
 	     * the transmit FIFO.
 	     */
 	    udelay(1);
-	    x = (mb->fifofc >> XMTFC_SH) & XMTFC_MASK;
+	    x = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK;
 	    if (x != 0) {
 		/* there were two bytes with an end-of-packet indication */
 		mp->tx_bad_runt = 1;
@@ -612,10 +662,10 @@
 		 * We flush the transmit FIFO just in case (by setting the
 		 * XMTFWU bit with the transmitter disabled).
 		 */
-		out_8(&mb->maccc, mb->maccc & ~ENXMT);
-		out_8(&mb->fifocc, mb->fifocc | XMTFWU);
+		out_8(&mb->maccc, in_8(&mb->maccc) & ~ENXMT);
+		out_8(&mb->fifocc, in_8(&mb->fifocc) | XMTFWU);
 		udelay(1);
-		out_8(&mb->maccc, mb->maccc | ENXMT);
+		out_8(&mb->maccc, in_8(&mb->maccc) | ENXMT);
 		out_8(&mb->xmtfc, AUTO_PAD_XMIT);
 	    }
 	}
@@ -632,7 +682,7 @@
 		++mp->stats.tx_carrier_errors;
 	    if (fs & (UFLO|LCOL|RTRY))
 		++mp->stats.tx_aborted_errors;
-	} else 
+	} else
 	    ++mp->stats.tx_packets;
 	dev_kfree_skb(mp->tx_bufs[i]);
 	--mp->tx_active;
@@ -686,19 +736,19 @@
 	goto out;
 
     /* update various counters */
-    mace_handle_misc_intrs(mp, mb->ir);
+    mace_handle_misc_intrs(mp, in_8(&mb->ir));
 
     cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty;
 
     /* turn off both tx and rx and reset the chip */
     out_8(&mb->maccc, 0);
-    out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
     printk(KERN_ERR "mace: transmit timeout - resetting\n");
+    dbdma_reset(td);
     mace_reset(dev);
 
     /* restart rx dma */
     cp = bus_to_virt(ld_le32(&rd->cmdptr));
-    out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+    dbdma_reset(rd);
     out_le16(&cp->xfer_status, 0);
     out_le32(&rd->cmdptr, virt_to_bus(cp));
     out_le32(&rd->control, (RUN << 16) | RUN);
@@ -786,8 +836,8 @@
 		    ++mp->stats.rx_crc_errors;
 	    } else {
 		/* Mace feature AUTO_STRIP_RCV is on by default, dropping the
-		 * FCS on frames with 802.3 headers. This means that Ethernet 
-		 * frames have 8 extra octets at the end, while 802.3 frames 
+		 * FCS on frames with 802.3 headers. This means that Ethernet
+		 * frames have 8 extra octets at the end, while 802.3 frames
 		 * have only 4. We need to correctly account for this. */
 		if (*(unsigned short *)(data+12) < 1536) /* 802.3 header */
 		    nb -= 4;
@@ -846,3 +896,35 @@
 	mp->rx_fill = i;
     }
 }
+
+#ifdef MODULE
+
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("Paul Mackerras");
+MODULE_DESCRIPTION("PowerMac MACE driver.");
+#endif
+
+int init_module(void)
+{
+    int res;
+
+    if(mace_devs != NULL)
+	return -EBUSY;
+    res = mace_probe(NULL);
+    return res;
+}
+
+void cleanup_module(void)
+{
+    struct mace_data *mp = (struct mace_data *) mace_devs->priv;
+    unregister_netdev(mace_devs);
+
+    free_irq(mace_devs->irq, mace_interrupt);
+    free_irq(mp->tx_dma_intr, mace_txdma_intr);
+    free_irq(mp->rx_dma_intr, mace_rxdma_intr);
+
+    kfree(mace_devs);
+    mace_devs = NULL;
+}
+
+#endif
diff -u --recursive --new-file v2.2.3/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.2.3/linux/drivers/net/myri_sbus.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/myri_sbus.c	Mon Mar 15 16:11:30 1999
@@ -278,7 +278,7 @@
 		mp->rx_skbs[i] = skb;
 		skb->dev = dev;
 		skb_put(skb, RX_ALLOC_SIZE);
-		rxd[i].myri_scatters[0].addr = (u32) ((unsigned long)skb->data);
+		rxd[i].myri_scatters[0].addr = sbus_dvma_addr(skb->data);
 		rxd[i].myri_scatters[0].len = RX_ALLOC_SIZE;
 		rxd[i].ctx = i;
 		rxd[i].num_sg = 1;
@@ -340,12 +340,6 @@
 		dev_kfree_skb(skb);
 		mp->tx_skbs[entry] = NULL;
 		mp->enet_stats.tx_packets++;
-
-#ifdef NEED_DMA_SYNCHRONIZATION
-		mmu_sync_dma(((u32)((unsigned long)skb->data)),
-			     skb->len, mp->myri_sbus_dev->my_bus);
-#endif
-
 		entry = NEXT_TX(entry);
 	}
 	mp->tx_old = entry;
@@ -437,8 +431,7 @@
 			drops++;
 			DRX(("DROP "));
 			mp->enet_stats.rx_dropped++;
-			rxd->myri_scatters[0].addr =
-				(u32) ((unsigned long)skb->data);
+			rxd->myri_scatters[0].addr = sbus_dvma_addr(skb->data);
 			rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
 			rxd->ctx = index;
 			rxd->num_sg = 1;
@@ -447,7 +440,7 @@
 		}
 
 #ifdef NEED_DMA_SYNCHRONIZATION
-		mmu_sync_dma(((u32)((unsigned long)skb->data)),
+		mmu_sync_dma(sbus_dvma_addr(skb->data),
 			     skb->len, mp->myri_sbus_dev->my_bus);
 #endif
 
@@ -464,8 +457,7 @@
 			mp->rx_skbs[index] = new_skb;
 			new_skb->dev = dev;
 			skb_put(new_skb, RX_ALLOC_SIZE);
-			rxd->myri_scatters[0].addr =
-				(u32) ((unsigned long)new_skb->data);
+			rxd->myri_scatters[0].addr = sbus_dvma_addr(new_skb->data);
 			rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
 			rxd->ctx = index;
 			rxd->num_sg = 1;
@@ -489,8 +481,7 @@
 
 			/* Reuse original ring buffer. */
 			DRX(("reuse "));
-			rxd->myri_scatters[0].addr =
-				(u32) ((unsigned long)skb->data);
+			rxd->myri_scatters[0].addr = sbus_dvma_addr(skb->data);
 			rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
 			rxd->ctx = index;
 			rxd->num_sg = 1;
@@ -600,6 +591,12 @@
 		return 1;
 	}
 
+
+#ifdef NEED_DMA_SYNCHRONIZATION
+	mmu_sync_dma(sbus_dvma_addr(skb->data),
+		     skb->len, mp->myri_sbus_dev->my_bus);
+#endif
+
 	/* This is just to prevent multiple PIO reads for TX_BUFFS_AVAIL. */
 	head = sq->head;
 	tail = sq->tail;
@@ -628,8 +625,7 @@
 	txd = &sq->myri_txd[entry];
 	mp->tx_skbs[entry] = skb;
 
-	txd->myri_gathers[0].addr =
-		(unsigned int) ((unsigned long)skb->data);
+	txd->myri_gathers[0].addr = sbus_dvma_addr(skb->data);
 	txd->myri_gathers[0].len = len;
 	txd->num_sg = 1;
 	txd->chan = KERNEL_CHANNEL;
diff -u --recursive --new-file v2.2.3/linux/drivers/net/ne2.c linux/drivers/net/ne2.c
--- v2.2.3/linux/drivers/net/ne2.c	Fri Jan  8 22:36:07 1999
+++ linux/drivers/net/ne2.c	Wed Mar 10 16:51:35 1999
@@ -75,7 +75,6 @@
 #include <asm/dma.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/ioport.h>
 #include <linux/mca.h>
 
 #include <linux/netdevice.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.2.3/linux/drivers/net/ppp.c	Tue Feb 23 15:21:33 1999
+++ linux/drivers/net/ppp.c	Wed Mar 10 16:51:35 1999
@@ -61,7 +61,6 @@
 #include <linux/malloc.h>
 #include <linux/tty.h>
 #include <linux/errno.h>
-#include <linux/sched.h>	/* to get the struct task_struct */
 #include <linux/string.h>	/* used in new tty drivers */
 #include <linux/signal.h>	/* used in new tty drivers */
 #include <asm/system.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/net/ppp_deflate.c linux/drivers/net/ppp_deflate.c
--- v2.2.3/linux/drivers/net/ppp_deflate.c	Wed Apr  1 20:11:52 1998
+++ linux/drivers/net/ppp_deflate.c	Wed Mar 10 16:51:35 1999
@@ -43,7 +43,6 @@
 #include <linux/malloc.h>
 #include <linux/vmalloc.h>
 #include <linux/errno.h>
-#include <linux/sched.h>	/* to get the struct task_struct */
 #include <linux/string.h>	/* used in new tty drivers */
 #include <linux/signal.h>	/* used in new tty drivers */
 
diff -u --recursive --new-file v2.2.3/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.2.3/linux/drivers/net/shaper.c	Wed Mar 10 15:29:46 1999
+++ linux/drivers/net/shaper.c	Wed Mar 10 16:51:35 1999
@@ -66,7 +66,6 @@
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/fcntl.h>
-#include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/net/smc9194.c linux/drivers/net/smc9194.c
--- v2.2.3/linux/drivers/net/smc9194.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/smc9194.c	Wed Mar 10 16:51:35 1999
@@ -65,7 +65,6 @@
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
-#include <linux/ioport.h>
 #include <linux/init.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunbmac.c linux/drivers/net/sunbmac.c
--- v2.2.3/linux/drivers/net/sunbmac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/sunbmac.c	Mon Mar 15 16:11:30 1999
@@ -0,0 +1,1557 @@
+/* sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters.
+ *
+ * Copyright (C) 1997, 1998 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+static char *version =
+        "sunbmac.c:v1.1 8/Dec/98 David S. Miller (davem@caipfs.rutgers.edu)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <asm/byteorder.h>
+
+#include <asm/idprom.h>
+#include <asm/sbus.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/auxio.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sunbmac.h"
+
+#undef DEBUG_PROBE
+#undef DEBUG_TX
+#undef DEBUG_IRQ
+
+#ifdef DEBUG_PROBE
+#define DP(x)  printk x
+#else
+#define DP(x)
+#endif
+
+#ifdef DEBUG_TX
+#define DTX(x)  printk x
+#else
+#define DTX(x)
+#endif
+
+#ifdef DEBUG_IRQ
+#define DIRQ(x)  printk x
+#else
+#define DIRQ(x)
+#endif
+
+#ifdef MODULE
+static struct bigmac *root_bigmac_dev = NULL;
+#endif
+
+#define DEFAULT_JAMSIZE    4 /* Toe jam */
+
+#define QEC_RESET_TRIES 200
+
+static inline int qec_global_reset(struct qe_globreg *gregs)
+{
+	int tries = QEC_RESET_TRIES;
+
+	gregs->ctrl = GLOB_CTRL_RESET;
+	while(--tries) {
+		if(gregs->ctrl & GLOB_CTRL_RESET) {
+			udelay(20);
+			continue;
+		}
+		break;
+	}
+	if(tries)
+		return 0;
+	printk("BigMAC: Cannot reset the QEC.\n");
+	return -1;
+}
+
+static void qec_init(struct bigmac *bp)
+{
+	struct qe_globreg *gregs = bp->gregs;
+	struct linux_sbus_device *qec_sdev = bp->qec_sbus_dev;
+	unsigned char bsizes = bp->bigmac_bursts;
+	unsigned int regval;
+
+	/* 64byte bursts do not work at the moment, do
+	 * not even try to enable them.  -DaveM
+	 */
+	if(bsizes & DMA_BURST32)
+		regval = GLOB_CTRL_B32;
+	else
+		regval = GLOB_CTRL_B16;
+	gregs->ctrl = regval | GLOB_CTRL_BMODE;
+
+	gregs->psize = GLOB_PSIZE_2048;
+
+	/* All of memsize is given to bigmac. */
+	gregs->msize = qec_sdev->reg_addrs[1].reg_size;
+
+	/* Half to the transmitter, half to the receiver. */
+	gregs->rsize = gregs->tsize = qec_sdev->reg_addrs[1].reg_size >> 1;
+}
+
+/* XXX auto negotiation on these things might not be pleasant... */
+
+#define TX_RESET_TRIES     32
+#define RX_RESET_TRIES     32
+
+static inline void bigmac_tx_reset(struct BIG_MAC_regs *bregs)
+{
+	int tries = TX_RESET_TRIES;
+
+	bregs->tx_cfg = 0;
+
+	/* The fifo threshold bit is read-only and does
+	 * not clear.  -DaveM
+	 */
+	while((bregs->tx_cfg & ~(BIGMAC_TXCFG_FIFO)) != 0 &&
+	      --tries != 0)
+		udelay(20);
+
+	if(!tries) {
+		printk("BIGMAC: Transmitter will not reset.\n");
+		printk("BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg);
+	}
+}
+
+static inline void bigmac_rx_reset(struct BIG_MAC_regs *bregs)
+{
+	int tries = RX_RESET_TRIES;
+
+	bregs->rx_cfg = 0;
+	while((bregs->rx_cfg) && --tries)
+		udelay(20);
+
+	if(!tries) {
+		printk("BIGMAC: Receiver will not reset.\n");
+		printk("BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg);
+	}
+}
+
+/* Reset the transmitter and receiver. */
+static void bigmac_stop(struct bigmac *bp)
+{
+	bigmac_tx_reset(bp->bregs);
+	bigmac_rx_reset(bp->bregs);
+}
+
+static void bigmac_get_counters(struct bigmac *bp, struct BIG_MAC_regs *bregs)
+{
+	struct enet_statistics *stats = &bp->enet_stats;
+
+	stats->rx_crc_errors += bregs->rcrce_ctr;
+	bregs->rcrce_ctr = 0;
+
+	stats->rx_frame_errors += bregs->unale_ctr;
+	bregs->unale_ctr = 0;
+
+	stats->rx_length_errors += bregs->gle_ctr;
+	bregs->gle_ctr = 0;
+
+	stats->tx_aborted_errors += bregs->ex_ctr;
+
+	stats->collisions += (bregs->ex_ctr + bregs->lt_ctr);
+	bregs->ex_ctr = bregs->lt_ctr = 0;
+}
+
+static inline void bigmac_clean_rings(struct bigmac *bp)
+{
+	int i;
+
+	for(i = 0; i < RX_RING_SIZE; i++) {
+		if(bp->rx_skbs[i] != NULL) {
+			dev_kfree_skb(bp->rx_skbs[i]);
+			bp->rx_skbs[i] = NULL;
+		}
+	}
+
+	for(i = 0; i < TX_RING_SIZE; i++) {
+		if(bp->tx_skbs[i] != NULL) {
+			dev_kfree_skb(bp->tx_skbs[i]);
+			bp->tx_skbs[i] = NULL;
+		}
+	}
+}
+
+static void bigmac_init_rings(struct bigmac *bp, int from_irq)
+{
+	struct bmac_init_block *bb = bp->bmac_block;
+	struct device *dev = bp->dev;
+	int i, gfp_flags = GFP_KERNEL;
+
+	if(from_irq || in_interrupt())
+		gfp_flags = GFP_ATOMIC;
+
+	bp->rx_new = bp->rx_old = bp->tx_new = bp->tx_old = 0;
+
+	/* Free any skippy bufs left around in the rings. */
+	bigmac_clean_rings(bp);
+
+	/* Now get new skippy bufs for the receive ring. */
+	for(i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb;
+
+		skb = big_mac_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags);
+		if(!skb)
+			continue;
+
+		bp->rx_skbs[i] = skb;
+		skb->dev = dev;
+
+		/* Because we reserve afterwards. */
+		skb_put(skb, ETH_FRAME_LEN);
+		skb_reserve(skb, 34);
+
+		bb->be_rxd[i].rx_addr = sbus_dvma_addr(skb->data);
+		bb->be_rxd[i].rx_flags =
+			(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
+	}
+
+	for(i = 0; i < TX_RING_SIZE; i++)
+		bb->be_txd[i].tx_flags = bb->be_txd[i].tx_addr = 0;
+}
+
+#ifndef __sparc_v9__
+static void sun4c_bigmac_init_rings(struct bigmac *bp)
+{
+	struct bmac_init_block *bb = bp->bmac_block;
+	__u32 bbufs_dvma = bp->s4c_buf_dvma;
+	int i;
+
+	bp->rx_new = bp->rx_old = bp->tx_new = bp->tx_old = 0;
+
+	for(i = 0; i < RX_RING_SIZE; i++) {
+		bb->be_rxd[i].rx_addr = bbufs_dvma + bbuf_offset(rx_buf, i);
+		bb->be_rxd[i].rx_flags =
+			(RXD_OWN | (SUN4C_RX_BUFF_SIZE & RXD_LENGTH));
+	}
+
+	for(i = 0; i < TX_RING_SIZE; i++)
+		bb->be_txd[i].tx_flags = bb->be_txd[i].tx_addr = 0;
+}
+#endif
+
+#define MGMT_CLKON  (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB|MGMT_PAL_DCLOCK)
+#define MGMT_CLKOFF (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB)
+
+static inline void idle_transceiver(struct bmac_tcvr *tregs)
+{
+	volatile unsigned int garbage;
+	int i = 20;
+
+	while(i--) {
+		tregs->mgmt_pal = MGMT_CLKOFF;
+		garbage = tregs->mgmt_pal;
+		tregs->mgmt_pal = MGMT_CLKON;
+		garbage = tregs->mgmt_pal;
+	}
+}
+
+static void write_tcvr_bit(struct bigmac *bp, struct bmac_tcvr *tregs, int bit)
+{
+	volatile unsigned int garbage;
+
+	if(bp->tcvr_type == internal) {
+		bit = (bit & 1) << 3;
+		tregs->mgmt_pal = bit | (MGMT_PAL_OENAB | MGMT_PAL_EXT_MDIO);
+		garbage = tregs->mgmt_pal;
+		tregs->mgmt_pal = bit | (MGMT_PAL_OENAB |
+					 MGMT_PAL_EXT_MDIO |
+					 MGMT_PAL_DCLOCK);
+		garbage = tregs->mgmt_pal;
+	} else if(bp->tcvr_type == external) {
+		bit = (bit & 1) << 2;
+		tregs->mgmt_pal = bit | (MGMT_PAL_INT_MDIO | MGMT_PAL_OENAB);
+		garbage = tregs->mgmt_pal;
+		tregs->mgmt_pal = bit | (MGMT_PAL_INT_MDIO |
+					 MGMT_PAL_OENAB |
+					 MGMT_PAL_DCLOCK);
+		garbage = tregs->mgmt_pal;
+	} else {
+		printk("write_tcvr_bit: No transceiver type known!\n");
+	}
+}
+
+static int read_tcvr_bit(struct bigmac *bp, struct bmac_tcvr *tregs)
+{
+	volatile unsigned int garbage;
+	int retval = 0;
+
+	if(bp->tcvr_type == internal) {
+		tregs->mgmt_pal = MGMT_PAL_EXT_MDIO;
+		garbage = tregs->mgmt_pal;
+		tregs->mgmt_pal = MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK;
+		garbage = tregs->mgmt_pal;
+		retval = (tregs->mgmt_pal & MGMT_PAL_INT_MDIO) >> 3;
+	} else if(bp->tcvr_type == external) {
+		tregs->mgmt_pal = MGMT_PAL_INT_MDIO;
+		garbage = tregs->mgmt_pal;
+		tregs->mgmt_pal = MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK;
+		garbage = tregs->mgmt_pal;
+		retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2;
+	} else {
+		printk("read_tcvr_bit: No transceiver type known!\n");
+	}
+	return retval;
+}
+
+static int read_tcvr_bit2(struct bigmac *bp, struct bmac_tcvr *tregs)
+{
+	volatile unsigned int garbage;
+	int retval = 0;
+
+	if(bp->tcvr_type == internal) {
+		tregs->mgmt_pal = MGMT_PAL_EXT_MDIO;
+		garbage = tregs->mgmt_pal;
+		retval = (tregs->mgmt_pal & MGMT_PAL_INT_MDIO) >> 3;
+		tregs->mgmt_pal = (MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK);
+		garbage = tregs->mgmt_pal;
+	} else if(bp->tcvr_type == external) {
+		tregs->mgmt_pal = MGMT_PAL_INT_MDIO;
+		garbage = tregs->mgmt_pal;
+		retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2;
+		tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK);
+		garbage = tregs->mgmt_pal;
+	} else {
+		printk("read_tcvr_bit2: No transceiver type known!\n");
+	}
+	return retval;
+}
+
+static inline void put_tcvr_byte(struct bigmac *bp,
+				 struct bmac_tcvr *tregs,
+				 unsigned int byte)
+{
+	int shift = 4;
+
+	do {
+		write_tcvr_bit(bp, tregs, ((byte >> shift) & 1));
+		shift -= 1;
+	} while (shift >= 0);
+}
+
+static void bigmac_tcvr_write(struct bigmac *bp, struct bmac_tcvr *tregs,
+			      int reg, unsigned short val)
+{
+	int shift;
+
+	reg &= 0xff;
+	val &= 0xffff;
+	switch(bp->tcvr_type) {
+	case internal:
+	case external:
+		break;
+
+	default:
+		printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n");
+		return;
+	};
+
+	idle_transceiver(tregs);
+	write_tcvr_bit(bp, tregs, 0);
+	write_tcvr_bit(bp, tregs, 1);
+	write_tcvr_bit(bp, tregs, 0);
+	write_tcvr_bit(bp, tregs, 1);
+
+	put_tcvr_byte(bp, tregs,
+		      ((bp->tcvr_type == internal) ?
+		       BIGMAC_PHY_INTERNAL : BIGMAC_PHY_EXTERNAL));
+
+	put_tcvr_byte(bp, tregs, reg);
+
+	write_tcvr_bit(bp, tregs, 1);
+	write_tcvr_bit(bp, tregs, 0);
+
+	shift = 15;
+	do {
+		write_tcvr_bit(bp, tregs, (val >> shift) & 1);
+		shift -= 1;
+	} while(shift >= 0);
+}
+
+static unsigned short bigmac_tcvr_read(struct bigmac *bp,
+				       struct bmac_tcvr *tregs,
+				       int reg)
+{
+	unsigned short retval = 0;
+
+	reg &= 0xff;
+	switch(bp->tcvr_type) {
+	case internal:
+	case external:
+		break;
+
+	default:
+		printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n");
+		return 0xffff;
+	};
+
+	idle_transceiver(tregs);
+	write_tcvr_bit(bp, tregs, 0);
+	write_tcvr_bit(bp, tregs, 1);
+	write_tcvr_bit(bp, tregs, 1);
+	write_tcvr_bit(bp, tregs, 0);
+
+	put_tcvr_byte(bp, tregs,
+		      ((bp->tcvr_type == internal) ?
+		       BIGMAC_PHY_INTERNAL : BIGMAC_PHY_EXTERNAL));
+
+	put_tcvr_byte(bp, tregs, reg);
+
+	if(bp->tcvr_type == external) {
+		int shift = 15;
+
+		(void) read_tcvr_bit2(bp, tregs);
+		(void) read_tcvr_bit2(bp, tregs);
+
+		do {
+			int tmp;
+
+			tmp = read_tcvr_bit2(bp, tregs);
+			retval |= ((tmp & 1) << shift);
+			shift -= 1;
+		} while(shift >= 0);
+
+		(void) read_tcvr_bit2(bp, tregs);
+		(void) read_tcvr_bit2(bp, tregs);
+		(void) read_tcvr_bit2(bp, tregs);
+	} else {
+		int shift = 15;
+
+		(void) read_tcvr_bit(bp, tregs);
+		(void) read_tcvr_bit(bp, tregs);
+
+		do {
+			int tmp;
+
+			tmp = read_tcvr_bit(bp, tregs);
+			retval |= ((tmp & 1) << shift);
+			shift -= 1;
+		} while(shift >= 0);
+
+		(void) read_tcvr_bit(bp, tregs);
+		(void) read_tcvr_bit(bp, tregs);
+		(void) read_tcvr_bit(bp, tregs);
+	}
+	return retval;
+}
+
+static void bigmac_tcvr_init(struct bigmac *bp)
+{
+	volatile unsigned int garbage;
+	struct bmac_tcvr *tregs = bp->tregs;
+
+	idle_transceiver(tregs);
+	tregs->mgmt_pal = (MGMT_PAL_INT_MDIO |
+			   MGMT_PAL_EXT_MDIO |
+			   MGMT_PAL_DCLOCK);
+	garbage = tregs->mgmt_pal;
+
+	/* Only the bit for the present transceiver (internal or
+	 * external) will stick, set them both and see what stays.
+	 */
+	tregs->mgmt_pal = (MGMT_PAL_INT_MDIO |
+			   MGMT_PAL_EXT_MDIO);
+	garbage = tregs->mgmt_pal;
+	udelay(20);
+
+	if(tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) {
+		bp->tcvr_type = external;
+		tregs->tcvr_pal = ~(TCVR_PAL_EXTLBACK |
+				    TCVR_PAL_MSENSE |
+				    TCVR_PAL_LTENABLE);
+		garbage = tregs->tcvr_pal;
+	} else if(tregs->mgmt_pal & MGMT_PAL_INT_MDIO) {
+		bp->tcvr_type = internal;
+		tregs->tcvr_pal = ~(TCVR_PAL_SERIAL |
+				    TCVR_PAL_EXTLBACK |
+				    TCVR_PAL_MSENSE |
+				    TCVR_PAL_LTENABLE);
+		garbage = tregs->tcvr_pal;
+	} else {
+		printk("BIGMAC: AIEEE, neither internal nor "
+		       "external MDIO available!\n");
+		printk("BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n",
+		       tregs->mgmt_pal, tregs->tcvr_pal);
+	}
+}
+
+static int bigmac_init(struct bigmac *, int);
+
+static int try_next_permutation(struct bigmac *bp, struct bmac_tcvr *tregs)
+{
+	if(bp->sw_bmcr & BMCR_SPEED100) {
+		int timeout;
+
+		/* Reset the PHY. */
+		bp->sw_bmcr	= (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK);
+		bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+		bp->sw_bmcr	= (BMCR_RESET);
+		bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+
+		timeout = 64;
+		while(--timeout) {
+			bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+			if((bp->sw_bmcr & BMCR_RESET) == 0)
+				break;
+			udelay(20);
+		}
+		if(timeout == 0)
+			printk("%s: PHY reset failed.\n", bp->dev->name);
+
+		bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+
+		/* Now we try 10baseT. */
+		bp->sw_bmcr &= ~(BMCR_SPEED100);
+		bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+		return 0;
+	}
+
+	/* We've tried them all. */
+	return -1;
+}
+
+static void bigmac_timer(unsigned long data)
+{
+	struct bigmac *bp = (struct bigmac *) data;
+	struct bmac_tcvr *tregs = bp->tregs;
+	int restart_timer = 0;
+
+	bp->timer_ticks++;
+	if(bp->timer_state == ltrywait) {
+		bp->sw_bmsr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR);
+		bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+		if(bp->sw_bmsr & BMSR_LSTATUS) {
+			printk("%s: Link is now up at %s.\n",
+			       bp->dev->name,
+			       (bp->sw_bmcr & BMCR_SPEED100) ?
+			       "100baseT" : "10baseT");
+			bp->timer_state = asleep;
+			restart_timer = 0;
+		} else {
+			if(bp->timer_ticks >= 4) {
+				int ret;
+
+				ret = try_next_permutation(bp, tregs);
+				if(ret == -1) {
+					printk("%s: Link down, cable problem?\n",
+					       bp->dev->name);
+					ret = bigmac_init(bp, 0);
+					if(ret) {
+						printk("%s: Error, cannot re-init the "
+						       "BigMAC.\n", bp->dev->name);
+					}
+					return;
+				}
+				bp->timer_ticks = 0;
+				restart_timer = 1;
+			} else {
+				restart_timer = 1;
+			}
+		}
+	} else {
+		/* Can't happens.... */
+		printk("%s: Aieee, link timer is asleep but we got one anyways!\n",
+		       bp->dev->name);
+		restart_timer = 0;
+		bp->timer_ticks = 0;
+		bp->timer_state = asleep; /* foo on you */
+	}
+
+	if(restart_timer != 0) {
+		bp->bigmac_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */
+		add_timer(&bp->bigmac_timer);
+	}
+}
+
+/* Well, really we just force the chip into 100baseT then
+ * 10baseT, each time checking for a link status.
+ */
+static void bigmac_begin_auto_negotiation(struct bigmac *bp)
+{
+	struct bmac_tcvr *tregs = bp->tregs;
+	int timeout;
+
+	/* Grab new software copies of PHY registers. */
+	bp->sw_bmsr	= bigmac_tcvr_read(bp, tregs, BIGMAC_BMSR);
+	bp->sw_bmcr	= bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+
+	/* Reset the PHY. */
+	bp->sw_bmcr	= (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK);
+	bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+	bp->sw_bmcr	= (BMCR_RESET);
+	bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+
+	timeout = 64;
+	while(--timeout) {
+		bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+		if((bp->sw_bmcr & BMCR_RESET) == 0)
+			break;
+		udelay(20);
+	}
+	if(timeout == 0)
+		printk("%s: PHY reset failed.\n", bp->dev->name);
+
+	bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR);
+
+	/* First we try 100baseT. */
+	bp->sw_bmcr |= BMCR_SPEED100;
+	bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr);
+
+	bp->timer_state = ltrywait;
+	bp->timer_ticks = 0;
+	bp->bigmac_timer.expires = jiffies + (12 * HZ) / 10;
+	bp->bigmac_timer.data = (unsigned long) bp;
+	bp->bigmac_timer.function = &bigmac_timer;
+	add_timer(&bp->bigmac_timer);
+}
+
+static int bigmac_init(struct bigmac *bp, int from_irq)
+{
+	struct qe_globreg    *gregs        = bp->gregs;
+	struct qe_creg       *cregs        = bp->creg;
+	struct BIG_MAC_regs  *bregs        = bp->bregs;
+	unsigned char *e = &bp->dev->dev_addr[0];
+
+	/* Latch current counters into statistics. */
+	bigmac_get_counters(bp, bregs);
+
+	/* Reset QEC. */
+	qec_global_reset(gregs);
+
+	/* Init QEC. */
+	qec_init(bp);
+
+	/* Alloc and reset the tx/rx descriptor chains. */
+#ifndef __sparc_v9__
+	if(sparc_cpu_model == sun4c)
+		sun4c_bigmac_init_rings(bp);
+	else
+#endif
+		bigmac_init_rings(bp, from_irq);
+
+	/* Initialize the PHY. */
+	bigmac_tcvr_init(bp);
+
+	/* Stop transmitter and receiver. */
+	bigmac_stop(bp);
+
+	/* Set hardware ethernet address. */
+	bregs->mac_addr2 = ((e[4] << 8) | e[5]);
+	bregs->mac_addr1 = ((e[2] << 8) | e[3]);
+	bregs->mac_addr0 = ((e[0] << 8) | e[1]);
+
+	/* Clear the hash table until mc upload occurs. */
+	bregs->htable3 = 0;
+	bregs->htable2 = 0;
+	bregs->htable1 = 0;
+	bregs->htable0 = 0;
+
+	/* Enable Big Mac hash table filter. */
+	bregs->rx_cfg = (BIGMAC_RXCFG_HENABLE | BIGMAC_RXCFG_FIFO);
+
+	udelay(20);
+
+	/* Ok, configure the Big Mac transmitter. */
+	bregs->tx_cfg = BIGMAC_TXCFG_FIFO;
+
+	/* The HME docs recommend to use the 10LSB of our MAC here. */
+	bregs->rand_seed = ((e[5] | e[4] << 8) & 0x3ff);
+
+	/* Enable the output drivers no matter what. */
+	bregs->xif_cfg = (BIGMAC_XCFG_ODENABLE | BIGMAC_XCFG_RESV);
+
+	/* Tell the QEC where the ring descriptors are. */
+	cregs->rxds = bp->bblock_dvma + bib_offset(be_rxd, 0);
+	cregs->txds = bp->bblock_dvma + bib_offset(be_txd, 0);
+
+	/* Setup the FIFO pointers into QEC local memory. */
+	cregs->rxwbufptr = cregs->rxrbufptr = 0;
+	cregs->txwbufptr = cregs->txrbufptr = gregs->rsize;
+
+	/* Tell bigmac what interrupts we don't want to hear about. */
+	bregs->imask = (BIGMAC_IMASK_GOTFRAME | BIGMAC_IMASK_SENTFRAME);
+
+	/* Enable the various other irq's. */
+	cregs->rimask = 0;
+	cregs->timask = 0;
+	cregs->qmask = 0;
+	cregs->bmask = 0;
+
+	/* Set jam size to a reasonable default. */
+	bregs->jsize     = DEFAULT_JAMSIZE;
+
+	/* Clear collision counter. */
+	cregs->ccnt = 0;
+
+	/* Enable transmitter and receiver. */
+	bregs->tx_cfg |= BIGMAC_TXCFG_ENABLE;
+	bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE;
+
+	/* Ok, start detecting link speed/duplex. */
+	bigmac_begin_auto_negotiation(bp);
+
+	/* Success. */
+	return 0;
+}
+
+/* Error interrupts get sent here. */
+static void bigmac_is_medium_rare(struct bigmac *bp,
+				  unsigned int qec_status,
+				  unsigned int bmac_status)
+{
+	printk("bigmac_is_medium_rare: ");
+	if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) {
+		if(qec_status & GLOB_STAT_ER)
+			printk("QEC_ERROR, ");
+		if(qec_status & GLOB_STAT_BM)
+			printk("QEC_BMAC_ERROR, ");
+	}
+	if(bmac_status & CREG_STAT_ERRORS) {
+		if(bmac_status & CREG_STAT_BERROR)
+			printk("BMAC_ERROR, ");
+		if(bmac_status & CREG_STAT_TXDERROR)
+			printk("TXD_ERROR, ");
+		if(bmac_status & CREG_STAT_TXLERR)
+			printk("TX_LATE_ERROR, ");
+		if(bmac_status & CREG_STAT_TXPERR)
+			printk("TX_PARITY_ERROR, ");
+		if(bmac_status & CREG_STAT_TXSERR)
+			printk("TX_SBUS_ERROR, ");
+
+		if(bmac_status & CREG_STAT_RXDROP)
+			printk("RX_DROP_ERROR, ");
+
+		if(bmac_status & CREG_STAT_RXSMALL)
+			printk("RX_SMALL_ERROR, ");
+		if(bmac_status & CREG_STAT_RXLERR)
+			printk("RX_LATE_ERROR, ");
+		if(bmac_status & CREG_STAT_RXPERR)
+			printk("RX_PARITY_ERROR, ");
+		if(bmac_status & CREG_STAT_RXSERR)
+			printk("RX_SBUS_ERROR, ");
+	}
+
+	printk(" RESET\n");
+	bigmac_init(bp, 1);
+}
+
+/* BigMAC transmit complete service routines. */
+static inline void bigmac_tx(struct bigmac *bp)
+{
+	struct be_txd *txbase = &bp->bmac_block->be_txd[0];
+	struct be_txd *this;
+	int elem = bp->tx_old;
+
+	DTX(("bigmac_tx: tx_old[%d] ", elem));
+	while(elem != bp->tx_new) {
+		struct sk_buff *skb;
+
+		this = &txbase[elem];
+
+		DTX(("this(%p) [flags(%08x)addr(%08x)]",
+		     this, this->tx_flags, this->tx_addr));
+
+		if(this->tx_flags & TXD_OWN)
+			break;
+		skb = bp->tx_skbs[elem];
+		DTX(("skb(%p) ", skb));
+		bp->tx_skbs[elem] = NULL;
+		dev_kfree_skb(skb);
+
+		bp->enet_stats.tx_packets++;
+		elem = NEXT_TX(elem);
+	}
+	DTX((" DONE, tx_old=%d\n", elem));
+	bp->tx_old = elem;
+}
+
+#ifndef __sparc_v9__
+static inline void sun4c_bigmac_tx(struct bigmac *bp)
+{
+	struct be_txd *txbase = &bp->bmac_block->be_txd[0];
+	struct be_txd *this;
+	int elem = bp->tx_old;
+
+	while(elem != bp->tx_new) {
+		this = &txbase[elem];
+		if(this->tx_flags & TXD_OWN)
+			break;
+		bp->enet_stats.tx_packets++;
+		elem = NEXT_TX(elem);
+	}
+	bp->tx_old = elem;
+}
+#endif
+
+/* BigMAC receive complete service routines. */
+static inline void bigmac_rx(struct bigmac *bp)
+{
+	struct be_rxd *rxbase = &bp->bmac_block->be_rxd[0];
+	struct be_rxd *this;
+	int elem = bp->rx_new, drops = 0;
+
+	this = &rxbase[elem];
+	while(!(this->rx_flags & RXD_OWN)) {
+		struct sk_buff *skb;
+		unsigned int flags = this->rx_flags;
+		int len = (flags & RXD_LENGTH); /* FCS not included */
+
+		/* Check for errors. */
+		if(len < ETH_ZLEN) {
+			bp->enet_stats.rx_errors++;
+			bp->enet_stats.rx_length_errors++;
+
+	drop_it:
+			/* Return it to the BigMAC. */
+			bp->enet_stats.rx_dropped++;
+			this->rx_addr = sbus_dvma_addr(bp->rx_skbs[elem]->data);
+			this->rx_flags =
+				(RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH));
+			goto next;
+		}
+		skb = bp->rx_skbs[elem];
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef __sparc_v9__
+		if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) {
+			printk("sunbmac: Bogus DMA buffer address "
+			       "[%016lx]\n", ((unsigned long) skb->data));
+			panic("DMA address too large, tell DaveM");
+		}
+#endif
+		mmu_sync_dma(sbus_dvma_addr(skb->data),
+			     skb->len, bp->bigmac_sbus_dev->my_bus);
+#endif
+		if(len > RX_COPY_THRESHOLD) {
+			struct sk_buff *new_skb;
+
+			/* Now refill the entry, if we can. */
+			new_skb = big_mac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			if(!new_skb) {
+				drops++;
+				goto drop_it;
+			}
+			bp->rx_skbs[elem] = new_skb;
+			new_skb->dev = bp->dev;
+			skb_put(new_skb, ETH_FRAME_LEN);
+			skb_reserve(new_skb, 34);
+			rxbase[elem].rx_addr = sbus_dvma_addr(new_skb->data);
+			rxbase[elem].rx_flags =
+				(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
+
+			/* Trim the original skb for the netif. */
+			skb_trim(skb, len);
+		} else {
+			struct sk_buff *copy_skb = dev_alloc_skb(len + 2);
+
+			if(!copy_skb) {
+				drops++;
+				goto drop_it;
+			}
+			copy_skb->dev = bp->dev;
+			skb_reserve(copy_skb, 2);
+			skb_put(copy_skb, len);
+			eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0);
+
+			/* Reuse otiginal ring buffer. */
+			rxbase[elem].rx_addr = sbus_dvma_addr(skb->data);
+			rxbase[elem].rx_flags =
+				(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
+
+			skb = copy_skb;
+		}
+
+		/* No checksums done by the BigMAC ;-( */
+		skb->protocol = eth_type_trans(skb, bp->dev);
+		netif_rx(skb);
+		bp->enet_stats.rx_packets++;
+	next:
+		elem = NEXT_RX(elem);
+		this = &rxbase[elem];
+	}
+	bp->rx_new = elem;
+	if(drops)
+		printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name);
+}
+
+#ifndef __sparc_v9__
+static inline void sun4c_bigmac_rx(struct bigmac *bp)
+{
+	struct be_rxd *rxbase = &bp->bmac_block->be_rxd[0];
+	struct be_rxd *this;
+	struct bigmac_buffers *bbufs = bp->sun4c_buffers;
+	__u32 bbufs_dvma = bp->s4c_buf_dvma;
+	int elem = bp->rx_new, drops = 0;
+
+	this = &rxbase[elem];
+	while(!(this->rx_flags & RXD_OWN)) {
+		struct sk_buff *skb;
+		unsigned char *this_bbuf =
+			bbufs->rx_buf[elem & (SUN4C_RX_RING_SIZE - 1)];
+		__u32 this_bbuf_dvma = bbufs_dvma +
+			bbuf_offset(rx_buf, (elem & (SUN4C_RX_RING_SIZE - 1)));
+		struct be_rxd *end_rxd =
+			&rxbase[(elem+SUN4C_RX_RING_SIZE)&(RX_RING_SIZE-1)];
+		unsigned int flags = this->rx_flags;
+		int len = (flags & RXD_LENGTH) - 4; /* FCS not included */
+
+		/* Check for errors. */
+		if(len < ETH_ZLEN) {
+			bp->enet_stats.rx_errors++;
+			bp->enet_stats.rx_length_errors++;
+			bp->enet_stats.rx_dropped++;
+		} else {
+			skb = dev_alloc_skb(len + 2);
+			if(skb == 0) {
+				drops++;
+				bp->enet_stats.rx_dropped++;
+			} else {
+				skb->dev = bp->dev;
+				skb_reserve(skb, 2);
+				skb_put(skb, len);
+				eth_copy_and_sum(skb, (unsigned char *)this_bbuf,
+						 len, 0);
+				skb->protocol = eth_type_trans(skb, bp->dev);
+				netif_rx(skb);
+				bp->enet_stats.rx_packets++;
+			}
+		}
+		end_rxd->rx_addr = this_bbuf_dvma;
+		end_rxd->rx_flags = (RXD_OWN | (SUN4C_RX_BUFF_SIZE & RXD_LENGTH));
+		
+		elem = NEXT_RX(elem);
+		this = &rxbase[elem];
+	}
+	bp->rx_new = elem;
+	if(drops)
+		printk("%s: Memory squeeze, deferring packet.\n", bp->dev->name);
+}
+#endif
+
+static void bigmac_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct bigmac *bp = (struct bigmac *) dev_id;
+	unsigned int qec_status, bmac_status;
+
+	DIRQ(("bigmac_interrupt: "));
+
+	/* Latch status registers now. */
+	bmac_status = bp->creg->stat;
+	qec_status = bp->gregs->stat;
+
+	bp->dev->interrupt = 1;
+
+	DIRQ(("qec_status=%08x bmac_status=%08x\n", qec_status, bmac_status));
+	if((qec_status & (GLOB_STAT_ER | GLOB_STAT_BM)) ||
+	   (bmac_status & CREG_STAT_ERRORS))
+		bigmac_is_medium_rare(bp, qec_status, bmac_status);
+
+	if(bmac_status & CREG_STAT_TXIRQ)
+		bigmac_tx(bp);
+
+	if(bmac_status & CREG_STAT_RXIRQ)
+		bigmac_rx(bp);
+
+	if(bp->dev->tbusy && (TX_BUFFS_AVAIL(bp) >= 0)) {
+		bp->dev->tbusy = 0;
+		mark_bh(NET_BH);
+	}
+
+	bp->dev->interrupt = 0;
+}
+
+#ifndef __sparc_v9__
+static void sun4c_bigmac_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct bigmac *bp = (struct bigmac *) dev_id;
+	unsigned int qec_status, bmac_status;
+
+	/* Latch status registers now. */
+	bmac_status = bp->creg->stat;
+	qec_status = bp->gregs->stat;
+
+	bp->dev->interrupt = 1;
+
+	if(qec_status & (GLOB_STAT_ER | GLOB_STAT_BM) ||
+	   (bmac_status & CREG_STAT_ERRORS))
+		bigmac_is_medium_rare(bp, qec_status, bmac_status);
+
+	if(bmac_status & CREG_STAT_TXIRQ)
+		sun4c_bigmac_tx(bp);
+
+	if(bmac_status & CREG_STAT_RXIRQ)
+		sun4c_bigmac_rx(bp);
+
+	if(bp->dev->tbusy && (SUN4C_TX_BUFFS_AVAIL(bp) >= 0)) {
+		bp->dev->tbusy = 0;
+		mark_bh(NET_BH);
+	}
+
+	bp->dev->interrupt = 0;
+}
+#endif
+
+static int bigmac_open(struct device *dev)
+{
+	struct bigmac *bp = (struct bigmac *) dev->priv;
+	int res;
+
+#ifndef __sparc_v9__
+	if(sparc_cpu_model == sun4c) {
+		if(request_irq(dev->irq, &sun4c_bigmac_interrupt,
+			       SA_SHIRQ, "BIG MAC", (void *) bp)) {
+			printk("BIGMAC: Can't order irq %d to go.\n", dev->irq);
+			return -EAGAIN;
+		}
+	} else
+#endif
+	if(request_irq(dev->irq, &bigmac_interrupt,
+		       SA_SHIRQ, "BIG MAC", (void *) bp)) {
+		printk("BIGMAC: Can't order irq %d to go.\n", dev->irq);
+		return -EAGAIN;
+	}
+	init_timer(&bp->bigmac_timer);
+	res = bigmac_init(bp, 0);
+	if(!res) {
+		MOD_INC_USE_COUNT;
+	}
+	return res;
+}
+
+static int bigmac_close(struct device *dev)
+{
+	struct bigmac *bp = (struct bigmac *) dev->priv;
+
+	del_timer(&bp->bigmac_timer);
+	bp->timer_state = asleep;
+	bp->timer_ticks = 0;
+
+	bigmac_stop(bp);
+	bigmac_clean_rings(bp);
+	free_irq(dev->irq, (void *)bp);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Put a packet on the wire. */
+static int bigmac_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct bigmac *bp = (struct bigmac *) dev->priv;
+	int len, entry;
+
+	if(dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+	    
+		if (tickssofar < 40) {
+			return 1;
+		} else {
+			printk ("%s: transmit timed out, resetting\n", dev->name);
+			bp->enet_stats.tx_errors++;
+			bigmac_init(bp, 0);
+			dev->tbusy = 0;
+			dev->trans_start = jiffies;
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	if(!TX_BUFFS_AVAIL(bp))
+		return 1;
+
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef __sparc_v9__
+	if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) {
+		struct sk_buff *new_skb = skb_copy(skb, GFP_DMA | GFP_ATOMIC);
+		if(!new_skb)
+			return 1;
+		dev_kfree_skb(skb);
+		skb = new_skb;
+	}
+#endif
+	mmu_sync_dma(sbus_dvma_addr(skb->data),
+		     skb->len, bp->bigmac_sbus_dev->my_bus);
+#endif
+	len = skb->len;
+	entry = bp->tx_new;
+	DTX(("bigmac_start_xmit: len(%d) entry(%d)\n", len, entry));
+
+	/* Avoid a race... */
+	bp->bmac_block->be_txd[entry].tx_flags = TXD_UPDATE;
+
+	bp->tx_skbs[entry] = skb;
+	bp->bmac_block->be_txd[entry].tx_addr = sbus_dvma_addr(skb->data);
+	bp->bmac_block->be_txd[entry].tx_flags =
+		(TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH));
+	dev->trans_start = jiffies;
+	bp->tx_new = NEXT_TX(entry);
+
+	/* Get it going. */
+	bp->creg->ctrl = CREG_CTRL_TWAKEUP;
+
+	if(TX_BUFFS_AVAIL(bp))
+		dev->tbusy = 0;
+
+	return 0;
+}
+
+#ifndef __sparc_v9__
+static int sun4c_bigmac_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct bigmac *bp = (struct bigmac *) dev->priv;
+	struct bigmac_buffers *bbufs = bp->sun4c_buffers;
+	__u32 txbuf_dvma, bbufs_dvma = bp->s4c_buf_dvma;
+	unsigned char *txbuf;
+	int len, entry;
+
+	if(dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+	    
+		if (tickssofar < 40) {
+			return 1;
+		} else {
+			printk ("%s: transmit timed out, resetting\n", dev->name);
+			bp->enet_stats.tx_errors++;
+			bigmac_init(bp, 0);
+			dev->tbusy = 0;
+			dev->trans_start = jiffies;
+			return 0;
+		}
+	}
+
+	if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	if(!SUN4C_TX_BUFFS_AVAIL(bp))
+		return 1;
+
+	len = skb->len;
+	entry = bp->tx_new;
+
+	txbuf = &bbufs->tx_buf[entry][0];
+	txbuf_dvma = bbufs_dvma + bbuf_offset(tx_buf, entry);
+	memcpy(txbuf, skb->data, len);
+
+	/* Avoid a race... */
+	bp->bmac_block->be_txd[entry].tx_flags = TXD_UPDATE;
+
+	bp->bmac_block->be_txd[entry].tx_addr = txbuf_dvma;
+	bp->bmac_block->be_txd[entry].tx_flags =
+		(TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH));
+	bp->tx_new = NEXT_TX(entry);
+
+	/* Get it going. */
+	dev->trans_start = jiffies;
+	bp->creg->ctrl = CREG_CTRL_TWAKEUP;
+
+	dev_kfree_skb(skb);
+
+	if(SUN4C_TX_BUFFS_AVAIL(bp))
+		dev->tbusy = 0;
+
+	return 0;
+}
+#endif
+
+static struct enet_statistics *bigmac_get_stats(struct device *dev)
+{
+	struct bigmac *bp = (struct bigmac *) dev->priv;
+
+	bigmac_get_counters(bp, bp->bregs);
+	return &bp->enet_stats;
+}
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
+
+static void bigmac_set_multicast(struct device *dev)
+{
+	struct bigmac *bp = (struct bigmac *) dev->priv;
+	struct BIG_MAC_regs *bregs = bp->bregs;
+	struct dev_mc_list *dmi = dev->mc_list;
+	char *addrs;
+	int i, j, bit, byte;
+	u32 crc, poly = CRC_POLYNOMIAL_LE;
+
+	/* Disable the receiver.  The bit self-clears when
+	 * the operation is complete.
+	 */
+	bregs->rx_cfg &= ~(BIGMAC_RXCFG_ENABLE);
+	while((bregs->rx_cfg & BIGMAC_RXCFG_ENABLE) != 0)
+		udelay(20);
+
+	if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
+		bregs->htable0 = 0xffff;
+		bregs->htable1 = 0xffff;
+		bregs->htable2 = 0xffff;
+		bregs->htable3 = 0xffff;
+	} else if(dev->flags & IFF_PROMISC) {
+		bregs->rx_cfg |= BIGMAC_RXCFG_PMISC;
+	} else {
+		u16 hash_table[4];
+
+		for(i = 0; i < 4; i++)
+			hash_table[i] = 0;
+
+		for(i = 0; i < dev->mc_count; i++) {
+			addrs = dmi->dmi_addr;
+			dmi = dmi->next;
+
+			if(!(*addrs & 1))
+				continue;
+
+			crc = 0xffffffffU;
+			for(byte = 0; byte < 6; byte++) {
+				for(bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+					int test;
+
+					test = ((bit ^ crc) & 0x01);
+					crc >>= 1;
+					if(test)
+						crc = crc ^ poly;
+				}
+			}
+			crc >>= 26;
+			hash_table[crc >> 4] |= 1 << (crc & 0xf);
+		}
+		bregs->htable0 = hash_table[0];
+		bregs->htable1 = hash_table[1];
+		bregs->htable2 = hash_table[2];
+		bregs->htable3 = hash_table[3];
+	}
+
+	/* Re-enable the receiver. */
+	bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE;
+}
+
+__initfunc(static int bigmac_ether_init(struct device *dev, struct linux_sbus_device *qec_sdev))
+{
+	static unsigned version_printed = 0;
+	struct bigmac *bp = 0;
+	unsigned char bsizes, bsizes_more;
+	int i, j, num_qranges, res = ENOMEM;
+	struct linux_prom_ranges qranges[8];
+
+	/* Get a new device struct for this interface. */
+	dev = init_etherdev(0, sizeof(struct bigmac));
+
+	if(version_printed++ == 0)
+		printk(version);
+
+	/* Report what we have found to the user. */
+	printk("%s: BigMAC 100baseT Ethernet ", dev->name);
+	dev->base_addr = (long) qec_sdev;
+	for(i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i],
+		       i == 5 ? ' ' : ':');
+	printk("\n");
+
+	/* Setup softc, with backpointers to QEC and BigMAC SBUS device structs. */
+	bp = (struct bigmac *) dev->priv;
+	bp->qec_sbus_dev = qec_sdev;
+	bp->bigmac_sbus_dev = qec_sdev->child;
+
+	/* All further failures we find return this. */
+	res = ENODEV;
+
+	/* Verify the registers we expect, are actually there. */
+	if((bp->bigmac_sbus_dev->num_registers != 3) ||
+	   (bp->qec_sbus_dev->num_registers != 2)) {
+		printk("BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n",
+		       bp->qec_sbus_dev->num_registers,
+		       bp->bigmac_sbus_dev->num_registers);
+		printk("BIGMAC: Would you like that for here or to go?\n");
+		goto fail_and_cleanup;
+	}
+
+	/* Fun with QEC ranges... */
+	if(bp->bigmac_sbus_dev->ranges_applied == 0) {
+		i = prom_getproperty(bp->qec_sbus_dev->prom_node, "ranges",
+				     (char *)&qranges[0], sizeof(qranges));
+		num_qranges = (i / sizeof(struct linux_prom_ranges));
+
+		/* Now, apply all the ranges for the BigMAC. */
+		for(j = 0; j < bp->bigmac_sbus_dev->num_registers; j++) {
+			int k;
+
+			for(k = 0; k < num_qranges; k++)
+				if(bp->bigmac_sbus_dev->reg_addrs[j].which_io ==
+				   qranges[k].ot_child_space)
+					break;
+			if(k >= num_qranges) {
+				printk("BigMAC: Aieee, bogus QEC range for space %08x\n",
+				       bp->bigmac_sbus_dev->reg_addrs[j].which_io);
+				goto fail_and_cleanup;
+			}
+			bp->bigmac_sbus_dev->reg_addrs[j].which_io = qranges[k].ot_parent_space;
+			bp->bigmac_sbus_dev->reg_addrs[j].phys_addr += qranges[k].ot_parent_base;
+		}
+
+		/* Next, apply SBUS ranges on top of what we just changed. */
+		prom_apply_sbus_ranges(bp->bigmac_sbus_dev->my_bus,
+				       &bp->bigmac_sbus_dev->reg_addrs[0],
+				       bp->bigmac_sbus_dev->num_registers,
+				       bp->bigmac_sbus_dev);
+	}
+
+	/* Apply SBUS ranges for the QEC parent. */
+	prom_apply_sbus_ranges(bp->qec_sbus_dev->my_bus,
+			       &bp->qec_sbus_dev->reg_addrs[0],
+			       bp->qec_sbus_dev->num_registers,
+			       bp->qec_sbus_dev);
+
+	/* Map in QEC global control registers. */
+	bp->gregs = sparc_alloc_io(bp->qec_sbus_dev->reg_addrs[0].phys_addr,
+				   0,
+				   sizeof(struct qe_globreg),
+				   "BigMAC QEC Global Regs",
+				   bp->qec_sbus_dev->reg_addrs[0].which_io,
+				   0);
+	if(!bp->gregs) {
+		printk("BIGMAC: Cannot map QEC global registers.\n");
+		goto fail_and_cleanup;
+	}
+
+	/* Make sure QEC is in BigMAC mode. */
+	if((bp->gregs->ctrl & 0xf0000000) != GLOB_CTRL_BMODE) {
+		printk("BigMAC: AIEEE, QEC is not in BigMAC mode!\n");
+		goto fail_and_cleanup;
+	}
+
+	/* Reset the QEC. */
+	if(qec_global_reset(bp->gregs))
+		goto fail_and_cleanup;
+
+	/* Get supported SBUS burst sizes. */
+	bsizes = prom_getintdefault(bp->qec_sbus_dev->prom_node,
+				    "burst-sizes",
+				    0xff);
+
+	bsizes_more = prom_getintdefault(bp->qec_sbus_dev->my_bus->prom_node,
+					 "burst-sizes",
+					 0xff);
+
+	bsizes &= 0xff;
+	if(bsizes_more != 0xff)
+		bsizes &= bsizes_more;
+	if(bsizes == 0xff || (bsizes & DMA_BURST16) == 0 ||
+	   (bsizes & DMA_BURST32) == 0)
+		bsizes = (DMA_BURST32 - 1);
+	bp->bigmac_bursts = bsizes;
+
+	/* Perform QEC initialization. */
+	qec_init(bp);
+
+	/* Map in the BigMAC channel registers. */
+	bp->creg = sparc_alloc_io(bp->bigmac_sbus_dev->reg_addrs[0].phys_addr,
+				  0,
+				  sizeof(struct qe_creg),
+				  "BigMAC QEC Channel Regs",
+				  bp->bigmac_sbus_dev->reg_addrs[0].which_io,
+				  0);
+	if(!bp->creg) {
+		printk("BIGMAC: Cannot map QEC channel registers.\n");
+		goto fail_and_cleanup;
+	}
+
+	/* Map in the BigMAC control registers. */
+	bp->bregs = sparc_alloc_io(bp->bigmac_sbus_dev->reg_addrs[1].phys_addr,
+				   0,
+				   sizeof(struct BIG_MAC_regs),
+				   "BigMAC Primary Regs",
+				   bp->bigmac_sbus_dev->reg_addrs[1].which_io,
+				   0);
+	if(!bp->bregs) {
+		printk("BIGMAC: Cannot map BigMAC primary registers.\n");
+		goto fail_and_cleanup;
+	}
+
+	/* Map in the BigMAC transceiver registers, this is how you poke at
+	 * the BigMAC's PHY.
+	 */
+	bp->tregs = sparc_alloc_io(bp->bigmac_sbus_dev->reg_addrs[2].phys_addr,
+				   0,
+				   sizeof(struct bmac_tcvr),
+				   "BigMAC Transceiver Regs",
+				   bp->bigmac_sbus_dev->reg_addrs[2].which_io,
+				   0);
+	if(!bp->tregs) {
+		printk("BIGMAC: Cannot map BigMAC transceiver registers.\n");
+		goto fail_and_cleanup;
+	}
+
+	/* Stop the BigMAC. */
+	bigmac_stop(bp);
+
+	/* Allocate transmit/receive descriptor DVMA block. */
+	bp->bmac_block = (struct bmac_init_block *)
+		sparc_dvma_malloc(PAGE_SIZE, "BigMAC Init Block",
+				  &bp->bblock_dvma);
+
+	/* Get the board revision of this BigMAC. */
+	bp->board_rev = prom_getintdefault(bp->bigmac_sbus_dev->prom_node,
+					   "board-version", 1);
+
+	/* If on sun4c, we use a static buffer pool, on sun4m we DMA directly
+	 * in and out of sk_buffs instead for speed and one copy to userspace.
+	 */
+#ifndef __sparc_v9__
+	if(sparc_cpu_model == sun4c)
+		bp->sun4c_buffers = (struct bigmac_buffers *)
+			sparc_dvma_malloc(sizeof(struct bigmac_buffers),
+					  "BigMAC Bufs",
+					  &bp->s4c_buf_dvma);
+	else
+#endif
+		bp->sun4c_buffers = 0;
+
+	/* Init auto-negotiation timer state. */
+	init_timer(&bp->bigmac_timer);
+	bp->timer_state = asleep;
+	bp->timer_ticks = 0;
+
+	/* Backlink to generic net device struct. */
+	bp->dev = dev;
+
+	/* Set links to our BigMAC open and close routines. */
+	dev->open = &bigmac_open;
+	dev->stop = &bigmac_close;
+
+	/* Choose transmit routine based upon buffering scheme. */
+#ifndef __sparc_v9__
+	if(sparc_cpu_model == sun4c)
+		dev->hard_start_xmit = &sun4c_bigmac_start_xmit;
+	else
+#endif
+		dev->hard_start_xmit = &bigmac_start_xmit;
+
+	/* Set links to BigMAC statistic and multi-cast loading code. */
+	dev->get_stats = &bigmac_get_stats;
+	dev->set_multicast_list = &bigmac_set_multicast;
+
+	/* Finish net device registration. */
+	dev->irq = bp->bigmac_sbus_dev->irqs[0];
+	dev->dma = 0;
+	ether_setup(dev);
+
+#ifdef MODULE
+	/* Put us into the list of instances attached for later module unloading. */
+	bp->next_module = root_bigmac_dev;
+	root_bigmac_dev = bp;
+#endif
+	return 0;
+
+fail_and_cleanup:
+	/* Something went wrong, undo whatever we did so far. */
+	if(bp) {
+		/* Free register mappings if any. */
+		if(bp->gregs)
+			sparc_free_io(bp->gregs, sizeof(struct qe_globreg));
+		if(bp->creg)
+			sparc_free_io(bp->creg, sizeof(struct qe_creg));
+		if(bp->bregs)
+			sparc_free_io(bp->bregs, sizeof(struct BIG_MAC_regs));
+		if(bp->tregs)
+			sparc_free_io(bp->tregs, sizeof(struct bmac_tcvr));
+
+		/* XXX todo, bmac_block and sun4c_buffers */
+
+		/* Free the BigMAC softc. */
+		kfree(bp);
+		dev->priv = 0;
+	}
+	return res;	/* Return error code. */
+}
+
+__initfunc(int bigmac_probe(struct device *dev))
+{
+	struct linux_sbus *bus;
+	struct linux_sbus_device *sdev = 0;
+	static int called = 0;
+	int cards = 0, v;
+
+	if(called)
+		return ENODEV;
+	called++;
+
+	for_each_sbus(bus) {
+		for_each_sbusdev(sdev, bus) {
+			if(cards) dev = NULL;
+
+			/* QEC can be the parent of either QuadEthernet or
+			 * a BigMAC.  We want the latter.
+			 */
+			if(!strcmp(sdev->prom_name, "qec") && sdev->child &&
+			   !strcmp(sdev->child->prom_name, "be")) {
+				cards++;
+				if((v = bigmac_ether_init(dev, sdev)))
+				   return v;
+			}
+		}
+	}
+	if(!cards)
+		return ENODEV;
+	return 0;
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+	root_bigmac_dev = NULL;
+	return bigmac_probe(NULL);
+}
+
+void
+cleanup_module(void)
+{
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_bigmac_dev) {
+		struct bigmac *bp = root_bigmac_dev;
+		struct bigmac *bp_nxt = root_bigmac_dev->next_module;
+
+		sparc_free_io(bp->gregs, sizeof(struct qe_globreg));
+		sparc_free_io(bp->creg, sizeof(struct qe_creg));
+		sparc_free_io(bp->bregs, sizeof(struct BIG_MAC_regs));
+		sparc_free_io(bp->tregs, sizeof(struct bmac_tcvr));
+
+		/* XXX todo, bmac_block and sun4c_buffers */
+
+		unregister_netdev(bp->dev);
+		kfree(bp->dev);
+		root_bigmac_dev = bp_nxt;
+	}
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunbmac.h linux/drivers/net/sunbmac.h
--- v2.2.3/linux/drivers/net/sunbmac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/sunbmac.h	Mon Mar 15 16:11:30 1999
@@ -0,0 +1,381 @@
+/* sunbmac.h: Defines for the Sun "Big MAC" 100baseT ethernet cards.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SUNBMAC_H
+#define _SUNBMAC_H
+
+/* QEC global registers. */
+struct qe_globreg {
+	volatile unsigned int ctrl;      /* Control                  */
+	volatile unsigned int stat;      /* Status                   */
+	volatile unsigned int psize;     /* Packet Size              */
+	volatile unsigned int msize;     /* Local-mem size (64K)     */
+	volatile unsigned int rsize;     /* Receive partition size   */
+	volatile unsigned int tsize;     /* Transmit partition size  */
+};
+
+#define GLOB_CTRL_MMODE       0x40000000 /* MACE qec mode            */
+#define GLOB_CTRL_BMODE       0x10000000 /* BigMAC qec mode          */
+#define GLOB_CTRL_EPAR        0x00000020 /* Enable parity            */
+#define GLOB_CTRL_ACNTRL      0x00000018 /* SBUS arbitration control */
+#define GLOB_CTRL_B64         0x00000004 /* 64 byte dvma bursts      */
+#define GLOB_CTRL_B32         0x00000002 /* 32 byte dvma bursts      */
+#define GLOB_CTRL_B16         0x00000000 /* 16 byte dvma bursts      */
+#define GLOB_CTRL_RESET       0x00000001 /* Reset the QEC            */
+
+#define GLOB_STAT_TX          0x00000008 /* BigMAC Transmit IRQ      */
+#define GLOB_STAT_RX          0x00000004 /* BigMAC Receive IRQ       */
+#define GLOB_STAT_BM          0x00000002 /* BigMAC Global IRQ        */
+#define GLOB_STAT_ER          0x00000001 /* BigMAC Error IRQ         */
+
+#define GLOB_PSIZE_2048       0x00       /* 2k packet size           */
+#define GLOB_PSIZE_4096       0x01       /* 4k packet size           */
+#define GLOB_PSIZE_6144       0x10       /* 6k packet size           */
+#define GLOB_PSIZE_8192       0x11       /* 8k packet size           */
+
+/* QEC BigMAC channel registers. */
+struct qe_creg {
+	volatile unsigned int ctrl;       /* Control                     */
+	volatile unsigned int stat;       /* Status                      */
+	volatile unsigned int rxds;       /* RX descriptor ring ptr      */
+	volatile unsigned int txds;       /* TX descriptor ring ptr      */
+	volatile unsigned int rimask;     /* RX Interrupt Mask           */
+	volatile unsigned int timask;     /* TX Interrupt Mask           */
+	volatile unsigned int qmask;      /* QEC Error Interrupt Mask    */
+	volatile unsigned int bmask;      /* BigMAC Error Interrupt Mask */
+	volatile unsigned int rxwbufptr;  /* Local memory rx write ptr   */
+	volatile unsigned int rxrbufptr;  /* Local memory rx read ptr    */
+	volatile unsigned int txwbufptr;  /* Local memory tx write ptr   */
+	volatile unsigned int txrbufptr;  /* Local memory tx read ptr    */
+	volatile unsigned int ccnt;       /* Collision Counter           */
+};
+
+#define CREG_CTRL_TWAKEUP     0x00000001  /* Transmitter Wakeup, 'go'. */
+
+#define CREG_STAT_BERROR      0x80000000  /* BigMAC error              */
+#define CREG_STAT_TXIRQ       0x00200000  /* Transmit Interrupt        */
+#define CREG_STAT_TXDERROR    0x00080000  /* TX Descriptor is bogus    */
+#define CREG_STAT_TXLERR      0x00040000  /* Late Transmit Error       */
+#define CREG_STAT_TXPERR      0x00020000  /* Transmit Parity Error     */
+#define CREG_STAT_TXSERR      0x00010000  /* Transmit SBUS error ack   */
+#define CREG_STAT_RXIRQ       0x00000020  /* Receive Interrupt         */
+#define CREG_STAT_RXDROP      0x00000010  /* Dropped a RX'd packet     */
+#define CREG_STAT_RXSMALL     0x00000008  /* Receive buffer too small  */
+#define CREG_STAT_RXLERR      0x00000004  /* Receive Late Error        */
+#define CREG_STAT_RXPERR      0x00000002  /* Receive Parity Error      */
+#define CREG_STAT_RXSERR      0x00000001  /* Receive SBUS Error ACK    */
+
+#define CREG_STAT_ERRORS      (CREG_STAT_BERROR|CREG_STAT_TXDERROR|CREG_STAT_TXLERR|   \
+                               CREG_STAT_TXPERR|CREG_STAT_TXSERR|CREG_STAT_RXDROP|     \
+                               CREG_STAT_RXSMALL|CREG_STAT_RXLERR|CREG_STAT_RXPERR|    \
+                               CREG_STAT_RXSERR)
+
+#define CREG_QMASK_TXDERROR   0x00080000  /* TXD error                 */
+#define CREG_QMASK_TXLERR     0x00040000  /* TX late error             */
+#define CREG_QMASK_TXPERR     0x00020000  /* TX parity error           */
+#define CREG_QMASK_TXSERR     0x00010000  /* TX sbus error ack         */
+#define CREG_QMASK_RXDROP     0x00000010  /* RX drop                   */
+#define CREG_QMASK_RXBERROR   0x00000008  /* RX buffer error           */
+#define CREG_QMASK_RXLEERR    0x00000004  /* RX late error             */
+#define CREG_QMASK_RXPERR     0x00000002  /* RX parity error           */
+#define CREG_QMASK_RXSERR     0x00000001  /* RX sbus error ack         */
+
+struct BIG_MAC_regs {
+	volatile unsigned int xif_cfg;		/* XIF config register                */
+	volatile unsigned int _unused[63];	/* Reserved...                        */
+	volatile unsigned int status;		/* Status register, clear on read     */
+	volatile unsigned int imask;		/* Interrupt mask register            */
+	volatile unsigned int _unused2[64];	/* Reserved...                        */
+	volatile unsigned int tx_swreset;	/* Transmitter software reset         */
+	volatile unsigned int tx_cfg;           /* Transmitter config register        */
+	volatile unsigned int ipkt_gap1;	/* Inter-packet gap 1                 */
+	volatile unsigned int ipkt_gap2;	/* Inter-packet gap 2                 */
+	volatile unsigned int attempt_limit;	/* Transmit attempt limit             */
+	volatile unsigned int stime;		/* Transmit slot time                 */
+	volatile unsigned int preamble_len;	/* Size of transmit preamble          */
+	volatile unsigned int preamble_pattern;	/* Pattern for transmit preamble      */
+	volatile unsigned int tx_sframe_delim;	/* Transmit delimiter                 */
+	volatile unsigned int jsize;		/* Toe jam...                         */
+	volatile unsigned int tx_pkt_max;	/* Transmit max pkt size              */
+	volatile unsigned int tx_pkt_min;	/* Transmit min pkt size              */
+	volatile unsigned int peak_attempt;	/* Count of transmit peak attempts    */
+	volatile unsigned int dt_ctr;		/* Transmit defer timer               */
+	volatile unsigned int nc_ctr;           /* Transmit normal-collision counter  */
+	volatile unsigned int fc_ctr;           /* Transmit first-collision counter   */
+	volatile unsigned int ex_ctr;           /* Transmit excess-collision counter  */
+	volatile unsigned int lt_ctr;           /* Transmit late-collision counter    */
+	volatile unsigned int rand_seed;        /* Transmit random number seed        */
+	volatile unsigned int tx_smachine;      /* Transmit state machine             */
+	volatile unsigned int _unused3[44];     /* Reserved                           */
+	volatile unsigned int rx_swreset;       /* Receiver software reset            */
+	volatile unsigned int rx_cfg;           /* Receiver config register           */
+	volatile unsigned int rx_pkt_max;       /* Receive max pkt size               */
+	volatile unsigned int rx_pkt_min;       /* Receive min pkt size               */
+	volatile unsigned int mac_addr2;        /* Ether address register 2           */
+	volatile unsigned int mac_addr1;        /* Ether address register 1           */
+	volatile unsigned int mac_addr0;        /* Ether address register 0           */
+	volatile unsigned int fr_ctr;           /* Receive frame receive counter      */
+	volatile unsigned int gle_ctr;          /* Receive giant-length error counter */
+	volatile unsigned int unale_ctr;        /* Receive unaligned error counter    */
+	volatile unsigned int rcrce_ctr;        /* Receive CRC error counter          */
+	volatile unsigned int rx_smachine;      /* Receiver state machine             */
+	volatile unsigned int rx_cvalid;        /* Receiver code violation            */
+	volatile unsigned int _unused4;         /* Reserved...                        */
+	volatile unsigned int htable3;          /* Hash table 3                       */
+	volatile unsigned int htable2;          /* Hash table 2                       */
+	volatile unsigned int htable1;          /* Hash table 1                       */
+	volatile unsigned int htable0;          /* Hash table 0                       */
+	volatile unsigned int afilter2;         /* Address filter 2                   */
+	volatile unsigned int afilter1;         /* Address filter 1                   */
+	volatile unsigned int afilter0;         /* Address filter 0                   */
+	volatile unsigned int afilter_mask;     /* Address filter mask                */
+};
+
+/* BigMac XIF config register. */
+#define BIGMAC_XCFG_ODENABLE   0x00000001 /* Output driver enable                     */
+#define BIGMAC_XCFG_RESV       0x00000002 /* Reserved, write always as 1              */
+#define BIGMAC_XCFG_MLBACK     0x00000004 /* Loopback-mode MII enable                 */
+#define BIGMAC_XCFG_SMODE      0x00000008 /* Enable serial mode                       */
+
+/* BigMAC status register. */
+#define BIGMAC_STAT_GOTFRAME   0x00000001 /* Received a frame                         */
+#define BIGMAC_STAT_RCNTEXP    0x00000002 /* Receive frame counter expired            */
+#define BIGMAC_STAT_ACNTEXP    0x00000004 /* Align-error counter expired              */
+#define BIGMAC_STAT_CCNTEXP    0x00000008 /* CRC-error counter expired                */
+#define BIGMAC_STAT_LCNTEXP    0x00000010 /* Length-error counter expired             */
+#define BIGMAC_STAT_RFIFOVF    0x00000020 /* Receive FIFO overflow                    */
+#define BIGMAC_STAT_CVCNTEXP   0x00000040 /* Code-violation counter expired           */
+#define BIGMAC_STAT_SENTFRAME  0x00000100 /* Transmitted a frame                      */
+#define BIGMAC_STAT_TFIFO_UND  0x00000200 /* Transmit FIFO underrun                   */
+#define BIGMAC_STAT_MAXPKTERR  0x00000400 /* Max-packet size error                    */
+#define BIGMAC_STAT_NCNTEXP    0x00000800 /* Normal-collision counter expired         */
+#define BIGMAC_STAT_ECNTEXP    0x00001000 /* Excess-collision counter expired         */
+#define BIGMAC_STAT_LCCNTEXP   0x00002000 /* Late-collision counter expired           */
+#define BIGMAC_STAT_FCNTEXP    0x00004000 /* First-collision counter expired          */
+#define BIGMAC_STAT_DTIMEXP    0x00008000 /* Defer-timer expired                      */
+
+/* BigMAC interrupt mask register. */
+#define BIGMAC_IMASK_GOTFRAME  0x00000001 /* Received a frame                         */
+#define BIGMAC_IMASK_RCNTEXP   0x00000002 /* Receive frame counter expired            */
+#define BIGMAC_IMASK_ACNTEXP   0x00000004 /* Align-error counter expired              */
+#define BIGMAC_IMASK_CCNTEXP   0x00000008 /* CRC-error counter expired                */
+#define BIGMAC_IMASK_LCNTEXP   0x00000010 /* Length-error counter expired             */
+#define BIGMAC_IMASK_RFIFOVF   0x00000020 /* Receive FIFO overflow                    */
+#define BIGMAC_IMASK_CVCNTEXP  0x00000040 /* Code-violation counter expired           */
+#define BIGMAC_IMASK_SENTFRAME 0x00000100 /* Transmitted a frame                      */
+#define BIGMAC_IMASK_TFIFO_UND 0x00000200 /* Transmit FIFO underrun                   */
+#define BIGMAC_IMASK_MAXPKTERR 0x00000400 /* Max-packet size error                    */
+#define BIGMAC_IMASK_NCNTEXP   0x00000800 /* Normal-collision counter expired         */
+#define BIGMAC_IMASK_ECNTEXP   0x00001000 /* Excess-collision counter expired         */
+#define BIGMAC_IMASK_LCCNTEXP  0x00002000 /* Late-collision counter expired           */
+#define BIGMAC_IMASK_FCNTEXP   0x00004000 /* First-collision counter expired          */
+#define BIGMAC_IMASK_DTIMEXP   0x00008000 /* Defer-timer expired                      */
+
+/* BigMac transmit config register. */
+#define BIGMAC_TXCFG_ENABLE    0x00000001 /* Enable the transmitter                   */
+#define BIGMAC_TXCFG_FIFO      0x00000010 /* Default tx fthresh...                    */
+#define BIGMAC_TXCFG_SMODE     0x00000020 /* Enable slow transmit mode                */
+#define BIGMAC_TXCFG_CIGN      0x00000040 /* Ignore transmit collisions               */
+#define BIGMAC_TXCFG_FCSOFF    0x00000080 /* Do not emit FCS                          */
+#define BIGMAC_TXCFG_DBACKOFF  0x00000100 /* Disable backoff                          */
+#define BIGMAC_TXCFG_FULLDPLX  0x00000200 /* Enable full-duplex                       */
+
+/* BigMac receive config register. */
+#define BIGMAC_RXCFG_ENABLE    0x00000001 /* Enable the receiver                      */
+#define BIGMAC_RXCFG_FIFO      0x0000000e /* Default rx fthresh...                    */
+#define BIGMAC_RXCFG_PSTRIP    0x00000020 /* Pad byte strip enable                    */
+#define BIGMAC_RXCFG_PMISC     0x00000040 /* Enable promiscous mode                   */
+#define BIGMAC_RXCFG_DERR      0x00000080 /* Disable error checking                   */
+#define BIGMAC_RXCFG_DCRCS     0x00000100 /* Disable CRC stripping                    */
+#define BIGMAC_RXCFG_ME        0x00000200 /* Receive packets addressed to me          */
+#define BIGMAC_RXCFG_PGRP      0x00000400 /* Enable promisc group mode                */
+#define BIGMAC_RXCFG_HENABLE   0x00000800 /* Enable the hash filter                   */
+#define BIGMAC_RXCFG_AENABLE   0x00001000 /* Enable the address filter                */
+
+/* The BigMAC PHY transceiver.  Not nearly as sophisticated as the happy meal
+ * one.  But it does have the "bit banger", oh baby.
+ */
+struct bmac_tcvr {
+	unsigned int	tcvr_pal;
+	unsigned int    mgmt_pal;
+};
+
+/* Frame commands. */
+#define FRAME_WRITE           0x50020000
+#define FRAME_READ            0x60020000
+
+/* Tranceiver registers. */
+#define TCVR_PAL_SERIAL       0x00000001 /* Enable serial mode              */
+#define TCVR_PAL_EXTLBACK     0x00000002 /* Enable external loopback        */
+#define TCVR_PAL_MSENSE       0x00000004 /* Media sense                     */
+#define TCVR_PAL_LTENABLE     0x00000008 /* Link test enable                */
+#define TCVR_PAL_LTSTATUS     0x00000010 /* Link test status  (P1 only)     */
+
+/* Management PAL. */
+#define MGMT_PAL_DCLOCK       0x00000001 /* Data clock                      */
+#define MGMT_PAL_OENAB        0x00000002 /* Output enabler                  */
+#define MGMT_PAL_MDIO         0x00000004 /* MDIO Data/attached              */
+#define MGMT_PAL_TIMEO        0x00000008 /* Transmit enable timeout error   */
+#define MGMT_PAL_EXT_MDIO     MGMT_PAL_MDIO
+#define MGMT_PAL_INT_MDIO     MGMT_PAL_TIMEO
+
+/* Here are some PHY addresses. */
+#define BIGMAC_PHY_EXTERNAL   0 /* External transceiver */
+#define BIGMAC_PHY_INTERNAL   1 /* Internal transceiver */
+
+/* PHY registers */
+#define BIGMAC_BMCR           0x00 /* Basic mode control register	*/
+#define BIGMAC_BMSR           0x01 /* Basic mode status register	*/
+
+/* BMCR bits */
+#define BMCR_ISOLATE            0x0400  /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN              0x0800  /* Powerdown the DP83840       */
+#define BMCR_ANENABLE           0x1000  /* Enable auto negotiation     */
+#define BMCR_SPEED100           0x2000  /* Select 100Mbps              */
+#define BMCR_LOOPBACK           0x4000  /* TXD loopback bits           */
+#define BMCR_RESET              0x8000  /* Reset the DP83840           */
+
+/* BMSR bits */
+#define BMSR_ERCAP              0x0001  /* Ext-reg capability          */
+#define BMSR_JCD                0x0002  /* Jabber detected             */
+#define BMSR_LSTATUS            0x0004  /* Link status                 */
+
+/* Ring descriptors and such, same as Quad Ethernet. */
+struct be_rxd {
+	unsigned int rx_flags;
+	unsigned int rx_addr;
+};
+
+#define RXD_OWN      0x80000000 /* Ownership.      */
+#define RXD_UPDATE   0x10000000 /* Being Updated?  */
+#define RXD_LENGTH   0x000007ff /* Packet Length.  */
+
+struct be_txd {
+	unsigned int tx_flags;
+	unsigned int tx_addr;
+};
+
+#define TXD_OWN      0x80000000 /* Ownership.      */
+#define TXD_SOP      0x40000000 /* Start Of Packet */
+#define TXD_EOP      0x20000000 /* End Of Packet   */
+#define TXD_UPDATE   0x10000000 /* Being Updated?  */
+#define TXD_LENGTH   0x000007ff /* Packet Length.  */
+
+#define TX_RING_MAXSIZE   256
+#define RX_RING_MAXSIZE   256
+
+#define TX_RING_SIZE      256
+#define RX_RING_SIZE      256
+
+#define NEXT_RX(num)       (((num) + 1) & (RX_RING_SIZE - 1))
+#define NEXT_TX(num)       (((num) + 1) & (TX_RING_SIZE - 1))
+#define PREV_RX(num)       (((num) - 1) & (RX_RING_SIZE - 1))
+#define PREV_TX(num)       (((num) - 1) & (TX_RING_SIZE - 1))
+
+#define TX_BUFFS_AVAIL(bp)                                    \
+        (((bp)->tx_old <= (bp)->tx_new) ?                     \
+	  (bp)->tx_old + (TX_RING_SIZE - 1) - (bp)->tx_new :  \
+			    (bp)->tx_old - (bp)->tx_new - 1)
+
+
+#define SUN4C_TX_BUFFS_AVAIL(bp)                                    \
+        (((bp)->tx_old <= (bp)->tx_new) ?                           \
+	  (bp)->tx_old + (SUN4C_TX_RING_SIZE - 1) - (bp)->tx_new :  \
+	  (bp)->tx_old - (bp)->tx_new - (TX_RING_SIZE - SUN4C_TX_RING_SIZE))
+
+
+#define RX_COPY_THRESHOLD  128
+#define RX_BUF_ALLOC_SIZE  (ETH_FRAME_LEN + (64 * 3))
+
+struct bmac_init_block {
+	struct be_rxd be_rxd[RX_RING_MAXSIZE];
+	struct be_txd be_txd[TX_RING_MAXSIZE];
+};
+
+#define bib_offset(mem, elem) \
+((__u32)((unsigned long)(&(((struct bmac_init_block *)0)->mem[elem]))))
+
+#define SUN4C_PKT_BUF_SZ	1546
+#define SUN4C_RX_BUFF_SIZE	SUN4C_PKT_BUF_SZ
+#define SUN4C_TX_BUFF_SIZE	SUN4C_PKT_BUF_SZ
+
+#define SUN4C_RX_RING_SIZE	16
+#define SUN4C_TX_RING_SIZE	16
+
+struct bigmac_buffers {
+	char	tx_buf[SUN4C_TX_RING_SIZE][SUN4C_TX_BUFF_SIZE];
+	char	pad[2];		/* Align rx_buf for copy_and_sum(). */
+	char	rx_buf[SUN4C_RX_RING_SIZE][SUN4C_RX_BUFF_SIZE];
+};
+
+#define bbuf_offset(mem, elem) \
+((__u32)((unsigned long)(&(((struct bigmac_buffers *)0)->mem[elem][0]))))
+
+/* Now software state stuff. */
+enum bigmac_transceiver {
+	external = 0,
+	internal = 1,
+	none     = 2,
+};
+
+/* Timer state engine. */
+enum bigmac_timer_state {
+	ltrywait = 1,  /* Forcing try of all modes, from fastest to slowest. */
+	asleep   = 2,  /* Timer inactive.                                    */
+};
+
+struct bigmac {
+	struct qe_globreg	*gregs;		/* QEC Global Registers               */
+	struct qe_creg		*creg;		/* QEC BigMAC Channel Registers       */
+	struct BIG_MAC_regs	*bregs;         /* BigMAC Registers                   */
+	struct bmac_tcvr	*tregs;         /* BigMAC Transceiver                 */
+	struct bmac_init_block	*bmac_block;	/* RX and TX descriptors              */
+	__u32			 bblock_dvma;	/* RX and TX descriptors              */
+
+	struct sk_buff		*rx_skbs[RX_RING_SIZE];
+	struct sk_buff		*tx_skbs[TX_RING_SIZE];
+
+	int rx_new, tx_new, rx_old, tx_old;
+
+	struct bigmac_buffers	*sun4c_buffers;
+	__u32			 s4c_buf_dvma;
+
+	int board_rev;				/* BigMAC board revision.             */
+
+	enum bigmac_transceiver	tcvr_type;
+	unsigned int		bigmac_bursts;
+	unsigned int		paddr;
+	unsigned short		sw_bmsr;         /* SW copy of PHY BMSR               */
+	unsigned short		sw_bmcr;         /* SW copy of PHY BMCR               */
+	struct timer_list	bigmac_timer;
+	enum bigmac_timer_state	timer_state;
+	unsigned int		timer_ticks;
+
+	struct enet_statistics		enet_stats;
+	struct linux_sbus_device	*qec_sbus_dev;
+	struct linux_sbus_device	*bigmac_sbus_dev;
+	struct device			*dev;
+	struct bigmac			*next_module;
+};
+
+/* We use this to acquire receive skb's that we can DMA directly into. */
+#define ALIGNED_RX_SKB_ADDR(addr) \
+        ((((unsigned long)(addr) + (64 - 1)) & ~(64 - 1)) - (unsigned long)(addr))
+
+static inline struct sk_buff *big_mac_alloc_skb(unsigned int length, int gfp_flags)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(length + 64, gfp_flags);
+	if(skb) {
+		int offset = ALIGNED_RX_SKB_ADDR(skb->data);
+
+		if(offset)
+			skb_reserve(skb, offset);
+	}
+	return skb;
+}
+
+#endif /* !(_SUNBMAC_H) */
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.2.3/linux/drivers/net/sunhme.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/net/sunhme.c	Mon Mar 15 16:11:30 1999
@@ -2,11 +2,11 @@
  *           auto carrier detecting ethernet driver.  Also known as the
  *           "Happy Meal Ethernet" found on SunSwift SBUS cards.
  *
- * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1996, 1998 David S. Miller (davem@caipfs.rutgers.edu)
  */
 
 static char *version =
-        "sunhme.c:v1.2 10/Oct/96 David S. Miller (davem@caipfs.rutgers.edu)\n";
+        "sunhme.c:v1.10 27/Jan/99 David S. Miller (davem@caipfs.rutgers.edu)\n";
 
 #include <linux/module.h>
 
@@ -35,12 +35,13 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/auxio.h>
-#include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 #ifndef __sparc_v9__
 #include <asm/io-unit.h>
 #endif
+#include <asm/ethtool.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -57,6 +58,11 @@
 static struct happy_meal *root_happy_dev = NULL;
 #endif
 
+static struct quattro *qfe_sbus_list = NULL;
+#ifdef CONFIG_PCI
+static struct quattro *qfe_pci_list = NULL;
+#endif
+
 #undef HMEDEBUG
 #undef SXDEBUG
 #undef RXDEBUG
@@ -363,6 +369,15 @@
 {
 	hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
 
+	/* Downgrade from full to half duplex.  Only possible
+	 * via ethtool.
+	 */
+	if(hp->sw_bmcr & BMCR_FULLDPLX) {
+		hp->sw_bmcr &= ~(BMCR_FULLDPLX);
+		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
+		return 0;
+	}
+
 	/* Downgrade from 100 to 10. */
 	if(hp->sw_bmcr & BMCR_SPEED100) {
 		hp->sw_bmcr &= ~(BMCR_SPEED100);
@@ -1049,7 +1064,7 @@
 	for(i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb;
 
-		skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags);
+		skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags | GFP_DMA);
 		if(!skb)
 			continue;
 		hp->rx_skbs[i] = skb;
@@ -1077,7 +1092,7 @@
 		} else
 #endif
 		{
-			hb->happy_meal_rxd[i].rx_addr = (u32)((unsigned long) skb->data);
+			hb->happy_meal_rxd[i].rx_addr = sbus_dvma_addr(skb->data);
 			hb->happy_meal_rxd[i].rx_flags =
 				(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
 		}
@@ -1115,7 +1130,8 @@
 #endif
 
 static void happy_meal_begin_auto_negotiation(struct happy_meal *hp,
-					      struct hmeal_tcvregs *tregs)
+					      struct hmeal_tcvregs *tregs,
+					      struct ethtool_cmd *ep)
 {
 	int timeout;
 
@@ -1124,93 +1140,108 @@
 	hp->sw_bmcr      = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
 	hp->sw_physid1   = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID1);
 	hp->sw_physid2   = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID2);
-	hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE);
 
 	/* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */
 
-	/* Advertise everything we can support. */
-	if(hp->sw_bmsr & BMSR_10HALF)
-		hp->sw_advertise |= (ADVERTISE_10HALF);
-	else
-		hp->sw_advertise &= ~(ADVERTISE_10HALF);
+	hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE);
+	if(ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
+		/* Advertise everything we can support. */
+		if(hp->sw_bmsr & BMSR_10HALF)
+			hp->sw_advertise |= (ADVERTISE_10HALF);
+		else
+			hp->sw_advertise &= ~(ADVERTISE_10HALF);
 
-	if(hp->sw_bmsr & BMSR_10FULL)
-		hp->sw_advertise |= (ADVERTISE_10FULL);
-	else
-		hp->sw_advertise &= ~(ADVERTISE_10FULL);
-	if(hp->sw_bmsr & BMSR_100HALF)
-		hp->sw_advertise |= (ADVERTISE_100HALF);
-	else
-		hp->sw_advertise &= ~(ADVERTISE_100HALF);
-	if(hp->sw_bmsr & BMSR_100FULL)
-		hp->sw_advertise |= (ADVERTISE_100FULL);
-	else
-		hp->sw_advertise &= ~(ADVERTISE_100FULL);
+		if(hp->sw_bmsr & BMSR_10FULL)
+			hp->sw_advertise |= (ADVERTISE_10FULL);
+		else
+			hp->sw_advertise &= ~(ADVERTISE_10FULL);
+		if(hp->sw_bmsr & BMSR_100HALF)
+			hp->sw_advertise |= (ADVERTISE_100HALF);
+		else
+			hp->sw_advertise &= ~(ADVERTISE_100HALF);
+		if(hp->sw_bmsr & BMSR_100FULL)
+			hp->sw_advertise |= (ADVERTISE_100FULL);
+		else
+			hp->sw_advertise &= ~(ADVERTISE_100FULL);
+		happy_meal_tcvr_write(hp, tregs, DP83840_ADVERTISE, hp->sw_advertise);
 
-	/* XXX Currently no Happy Meal cards I know off support 100BaseT4,
-	 * XXX and this is because the DP83840 does not support it, changes
-	 * XXX would need to be made to the tx/rx logic in the driver as well
-	 * XXX so I completely skip checking for it in the BMSR for now.
-	 */
+		/* XXX Currently no Happy Meal cards I know off support 100BaseT4,
+		 * XXX and this is because the DP83840 does not support it, changes
+		 * XXX would need to be made to the tx/rx logic in the driver as well
+		 * XXX so I completely skip checking for it in the BMSR for now.
+		 */
 
 #ifdef AUTO_SWITCH_DEBUG
-	ASD(("%s: Advertising [ ", hp->dev->name));
-	if(hp->sw_advertise & ADVERTISE_10HALF)
-		ASD(("10H "));
-	if(hp->sw_advertise & ADVERTISE_10FULL)
-		ASD(("10F "));
-	if(hp->sw_advertise & ADVERTISE_100HALF)
-		ASD(("100H "));
-	if(hp->sw_advertise & ADVERTISE_100FULL)
-		ASD(("100F "));
+		ASD(("%s: Advertising [ ", hp->dev->name));
+		if(hp->sw_advertise & ADVERTISE_10HALF)
+			ASD(("10H "));
+		if(hp->sw_advertise & ADVERTISE_10FULL)
+			ASD(("10F "));
+		if(hp->sw_advertise & ADVERTISE_100HALF)
+			ASD(("100H "));
+		if(hp->sw_advertise & ADVERTISE_100FULL)
+			ASD(("100F "));
 #endif
 
-	happy_meal_tcvr_write(hp, tregs, DP83840_ADVERTISE, hp->sw_advertise);
+		/* Enable Auto-Negotiation, this is usually on already... */
+		hp->sw_bmcr |= BMCR_ANENABLE;
+		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
 
-	/* Enable Auto-Negotiation, this is usually on already... */
-	hp->sw_bmcr |= BMCR_ANENABLE;
-	happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
+		/* Restart it to make sure it is going. */
+		hp->sw_bmcr |= BMCR_ANRESTART;
+		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
 
-	/* Restart it to make sure it is going. */
-	hp->sw_bmcr |= BMCR_ANRESTART;
-	happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
+		/* BMCR_ANRESTART self clears when the process has begun. */
 
-	/* BMCR_ANRESTART self clears when the process has begun. */
+		timeout = 64;  /* More than enough. */
+		while(--timeout) {
+			hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
+			if(!(hp->sw_bmcr & BMCR_ANRESTART))
+				break; /* got it. */
+			udelay(10);
+		}
+		if(!timeout) {
+			printk("%s: Happy Meal would not start auto negotiation "
+			       "BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr);
+			printk("%s: Performing force link detection.\n",
+			       hp->dev->name);
+			goto force_link;
+		} else {
+			hp->timer_state = arbwait;
+		}
+	} else {
+force_link:
+		/* Force the link up, trying first a particular mode.
+		 * Either we are here at the request of ethtool or
+		 * because the Happy Meal would not start to autoneg.
+		 */
 
-	timeout = 64;  /* More than enough. */
-	while(--timeout) {
-		hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
-		if(!(hp->sw_bmcr & BMCR_ANRESTART))
-			break; /* got it. */
-		udelay(10);
-	}
-	if(!timeout) {
-		printk("%s: Happy Meal would not start auto negotiation BMCR=0x%04x\n",
-		       hp->dev->name, hp->sw_bmcr);
-		printk("%s: Performing force link detection.\n", hp->dev->name);
-
-		/* Disable auto-negotiation in BMCR, enable FULL duplex and 100mb/s,
-		 * setup the timer state machine, and fire it off.
-		 *
-		 * XXX Should probably reset the DP83840 first
-		 * XXX as this is a gross fatal error...
+		/* Disable auto-negotiation in BMCR, enable the duplex and
+		 * speed setting, init the timer state machine, and fire it off.
 		 */
-		hp->sw_bmcr = BMCR_SPEED100;
+		if(ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
+			hp->sw_bmcr = BMCR_SPEED100;
+		} else {
+			if(ep->speed == SPEED_100)
+				hp->sw_bmcr = BMCR_SPEED100;
+			else
+				hp->sw_bmcr = 0;
+			if(ep->duplex == DUPLEX_FULL)
+				hp->sw_bmcr |= BMCR_FULLDPLX;
+		}
 		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
 
 		/* OK, seems we need do disable the transceiver for the first
 		 * tick to make sure we get an accurate link state at the
 		 * second tick.
 		 */
-		hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
-		printk("%s: CSCONFIG [%04x], disabling transceiver\n", hp->dev->name,
-		       hp->sw_csconfig);
+		hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
+						       DP83840_CSCONFIG);
 		hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
-		happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+		happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG,
+				      hp->sw_csconfig);
 
 		hp->timer_state = ltrywait;
-	} else {
-		hp->timer_state = arbwait;
 	}
 
 	hp->timer_ticks = 0;
@@ -1220,6 +1251,9 @@
 	add_timer(&hp->happy_timer);
 }
 
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
+
 static int happy_meal_init(struct happy_meal *hp, int from_irq)
 {
 	struct hmeal_gregs   *gregs        = hp->gregs;
@@ -1227,9 +1261,12 @@
 	struct hmeal_erxregs *erxregs      = hp->erxregs;
 	struct hmeal_bigmacregs *bregs     = hp->bigmacregs;
 	struct hmeal_tcvregs *tregs        = hp->tcvregs;
-	unsigned long regtmp;
+	unsigned long regtmp, rxcfg;
 	unsigned char *e = &hp->dev->dev_addr[0];
 
+	/* If auto-negotiation timer is running, kill it. */
+	del_timer(&hp->happy_timer);
+
 	HMD(("happy_meal_init: happy_flags[%08x] ",
 	     hp->happy_flags));
 	if(!(hp->happy_flags & HFLAG_INIT)) {
@@ -1322,12 +1359,54 @@
 	hme_write32(hp, &bregs->mac_addr1, ((e[2] << 8) | e[3]));
 	hme_write32(hp, &bregs->mac_addr0, ((e[0] << 8) | e[1]));
 
-	/* Ick, figure out how to properly program the hash table later... */
 	HMD(("htable, "));
-	hme_write32(hp, &bregs->htable3, 0);
-	hme_write32(hp, &bregs->htable2, 0);
-	hme_write32(hp, &bregs->htable1, 0);
-	hme_write32(hp, &bregs->htable0, 0);
+	if((hp->dev->flags & IFF_ALLMULTI) ||
+	   (hp->dev->mc_count > 64)) {
+		hme_write32(hp, &bregs->htable0, 0xffff);
+		hme_write32(hp, &bregs->htable1, 0xffff);
+		hme_write32(hp, &bregs->htable2, 0xffff);
+		hme_write32(hp, &bregs->htable3, 0xffff);
+	} else if((hp->dev->flags & IFF_PROMISC) == 0) {
+		u16 hash_table[4];
+		struct dev_mc_list *dmi = hp->dev->mc_list;
+		char *addrs;
+		int i, j, bit, byte;
+		u32 crc, poly = CRC_POLYNOMIAL_LE;
+
+		for(i = 0; i < 4; i++)
+			hash_table[i] = 0;
+
+		for(i = 0; i < hp->dev->mc_count; i++) {
+			addrs = dmi->dmi_addr;
+			dmi = dmi->next;
+
+			if(!(*addrs & 1))
+				continue;
+
+			crc = 0xffffffffU;
+			for(byte = 0; byte < 6; byte++) {
+				for(bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+					int test;
+
+					test = ((bit ^ crc) & 0x01);
+					crc >>= 1;
+					if(test)
+						crc = crc ^ poly;
+				}
+			}
+			crc >>= 26;
+			hash_table[crc >> 4] |= 1 << (crc & 0xf);
+		}
+		hme_write32(hp, &bregs->htable0, hash_table[0]);
+		hme_write32(hp, &bregs->htable1, hash_table[1]);
+		hme_write32(hp, &bregs->htable2, hash_table[2]);
+		hme_write32(hp, &bregs->htable3, hash_table[3]);
+	} else {
+		hme_write32(hp, &bregs->htable3, 0);
+		hme_write32(hp, &bregs->htable2, 0);
+		hme_write32(hp, &bregs->htable1, 0);
+		hme_write32(hp, &bregs->htable0, 0);
+	}
 
 	/* Set the RX and TX ring ptrs. */
 	HMD(("ring ptrs rxr[%08x] txr[%08x]\n",
@@ -1343,9 +1422,22 @@
 	     hme_read32(hp, &gregs->cfg)));
 
 #ifdef __sparc_v9__
+	/* XXX Can sun4d do these too? */
 	if(hp->happy_bursts & DMA_BURST64) {
+		u32 gcfg = GREG_CFG_BURST64;
+
+		/* I have no idea if I should set the extended
+		 * transfer mode bit for Cheerio, so for now I
+		 * do not.  -DaveM
+		 */
+		if((hp->happy_flags & HFLAG_PCI) == 0) {
+			mmu_set_sbus64(hp->happy_sbus_dev,
+				       hp->happy_bursts);
+			gcfg |= GREG_CFG_64BIT;
+		}
+
 		HMD(("64>"));
-		hme_write32(hp, &gregs->cfg, GREG_CFG_BURST64);
+		hme_write32(hp, &gregs->cfg, gcfg);
 	} else
 #endif
 	if(hp->happy_bursts & DMA_BURST32) {
@@ -1396,7 +1488,10 @@
 	/* Enable Big Mac hash table filter. */
 	HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ",
 	     hme_read32(hp, &bregs->rx_cfg)));
-	hme_write32(hp, &bregs->rx_cfg, BIGMAC_RXCFG_HENABLE);
+	rxcfg = BIGMAC_RXCFG_HENABLE;
+	if(hp->dev->flags & IFF_PROMISC)
+		rxcfg |= BIGMAC_RXCFG_PMISC;
+	hme_write32(hp, &bregs->rx_cfg, rxcfg);
 
 	/* Let the bits settle in the chip. */
 	udelay(10);
@@ -1433,7 +1528,7 @@
 		    hme_read32(hp, &bregs->rx_cfg) | BIGMAC_RXCFG_ENABLE);
 
 	/* Get the autonegotiation started, and the watch timer ticking. */
-	happy_meal_begin_auto_negotiation(hp, tregs);
+	happy_meal_begin_auto_negotiation(hp, tregs, NULL);
 
 	/* Success. */
 	return 0;
@@ -1505,7 +1600,7 @@
 
 	/* Only print messages for non-counter related interrupts. */
 	if(status & (GREG_STAT_RFIFOVF | GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND |
-		     GREG_STAT_MAXPKTERR | GREG_STAT_NORXD | GREG_STAT_RXERR |
+		     GREG_STAT_MAXPKTERR | GREG_STAT_RXERR |
 		     GREG_STAT_RXPERR | GREG_STAT_RXTERR | GREG_STAT_EOPERR |
 		     GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR |
 		     GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR |
@@ -1541,13 +1636,14 @@
 	}
 
 	if(status & GREG_STAT_NORXD) {
-		/* AIEEE, out of receive descriptors.  Check out our drop
-		 * processing in happy_meal_rx to see how we try very hard
-		 * to avoid this situation.
+		/* This is harmless, it just means the system is
+		 * quite loaded and the incomming packet rate was
+		 * faster than the interrupt handler could keep up
+		 * with.
 		 */
-		printk("%s: Happy Meal out of receive descriptors, aieee!\n",
+		printk(KERN_INFO "%s: Happy Meal out of receive "
+		       "descriptors, packet dropped.\n",
 		       hp->dev->name);
-		reset = 1;
 	}
 
 	if(status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) {
@@ -1662,13 +1758,6 @@
 		hp->tx_skbs[elem] = NULL;
 		hp->net_stats.tx_bytes+=skb->len;
 		
-#ifdef NEED_DMA_SYNCHRONIZATION
-#ifdef CONFIG_PCI
-		if(!(hp->happy_flags & HFLAG_PCI))
-#endif
-			mmu_sync_dma(kva_to_hva(hp, skb->data),
-				     skb->len, hp->happy_sbus_dev->my_bus);
-#endif
 		dev_kfree_skb(skb);
 
 		hp->net_stats.tx_packets++;
@@ -1797,7 +1886,8 @@
 			struct sk_buff *new_skb;
 
 			/* Now refill the entry, if we can. */
-			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE,
+						       (GFP_DMA|GFP_ATOMIC));
 			if(!new_skb) {
 				drops++;
 				goto drop_it;
@@ -1814,7 +1904,7 @@
 			/* Trim the original skb for the netif. */
 			skb_trim(skb, len);
 		} else {
-			struct sk_buff *copy_skb = dev_alloc_skb(len+2);
+			struct sk_buff *copy_skb = dev_alloc_skb(len + 2);
 
 			if(!copy_skb) {
 				drops++;
@@ -1908,7 +1998,8 @@
 			struct sk_buff *new_skb;
 
 			/* Now refill the entry, if we can. */
-			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE,
+						       (GFP_DMA|GFP_ATOMIC));
 			if(!new_skb) {
 				drops++;
 				goto drop_it;
@@ -1925,7 +2016,7 @@
 			/* Trim the original skb for the netif. */
 			skb_trim(skb, len);
 		} else {
-			struct sk_buff *copy_skb = dev_alloc_skb(len+2);
+			struct sk_buff *copy_skb = dev_alloc_skb(len + 2);
 
 			if(!copy_skb) {
 				drops++;
@@ -2083,7 +2174,8 @@
 			struct sk_buff *new_skb;
 
 			/* Now refill the entry, if we can. */
-			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE,
+						       (GFP_DMA | GFP_ATOMIC));
 			if(!new_skb) {
 				drops++;
 				goto drop_it;
@@ -2103,7 +2195,7 @@
 			/* Trim the original skb for the netif. */
 			skb_trim(skb, len);
 		} else {
-			struct sk_buff *copy_skb = dev_alloc_skb(len+2);
+			struct sk_buff *copy_skb = dev_alloc_skb(len + 2);
 
 			if(!copy_skb) {
 				drops++;
@@ -2328,12 +2420,39 @@
 }
 #endif
 
+static void quattro_sbus_interrupt(int irq, void *cookie, struct pt_regs *ptregs)
+{
+	struct quattro *qp = (struct quattro *)cookie;
+	int i;
+
+	for(i = 0; i < 4; i++) {
+		struct device *hdev = qp->happy_meals[i];
+		struct happy_meal *hp = (struct happy_meal *) hdev->priv;
+		volatile u32 *sreg = qp->irq_status[i];
+
+		if(sreg &&
+		   (hme_read32(hp, sreg) & (GREG_STAT_ERRORS |
+					    GREG_STAT_MIFIRQ |
+					    GREG_STAT_TXALL  |
+					    GREG_STAT_RXTOHOST)) != 0)
+			qp->handler(irq, hdev, ptregs);
+	}
+}
+
 static int happy_meal_open(struct device *dev)
 {
 	struct happy_meal *hp = (struct happy_meal *) dev->priv;
 	int res;
 
 	HMD(("happy_meal_open: "));
+
+	/* On SBUS Quattro QFE cards, all hme interrupts are concentrated
+	 * into a single source which we register handling at probe time.
+	 */
+	if((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) == HFLAG_QUATTRO) {
+		hp->qfe_parent->irq_status[hp->qfe_ent] = &hp->gregs->stat;
+		goto after_request_irq;
+	}
 #ifndef __sparc_v9__
 	if(sparc_cpu_model == sun4c) {
 		if(request_irq(dev->irq, &sun4c_happy_meal_interrupt,
@@ -2370,8 +2489,8 @@
 		       __irq_itoa(dev->irq));
 		return -EAGAIN;
 	}
-	HMD(("Init happy timer\n"));
-	init_timer(&hp->happy_timer);
+
+after_request_irq:
 	HMD(("to happy_meal_init\n"));
 	res = happy_meal_init(hp, 0);
 	if(!res) {
@@ -2390,7 +2509,17 @@
 	/* If auto-negotiation timer is running, kill it. */
 	del_timer(&hp->happy_timer);
 
-	free_irq(dev->irq, (void *)dev);
+	/* On Quattro QFE cards, all hme interrupts are concentrated
+	 * into a single source which we register handling at probe
+	 * time and never unregister.
+	 */
+	if((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO) {
+		free_irq(dev->irq, (void *)dev);
+	} else {
+		/* Zap the status register pointer. */
+		hp->qfe_parent->irq_status[hp->qfe_ent] = NULL;
+	}
+
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2429,10 +2558,23 @@
 		tx_add_log(hp, TXLOG_ACTION_TXMIT|TXLOG_ACTION_NBUFS, 0);
 		return 1;
 	}
+#ifdef __sparc_v9__
+	if ((unsigned long)(skb->data + skb->len) >= MAX_DMA_ADDRESS) {
+		struct sk_buff *new_skb = skb_copy(skb, GFP_DMA | GFP_ATOMIC);
+		if (!new_skb)
+			return 1;
+		dev_kfree_skb(skb);
+		skb = new_skb;
+	}
+#endif
 	len = skb->len;
 	entry = hp->tx_new;
 
 	SXD(("SX<l[%d]e[%d]>", len, entry));
+#ifdef NEED_DMA_SYNCHRONIZATION
+	mmu_sync_dma(kva_to_hva(hp, skb->data),
+		     skb->len, hp->happy_sbus_dev->my_bus);
+#endif
 	hp->tx_skbs[entry] = skb;
 	hp->happy_block->happy_meal_txd[entry].tx_addr = kva_to_hva(hp, skb->data);
 	hp->happy_block->happy_meal_txd[entry].tx_flags =
@@ -2622,9 +2764,6 @@
 	return &hp->net_stats;
 }
 
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
-
 static void happy_meal_set_multicast(struct device *dev)
 {
 	struct happy_meal *hp = (struct happy_meal *) dev->priv;
@@ -2634,10 +2773,6 @@
 	int i, j, bit, byte;
 	u32 crc, poly = CRC_POLYNOMIAL_LE;
 
-	/* Let the transmits drain. */
-	while(dev->tbusy)
-		schedule();
-
 	/* Lock out others. */
 	set_bit(0, (void *) &dev->tbusy);
 
@@ -2686,13 +2821,262 @@
 	dev->tbusy = 0;
 }
 
+/* Ethtool support... */
+static int happy_meal_ioctl(struct device *dev,
+			    struct ifreq *rq, int cmd)
+{
+	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	struct ethtool_cmd *ep_user = (struct ethtool_cmd *) rq->ifr_data;
+	struct ethtool_cmd ecmd;
+
+	if(cmd != SIOCETHTOOL)
+		return -EOPNOTSUPP;
+	if(copy_from_user(&ecmd, ep_user, sizeof(ecmd)))
+		return -EFAULT;
+
+	if(ecmd.cmd == SPARC_ETH_GSET) {
+		ecmd.supported =
+			(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+			 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+			 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
+
+		/* XXX hardcoded stuff for now */
+		ecmd.port = PORT_TP; /* XXX no MII support */
+		ecmd.transceiver = XCVR_INTERNAL; /* XXX no external xcvr support */
+		ecmd.phy_address = 0; /* XXX fixed PHYAD */
+
+		/* Record PHY settings. */
+		hp->sw_bmcr = happy_meal_tcvr_read(hp, hp->tcvregs, DP83840_BMCR);
+		hp->sw_lpa = happy_meal_tcvr_read(hp, hp->tcvregs, DP83840_LPA);
+		if(hp->sw_bmcr & BMCR_ANENABLE) {
+			ecmd.autoneg = AUTONEG_ENABLE;
+			ecmd.speed =
+				(hp->sw_lpa & (LPA_100HALF | LPA_100FULL)) ?
+				SPEED_100 : SPEED_10;
+			if(ecmd.speed == SPEED_100)
+				ecmd.duplex =
+					(hp->sw_lpa & (LPA_100FULL)) ?
+					DUPLEX_FULL : DUPLEX_HALF;
+			else
+				ecmd.duplex =
+					(hp->sw_lpa & (LPA_10FULL)) ?
+					DUPLEX_FULL : DUPLEX_HALF;
+		} else {
+			ecmd.autoneg = AUTONEG_DISABLE;
+			ecmd.speed =
+				(hp->sw_bmcr & BMCR_SPEED100) ?
+				SPEED_100 : SPEED_10;
+			ecmd.duplex =
+				(hp->sw_bmcr & BMCR_FULLDPLX) ?
+				DUPLEX_FULL : DUPLEX_HALF;
+		}
+		if(copy_to_user(ep_user, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	} else if(ecmd.cmd == SPARC_ETH_SSET) {
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		/* Verify the settings we care about. */
+		if(ecmd.autoneg != AUTONEG_ENABLE &&
+		   ecmd.autoneg != AUTONEG_DISABLE)
+			return -EINVAL;
+		if(ecmd.autoneg == AUTONEG_DISABLE &&
+		   ((ecmd.speed != SPEED_100 &&
+		     ecmd.speed != SPEED_10) ||
+		    (ecmd.duplex != DUPLEX_HALF &&
+		     ecmd.duplex != DUPLEX_FULL)))
+			return -EINVAL;
+
+		/* Ok, do it to it. */
+		del_timer(&hp->happy_timer);
+		happy_meal_begin_auto_negotiation(hp,
+						  hp->tcvregs,
+						  &ecmd);
+
+		return 0;
+	} else
+		return -EOPNOTSUPP;
+}
+
+void __init quattro_get_ranges(struct quattro *qp)
+{
+	int err;
+
+	err = prom_getproperty(qp->quattro_sbus_dev->prom_node,
+			       "ranges",
+			       (char *)&qp->ranges[0],
+			       sizeof(qp->ranges));
+	if(err == 0 || err == -1) {
+		qp->nranges = 0;
+		return;
+	}
+	qp->nranges = (err / sizeof(struct linux_prom_ranges));
+}
+
+static void __init quattro_apply_ranges(struct quattro *qp, struct happy_meal *hp)
+{
+	struct linux_sbus_device *sdev = hp->happy_sbus_dev;
+	int rng;
+
+	for(rng = 0; rng < qp->nranges; rng++) {
+		struct linux_prom_ranges *rngp = &qp->ranges[rng];
+		int reg;
+
+		for(reg = 0; reg < 5; reg++) {
+			if(sdev->reg_addrs[reg].which_io ==
+			   rngp->ot_child_space)
+				break;
+		}
+		if(reg == 5)
+			continue;
+
+		sdev->reg_addrs[reg].which_io = rngp->ot_parent_space;
+		sdev->reg_addrs[reg].phys_addr += rngp->ot_parent_base;
+	}
+}
+
+/* Given a happy meal sbus device, find it's quattro parent.
+ * If none exist, allocate and return a new one.
+ *
+ * Return NULL on failure.
+ */
+static struct quattro * __init quattro_sbus_find(struct linux_sbus_device *goal_sdev)
+{
+	struct linux_sbus *sbus;
+	struct linux_sbus_device *sdev;
+	struct quattro *qp;
+
+	for(qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
+		for(sdev = qp->quattro_sbus_dev->child;
+		    sdev != NULL;
+		    sdev = sdev->next) {
+			if(sdev == goal_sdev)
+				return qp;
+		}
+	}
+	for_each_sbus(sbus) {
+		for_each_sbusdev(sdev, sbus) {
+			if(sdev->child != NULL) {
+				struct linux_sbus_device *p;
+
+				for(p = sdev->child; p != NULL; p = p->next)
+					if(p == goal_sdev)
+						goto found;
+			}
+		}
+	}
+
+	/* Cannot find quattro parent, fail. */
+	return NULL;
+
+found:
+	qp = kmalloc(sizeof(struct quattro), GFP_KERNEL);
+	if(qp != NULL) {
+		int i;
+
+		for(i = 0; i < 4; i++) {
+			qp->irq_status[i] = NULL;
+			qp->happy_meals[i] = NULL;
+		}
+		qp->handler = NULL;
+		qp->quattro_sbus_dev = sdev;
+#ifdef CONFIG_PCI
+		qp->quattro_pci_dev = NULL;
+#endif
+		qp->next = qfe_sbus_list;
+		qfe_sbus_list = qp;
+		quattro_get_ranges(qp);
+	}
+	return qp;
+}
+
+#ifdef CONFIG_PCI
+static struct quattro * __init quattro_pci_find(struct pci_dev *pdev)
+{
+	struct pci_dev *bdev = pdev->bus->self;
+	struct quattro *qp;
+
+	if (!bdev) return NULL;
+	for(qp = qfe_pci_list; qp != NULL; qp = qp->next) {
+		if(qp->quattro_pci_dev == bdev)
+			return qp;
+	}
+	qp = kmalloc(sizeof(struct quattro), GFP_KERNEL);
+	if(qp != NULL) {
+		int i;
+
+		for(i = 0; i < 4; i++) {
+			qp->irq_status[i] = NULL;
+			qp->happy_meals[i] = NULL;
+		}
+		qp->handler = NULL;
+		qp->quattro_sbus_dev = NULL;
+		qp->quattro_pci_dev = bdev;
+		qp->next = qfe_pci_list;
+		qfe_pci_list = qp;
+
+		/* No range tricks necessary on PCI. */
+		qp->nranges = 0;
+	}
+	return qp;
+}
+#endif
+
+/* After all quattro cards have been probed, we call these functions
+ * to register the IRQ handlers.
+ */
+static void __init quattro_sbus_register_irqs(void)
+{
+	struct quattro *qp;
+
+	for(qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
+		int err;
+
+		/* Set the handler. */
+#ifndef __sparc_v9__
+		if(sparc_cpu_model == sun4c)
+			qp->handler = sun4c_happy_meal_interrupt;
+		else if(sparc_cpu_model == sun4c)
+			qp->handler = sun4d_happy_meal_interrupt;
+		else
+#endif
+#ifdef CONFIG_PCI
+		if(qp->quattro_pci_dev != NULL)
+			panic("QFE: PCI qfe in sbus_register_irqs!");
+		else
+#endif
+			qp->handler = happy_meal_interrupt;
+
+		err = request_irq(qp->quattro_sbus_dev->irqs[0],
+				  quattro_sbus_interrupt,
+				  SA_SHIRQ, "Quattro",
+				  qp);
+		if(err != 0) {
+			printk("Quattro: Fatal IRQ registery error %d.\n", err);
+			panic("QFE request irq");
+		}
+	}
+}
+
 static unsigned hme_version_printed = 0;
 
-static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_device *sdev)
+static int __init happy_meal_ether_init(struct device *dev, struct linux_sbus_device *sdev, int is_qfe)
 {
+	struct quattro *qp = NULL;
 	struct happy_meal *hp;
-	int i;
+	int i, qfe_slot = -1;
 
+	if(is_qfe) {
+		qp = quattro_sbus_find(sdev);
+		if(qp == NULL)
+			return ENODEV;
+		for(qfe_slot = 0; qfe_slot < 4; qfe_slot++)
+			if(qp->happy_meals[qfe_slot] == NULL)
+				break;
+		if(qfe_slot == 4)
+			return ENODEV;
+	}
 	if(dev == NULL) {
 		dev = init_etherdev(0, sizeof(struct happy_meal));
 	} else {
@@ -2703,9 +3087,16 @@
 	if(hme_version_printed++ == 0)
 		printk(version);
 
-	printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", dev->name);
+	if(qfe_slot != -1)
+		printk("%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet",
+		       dev->name, qfe_slot);
+	else
+		printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
+		       dev->name);
 
 	dev->base_addr = (long) sdev;
+
+	/* XXX Check for local-mac-address property on Quattro... -DaveM */
 	for(i = 0; i < 6; i++)
 		printk("%2.2x%c",
 		       dev->dev_addr[i] = idprom->id_ethaddr[i],
@@ -2727,6 +3118,13 @@
 		return ENODEV;
 	}
 
+	if(qp != NULL) {
+		hp->qfe_parent = qp;
+		hp->qfe_ent = qfe_slot;
+		qp->happy_meals[qfe_slot] = dev;
+		quattro_apply_ranges(qp, hp);
+	}
+
 	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
 			       sdev->num_registers, sdev);
 	hp->gregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
@@ -2784,6 +3182,9 @@
 	else if(hp->hm_revision != 0xa0)
 		hp->happy_flags = HFLAG_NOT_A0;
 
+	if(qp != NULL)
+		hp->happy_flags |= HFLAG_QUATTRO;
+
 	/* Get the supported DVMA burst sizes from our Happy SBUS. */
 	hp->happy_bursts = prom_getintdefault(hp->happy_sbus_dev->my_bus->prom_node,
 					      "burst-sizes", 0x00);
@@ -2817,6 +3218,8 @@
 	 */
 	happy_meal_set_initial_advertisement(hp);
 
+	init_timer(&hp->happy_timer);
+
 	hp->dev = dev;
 	dev->open = &happy_meal_open;
 	dev->stop = &happy_meal_close;
@@ -2830,6 +3233,7 @@
 		dev->hard_start_xmit = &happy_meal_start_xmit;
 	dev->get_stats = &happy_meal_get_stats;
 	dev->set_multicast_list = &happy_meal_set_multicast;
+	dev->do_ioctl = &happy_meal_ioctl;
 
 	dev->irq = sdev->irqs[0];
 	dev->dma = 0;
@@ -2846,14 +3250,35 @@
 }
 
 #ifdef CONFIG_PCI
-__initfunc(int happy_meal_pci_init(struct device *dev, struct pci_dev *pdev))
+static int __init happy_meal_pci_init(struct device *dev, struct pci_dev *pdev)
 {
+	struct quattro *qp = NULL;
 	struct pcidev_cookie *pcp;
 	struct happy_meal *hp;
 	unsigned long hpreg_base;
 	unsigned short pci_command;
-	int i, node;
+	int i, node, qfe_slot = -1;
+	char prom_name[64];
 
+	/* Now make sure pci_dev cookie is there. */
+	pcp = pdev->sysdata;
+	if(pcp == NULL || pcp->prom_node == -1) {
+		printk("happymeal(PCI): Some PCI device info missing\n");
+		return ENODEV;
+	}
+	node = pcp->prom_node;
+	
+	prom_getstring(node, "name", prom_name, sizeof(prom_name));
+	if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) {
+		qp = quattro_pci_find(pdev);
+		if(qp == NULL)
+			return ENODEV;
+		for(qfe_slot = 0; qfe_slot < 4; qfe_slot++)
+			if(qp->happy_meals[qfe_slot] == NULL)
+				break;
+		if(qfe_slot == 4)
+			return ENODEV;
+	}
 	if(dev == NULL) {
 		dev = init_etherdev(0, sizeof(struct happy_meal));
 	} else {
@@ -2864,15 +3289,28 @@
 	if(hme_version_printed++ == 0)
 		printk(version);
 
-	printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", dev->name);
+	if (!qfe_slot) {
+		prom_name[0] = 0;
+		if (!strncmp(dev->name, "eth", 3)) {
+			int i = simple_strtoul(dev->name + 3, NULL, 10);
+			sprintf(prom_name, "-%d", i + 3);
+		}
+		printk("%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
+		if (qp->quattro_pci_dev->vendor == PCI_VENDOR_ID_DEC &&
+		    qp->quattro_pci_dev->device == PCI_DEVICE_ID_DEC_21153)
+			printk("DEC 21153 PCI Bridge\n");
+		else
+			printk("unknown bridge %04x.%04x\n", 
+				qp->quattro_pci_dev->vendor, qp->quattro_pci_dev->device);
+	}
+	if(qfe_slot != -1)
+		printk("%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
+		       dev->name, qfe_slot);
+	else
+		printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
+		       dev->name);
 
 	dev->base_addr = (long) pdev;
-	for(i = 0; i < 6; i++)
-		printk("%2.2x%c",
-		       dev->dev_addr[i] = idprom->id_ethaddr[i],
-		       i == 5 ? ' ' : ':');
-
-	printk("\n");
 
 	hp = (struct happy_meal *)dev->priv;
 	memset(hp, 0, sizeof(*hp));
@@ -2880,6 +3318,12 @@
 	hp->happy_sbus_dev = NULL;
 	hp->happy_pci_dev = pdev;
 
+	if(qp != NULL) {
+		hp->qfe_parent = qp;
+		hp->qfe_ent = qfe_slot;
+		qp->happy_meals[qfe_slot] = dev;
+	}		
+
 	hpreg_base = pdev->base_address[0];
 	if((hpreg_base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
 		printk("happymeal(PCI): Cannot find proper PCI device base address.\n");
@@ -2887,13 +3331,14 @@
 	}
 	hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK;
 
-	/* Now make sure pci_dev cookie is there. */
-	pcp = pdev->sysdata;
-	if(pcp == NULL || pcp->prom_node == -1) {
-		printk("happymeal(PCI): Some PCI device info missing\n");
-		return ENODEV;
-	}
-	node = pcp->prom_node;
+	if (qfe_slot != -1 && prom_getproplen(node, "local-mac-address") == 6)
+		prom_getproperty(node, "local-mac-address", dev->dev_addr, 6);
+	else
+		memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+	for(i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':');
+
+	printk("\n");
 
 	/* Layout registers. */
 	hp->gregs      = (struct hmeal_gregs *)		(hpreg_base + 0x0000);
@@ -2912,6 +3357,9 @@
 	else if(hp->hm_revision != 0xa0)
 		hp->happy_flags = HFLAG_NOT_A0;
 
+	if(qp != NULL)
+		hp->happy_flags |= HFLAG_QUATTRO;
+
 	/* And of course, indicate this is PCI. */
 	hp->happy_flags |= HFLAG_PCI;
 
@@ -2937,29 +3385,32 @@
 	hp->timer_ticks = 0;
 	happy_meal_set_initial_advertisement(hp);
 
+	init_timer(&hp->happy_timer);
+
 	hp->dev = dev;
 	dev->open = &happy_meal_open;
 	dev->stop = &happy_meal_close;
 	dev->hard_start_xmit = &pci_happy_meal_start_xmit;
 	dev->get_stats = &happy_meal_get_stats;
 	dev->set_multicast_list = &happy_meal_set_multicast;
+	dev->do_ioctl = &happy_meal_ioctl;
 	dev->irq = pdev->irq;
 	dev->dma = 0;
 	ether_setup(dev);
 
 	/* If we don't do this, nothing works. */
-	pcibios_read_config_word(pdev->bus->number,
-				 pdev->devfn,
-				 PCI_COMMAND, &pci_command);
+	pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
 	pci_command |= PCI_COMMAND_MASTER;
-	pcibios_write_config_word(pdev->bus->number,
-				  pdev->devfn,
-				  PCI_COMMAND, pci_command);
-
-	/* Set the latency timer as well, PROM leaves it at zero. */
-	pcibios_write_config_byte(pdev->bus->number,
-				  pdev->devfn,
-				  PCI_LATENCY_TIMER, 240);
+	pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+
+	/* Set the latency timer and cache line size as well,
+	 * PROM leaves it at zero.
+	 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128);
+#ifdef __sparc_v9__
+	/* NOTE: Cache line size is in 32-bit word units. */
+	pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10);
+#endif
 
 #ifdef MODULE
 	/* We are home free at this point, link us in to the happy
@@ -2973,7 +3424,7 @@
 }
 #endif
 
-__initfunc(int happy_meal_probe(struct device *dev))
+int __init happy_meal_probe(struct device *dev)
 {
 	struct linux_sbus *bus;
 	struct linux_sbus_device *sdev = 0;
@@ -2990,11 +3441,18 @@
 				dev = NULL;
 			if(!strcmp(sdev->prom_name, "SUNW,hme")) {
 				cards++;
-				if((v = happy_meal_ether_init(dev, sdev)))
+				if((v = happy_meal_ether_init(dev, sdev, 0)))
+					return v;
+			} else if(!strcmp(sdev->prom_name, "qfe") ||
+				  !strcmp(sdev->prom_name, "SUNW,qfe")) {
+				cards++;
+				if((v = happy_meal_ether_init(dev, sdev, 1)))
 					return v;
 			}
 		}
 	}
+	if(cards != 0)
+		quattro_sbus_register_irqs();
 #ifdef CONFIG_PCI
 	if(pci_present()) {
 		struct pci_dev *pdev;
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunhme.h linux/drivers/net/sunhme.h
--- v2.2.3/linux/drivers/net/sunhme.h	Thu Nov 19 09:56:28 1998
+++ linux/drivers/net/sunhme.h	Mon Mar 15 16:11:30 1999
@@ -507,6 +507,8 @@
 	asleep   = 3,  /* Time inactive.                                     */
 };
 
+struct quattro;
+
 /* Happy happy, joy joy! */
 struct happy_meal {
 	struct hmeal_gregs       *gregs;          /* Happy meal global registers       */
@@ -559,6 +561,8 @@
 	struct pci_dev		 *happy_pci_dev;
 #endif
 	struct device            *dev;            /* Backpointer                       */
+	struct quattro		 *qfe_parent;	  /* For Quattro cards                 */
+	int			  qfe_ent;	  /* Which instance on quattro         */
 	struct happy_meal        *next_module;
 };
 
@@ -575,10 +579,28 @@
 #define HFLAG_INIT                0x00000200      /* Init called at least once         */
 #define HFLAG_LINKUP              0x00000400      /* 1 = Link is up                    */
 #define HFLAG_PCI                 0x00000800      /* PCI based Happy Meal              */
+#define HFLAG_QUATTRO		  0x00001000      /* On QFE/Quattro card	       */
 
 #define HFLAG_20_21  (HFLAG_POLLENABLE | HFLAG_FENABLE)
 #define HFLAG_NOT_A0 (HFLAG_POLLENABLE | HFLAG_FENABLE | HFLAG_LANCE | HFLAG_RXCV)
 
+/* Support for QFE/Quattro cards. */
+struct quattro {
+	volatile u32		 *irq_status[4];
+	struct device		 *happy_meals[4];
+	void (*handler)(int, void *, struct pt_regs *);
+
+	struct linux_sbus_device *quattro_sbus_dev;
+#ifdef CONFIG_PCI
+	struct pci_dev		 *quattro_pci_dev;
+#endif
+	struct quattro		 *next;
+
+	/* PROM ranges, if any. */
+	struct linux_prom_ranges  ranges[8];
+	int			  nranges;
+};
+
 /* We use this to acquire receive skb's that we can DMA directly into. */
 #define ALIGNED_RX_SKB_ADDR(addr) \
         ((((unsigned long)(addr) + (64 - 1)) & ~(64 - 1)) - (unsigned long)(addr))
@@ -606,7 +628,16 @@
 		return (u32) virt_to_bus((volatile void *)addr);
 	else
 #endif
-		return (u32) ((unsigned long)addr);
+	{
+#ifdef __sparc_v9__
+		if (((unsigned long) addr) >= MAX_DMA_ADDRESS) {
+			printk("sunhme: Bogus DMA buffer address "
+			       "[%016lx]\n", ((unsigned long) addr));
+			panic("DMA address too large, tell DaveM");
+		}
+#endif
+		return sbus_dvma_addr(addr);
+	}
 }
 
 extern inline unsigned int hme_read32(struct happy_meal *hp,
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.2.3/linux/drivers/net/sunlance.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/net/sunlance.c	Sun Mar 21 07:22:00 1999
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.81 1998/08/10 09:08:23 jj Exp $
+/* $Id: sunlance.c,v 1.85 1999/03/21 05:22:05 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -56,12 +56,16 @@
  *
  * 1.11:
  *	12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz)
+ *
+ * 1.12:
+ * 	 11/3/99: Fixed SMP race in lance_start_xmit found by davem.
+ * 	          Anton Blanchard (anton@progsoc.uts.edu.au)
  */
 
 #undef DEBUG_DRIVER
 
 static char *version =
-	"sunlance.c:v1.11 27/Dec/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n";
+	"sunlance.c:v1.12 11/Mar/99 Miguel de Icaza (miguel@nuclecu.unam.mx)\n";
 
 static char *lancestr = "LANCE";
 static char *lancedma = "LANCE DMA";
@@ -252,6 +256,7 @@
 	struct device		 *dev;		  /* Backpointer	*/
 	struct lance_private	 *next_module;
 	struct linux_sbus        *sbus;
+	struct timer_list         multicast_timer;
 };
 
 #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
@@ -326,8 +331,6 @@
 	lp->rx_new = lp->tx_new = 0;
 	lp->rx_old = lp->tx_old = 0;
 
-	ib->mode = 0;
-
 	/* Copy the ethernet address to the lance init block
 	 * Note that on the sparc you need to swap the ethernet address.
 	 * Note also we want the CPU ptr of the init_block here.
@@ -384,10 +387,6 @@
 	ib->tx_ptr = leptr;
 	if (ZERO)
 		printk ("TX ptr: %8.8x\n", leptr);
-
-	/* Clear the multicast filter */
-	ib->filter [0] = 0;
-	ib->filter [1] = 0;
 }
 
 static int init_restart_lance (struct lance_private *lp)
@@ -668,6 +667,7 @@
 {
 	struct lance_private *lp = (struct lance_private *)dev->priv;
 	volatile struct lance_regs *ll = lp->ll;
+	volatile struct lance_init_block *ib = lp->init_block;
 	int status = 0;
 
 	last_dev = dev;
@@ -686,6 +686,16 @@
 	if (lp->ledma)
 		lp->ledma->regs->dma_test = ((__u32) lp->init_block_dvma) & 0xff000000;
 
+	/* Set mode and clear multicast filter only at device open,
+	   so that lance_init_ring() called at any error will not
+	   forget multicast filters.
+
+	   BTW it is common bug in all lance drivers! --ANK
+	 */
+	ib->mode = 0;
+	ib->filter [0] = 0;
+	ib->filter [1] = 0;
+
 	lance_init_ring (dev);
 	load_csrs (lp);
 
@@ -742,6 +752,7 @@
 
 	dev->start = 0;
 	dev->tbusy = 1;
+	del_timer(&lp->multicast_timer);
 
 	/* Stop the card */
 	ll->rap = LE_CSR0;
@@ -791,27 +802,19 @@
 	volatile unsigned long flush;
 	unsigned long flags;
 	int entry, skblen, len;
-	int status = 0;
-	static int outs;
 
-	/* Transmitter timeout, serious problems */
-	if (dev->tbusy) {
+	if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) {
 		int tickssofar = jiffies - dev->trans_start;
-	    
-		if (tickssofar < 100) {
-			status = -1;
-		} else {
-			printk ("%s: transmit timed out, status %04x, reset\n",
-				dev->name, ll->rdp);
-			lance_reset (dev);
-		}
-		return status;
-	}
 
-	/* Block a timer-based transmit from overlapping. */
-	if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) {
-		printk ("Transmitter access conflict.\n");
-		return -1;
+		if (tickssofar < 100)
+			return 1;
+
+		printk ("%s: transmit timed out, status %04x, reset\n",
+			dev->name, ll->rdp);
+		lp->stats.tx_errors++;
+		lance_reset (dev);
+
+		return 1;
 	}
 
 	skblen = skb->len;
@@ -820,13 +823,13 @@
 
 	if (!TX_BUFFS_AVAIL) {
 		restore_flags(flags);
-		return -1;
+		return 1;
 	}
 
 	len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;
-	
+
 	lp->stats.tx_bytes += len;
-	
+
 	entry = lp->tx_new & TX_RING_MOD_MASK;
 	ib->btx_ring [entry].length = (-len) | 0xf000;
 	ib->btx_ring [entry].misc = 0;
@@ -842,7 +845,6 @@
 	ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
 	lp->tx_new = (lp->tx_new+1) & TX_RING_MOD_MASK;
 
-	outs++;
 	/* Kick the lance: transmit now */
 	ll->rdp = LE_C0_INEA | LE_C0_TDMD;
 	dev->trans_start = jiffies;
@@ -857,7 +859,7 @@
 		flush = ll->rdp;
 
 	restore_flags(flags);
-	return status;
+	return 0;
 }
 
 static struct net_device_stats *lance_get_stats (struct device *dev)
@@ -879,7 +881,7 @@
 	u32 crc, poly = CRC_POLYNOMIAL_LE;
 	
 	/* set all multicast bits */
-	if (dev->flags & IFF_ALLMULTI){ 
+	if (dev->flags & IFF_ALLMULTI) {
 		ib->filter [0] = 0xffffffff;
 		ib->filter [1] = 0xffffffff;
 		return;
@@ -889,31 +891,29 @@
 	ib->filter [1] = 0;
 
 	/* Add addresses */
-	for (i = 0; i < dev->mc_count; i++){
+	for (i = 0; i < dev->mc_count; i++) {
 		addrs = dmi->dmi_addr;
 		dmi   = dmi->next;
 
 		/* multicast address? */
 		if (!(*addrs & 1))
 			continue;
-		
+
 		crc = 0xffffffff;
-		for (byte = 0; byte < 6; byte++)
+		for (byte = 0; byte < 6; byte++) {
 			for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
 				int test;
 
 				test = ((bit ^ crc) & 0x01);
 				crc >>= 1;
 
-				if (test) {
+				if (test)
 					crc = crc ^ poly;
-				}
 			}
-		
+		}
 		crc = crc >> 26;
 		mcast_table [crc >> 4] |= 1 << (crc & 0xf);
 	}
-	return;
 }
 
 static void lance_set_multicast (struct device *dev)
@@ -922,12 +922,33 @@
 	volatile struct lance_init_block *ib = lp->init_block;
 	volatile struct lance_regs *ll = lp->ll;
 
-	while (dev->tbusy)
-		schedule();
+	if (!dev->start)
+		return;
+
+	if (dev->tbusy) {
+		mod_timer(&lp->multicast_timer, jiffies + 2);
+		return;
+	}
+	/* This CANNOT be correct. Chip is running
+	   and dev->tbusy may change any moment.
+	   It is useless to set it.
+
+	   Generally, usage of dev->tbusy in this driver is completely
+	   wrong.
+
+	   I protected calls to this function
+	   with start_bh_atomic, so that set_multicast_list
+	   and hard_start_xmit are serialized now by top level. --ANK
+
+	   The same is true about a2065.
+	 */
 	set_bit (0, (void *) &dev->tbusy);
 
-	while (lp->tx_old != lp->tx_new)
-		schedule();
+	if (lp->tx_old != lp->tx_new) {
+		mod_timer(&lp->multicast_timer, jiffies + 4);
+		dev->tbusy = 0;
+		return;
+	}
 
 	ll->rap = LE_CSR0;
 	ll->rdp = LE_C0_STOP;
@@ -942,6 +963,7 @@
 	load_csrs (lp);
 	init_restart_lance (lp);
 	dev->tbusy = 0;
+	mark_bh(NET_BH);
 }
 
 __initfunc(static int 
@@ -1100,6 +1122,16 @@
 
 	dev->dma = 0;
 	ether_setup (dev);
+
+	/* We cannot sleep if the chip is busy during a
+	 * multicast list update event, because such events
+	 * can occur from interrupts (ex. IPv6).  So we
+	 * use a timer to try again later when necessary. -DaveM
+	 */
+	init_timer(&lp->multicast_timer);
+	lp->multicast_timer.data = (unsigned long) dev;
+	lp->multicast_timer.function =
+		(void (*)(unsigned long)) &lance_set_multicast;
 
 #ifdef MODULE
 	dev->ifindex = dev_new_index();
diff -u --recursive --new-file v2.2.3/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c
--- v2.2.3/linux/drivers/net/sunqe.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/sunqe.c	Mon Mar 15 16:11:30 1999
@@ -36,7 +36,6 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/auxio.h>
-#include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 
@@ -145,7 +144,7 @@
 	for(i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb;
 
-		skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags);
+		skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags | GFP_DMA);
 		if(!skb)
 			continue;
 
@@ -155,8 +154,7 @@
 		skb_put(skb, ETH_FRAME_LEN);
 		skb_reserve(skb, 34);
 
-		qb->qe_rxd[i].rx_addr =
-			(u32) ((unsigned long)skb->data);
+		qb->qe_rxd[i].rx_addr = sbus_dvma_addr(skb->data);
 		qb->qe_rxd[i].rx_flags =
 			(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
 	}
@@ -447,10 +445,6 @@
 		skb = qep->tx_skbs[elem];
 		qep->tx_skbs[elem] = NULL;
 		qep->net_stats.tx_bytes+=skb->len;
-#ifdef NEED_DMA_SYNCHRONIZATION
-		mmu_sync_dma(((u32)((unsigned long)skb->data)),
-			     skb->len, qep->qe_sbusdev->my_bus);
-#endif
 		dev_kfree_skb(skb);
 
 		qep->net_stats.tx_packets++;
@@ -498,22 +492,28 @@
 	drop_it:
 			/* Return it to the QE. */
 			qep->net_stats.rx_dropped++;
-			this->rx_addr =
-				(u32) ((unsigned long)qep->rx_skbs[elem]->data);
+			this->rx_addr = sbus_dvma_addr(qep->rx_skbs[elem]->data);
 			this->rx_flags =
 				(RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH));
 			goto next;
 		}
 		skb = qep->rx_skbs[elem];
 #ifdef NEED_DMA_SYNCHRONIZATION
-		mmu_sync_dma(((u32)((unsigned long)skb->data)),
+#ifdef __sparc_v9__
+		if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) {
+			printk("sunqe: Bogus DMA buffer address "
+			       "[%016lx]\n", ((unsigned long) skb->data));
+			panic("DMA address too large, tell DaveM");
+		}
+#endif
+		mmu_sync_dma(sbus_dvma_addr(skb->data),
 			     skb->len, qep->qe_sbusdev->my_bus);
 #endif
 		if(len > RX_COPY_THRESHOLD) {
 			struct sk_buff *new_skb;
 
 			/* Now refill the entry, if we can. */
-			new_skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			new_skb = qe_alloc_skb(RX_BUF_ALLOC_SIZE, (GFP_DMA|GFP_ATOMIC));
 			if(!new_skb) {
 				drops++;
 				goto drop_it;
@@ -524,8 +524,7 @@
 			skb_put(new_skb, ETH_FRAME_LEN);
 			skb_reserve(new_skb, 34);
 
-			rxbase[elem].rx_addr =
-				(u32) ((unsigned long)new_skb->data);
+			rxbase[elem].rx_addr = sbus_dvma_addr(new_skb->data);
 			rxbase[elem].rx_flags =
 				(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
 
@@ -545,8 +544,7 @@
 			eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0);
 
 			/* Reuse original ring buffer. */
-			rxbase[elem].rx_addr =
-				(u32) ((unsigned long)skb->data);
+			rxbase[elem].rx_addr = sbus_dvma_addr(skb->data);
 			rxbase[elem].rx_flags =
 				(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
 
@@ -741,6 +739,19 @@
 	if(!TX_BUFFS_AVAIL(qep))
 		return 1;
 
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef __sparc_v9__
+	if ((unsigned long) (skb->data + skb->len) >= MAX_DMA_ADDRESS) {
+		struct sk_buff *new_skb = skb_copy(skb, GFP_DMA | GFP_ATOMIC);
+		if(!new_skb)
+			return 1;
+		dev_kfree_skb(skb);
+		skb = new_skb;
+	}
+#endif
+	mmu_sync_dma(sbus_dvma_addr(skb->data),
+		     skb->len, qep->qe_sbusdev->my_bus);
+#endif
 	len = skb->len;
 	entry = qep->tx_new;
 
@@ -749,7 +760,7 @@
 
 	qep->tx_skbs[entry] = skb;
 
-	qep->qe_block->qe_txd[entry].tx_addr = (u32) ((unsigned long) skb->data);
+	qep->qe_block->qe_txd[entry].tx_addr = sbus_dvma_addr(skb->data);
 	qep->qe_block->qe_txd[entry].tx_flags =
 		(TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH));
 	qep->tx_new = NEXT_TX(entry);
diff -u --recursive --new-file v2.2.3/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.2.3/linux/drivers/net/wavelan.c	Fri Oct  9 13:27:09 1998
+++ linux/drivers/net/wavelan.c	Wed Mar 10 16:51:35 1999
@@ -290,24 +290,26 @@
   wv_16_on(ioaddr, hacr);
 } /* psa_write */
 
-#ifdef PSA_CRC
+#ifdef SET_PSA_CRC
 /*------------------------------------------------------------------*/
 /*
- * Calculate the PSA CRC (not tested yet)
- * As the WaveLAN drivers don't use the CRC, I won't use it either.
- * Thanks to Nico Valster <NVALSTER@wcnd.nl.lucent.com> for the code
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
  * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA).
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
  */
-static u_short
-psa_crc(u_short *	psa,	/* The PSA */
+static inline u_short
+psa_crc(u_char *	psa,	/* The PSA */
 	int		size)	/* Number of short for CRC */
 {
   int		byte_cnt;	/* Loop on the PSA */
   u_short	crc_bytes = 0;	/* Data in the PSA */
   int		bit_cnt;	/* Loop on the bits of the short */
 
-  for(byte_cnt = 0; byte_cnt <= size; byte_cnt++ )
+  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
     {
       crc_bytes ^= psa[byte_cnt];	/* Its an xor */
 
@@ -322,7 +324,47 @@
 
   return crc_bytes;
 } /* psa_crc */
-#endif	/* PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void
+update_psa_checksum(device *	dev,
+		    u_long	ioaddr,
+		    u_short	hacr)
+{
+  psa_t		psa;
+  u_short	crc;
+
+  /* read the parameter storage area */
+  psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /* update the checksum */
+  crc = psa_crc((unsigned char *) &psa,
+		sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
+		- sizeof(psa.psa_crc_status));
+
+  psa.psa_crc[0] = crc & 0xFF;
+  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+  /* Write it ! */
+  psa_write(ioaddr, hacr, (char *)&psa.psa_crc - (char *)&psa,
+	    (unsigned char *)&psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+  /* Check again (luxury !) */
+  crc = psa_crc ((unsigned char *) &psa,
+		 sizeof(psa) - sizeof(psa.psa_crc_status));
+
+  if(crc != 0)
+    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+} /* update_psa_checksum */
+#endif	/* SET_PSA_CRC */
 
 /*------------------------------------------------------------------*/
 /*
@@ -706,21 +748,21 @@
       unsigned short	ias_addr;
 
       /* Check mc_config command */
-      if(status & AC_SFLD_OK != 0)
+      if((status & AC_SFLD_OK) != 0)
 	printk(KERN_INFO "wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
 	       dev->name, str, status);
 
       /* check ia-config command */
       ias_addr = mcs_addr - sizeof(ac_ias_t);
       obram_read(ioaddr, acoff(ias_addr, ac_status), (unsigned char *)&status, sizeof(status));
-      if(status & AC_SFLD_OK != 0)
+      if((status & AC_SFLD_OK) != 0)
 	printk(KERN_INFO "wv_config_complete(): set_MAC_address; status = 0x%x\n",
 	       dev->name, str, status);
 
       /* Check config command. */
       cfg_addr = ias_addr - sizeof(ac_cfg_t);
       obram_read(ioaddr, acoff(cfg_addr, ac_status), (unsigned char *)&status, sizeof(status));
-      if(status & AC_SFLD_OK != 0)
+      if((status & AC_SFLD_OK) != 0)
 	printk(KERN_INFO "wv_config_complete(): configure; status = 0x%x\n",
 	       dev->name, str, status);
 #endif	/* DEBUG_CONFIG_ERROR */
@@ -1890,6 +1932,10 @@
 	  /* Disable NWID in the mmc (no filtering). */
 	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
 	}
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
       break;
 
     case SIOCGIWNWID:
@@ -1946,6 +1992,10 @@
       psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
 	       (unsigned char *) &psa.psa_thr_pre_set, 1);
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
       mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
       break;
 
@@ -1993,6 +2043,10 @@
 
 	   mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
 	 }
+#ifdef SET_PSA_CRC
+       /* update the Wavelan checksum */
+       update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
        break;
 
      case SIOCGIWENCODE:
@@ -2206,6 +2260,10 @@
       psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
 	       (unsigned char *)&psa.psa_quality_thr, 1);
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
       mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
       break;
 
@@ -2316,7 +2374,7 @@
   mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
 
   /* Copy data to wireless stuff. */
-  wstats->status = m.mmr_dce_status;
+  wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
   wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
   wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
   wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
@@ -2892,6 +2950,10 @@
 		(unsigned char *)&psa.psa_quality_thr, 1);
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_conf_status - (char *)&psa,
 		(unsigned char *)&psa.psa_conf_status, 1);
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
 #endif
     }
 
@@ -2918,21 +2980,18 @@
   m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
   m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
 
-  /* Encryption stuff is missing. */
-
   /*
    * Set default modem control parameters.
    * See NCR document 407-0024326 Rev. A.
    */
   m.mmw_jabber_enable = 0x01;
+  m.mmw_freeze = 0;
   m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
   m.mmw_ifs = 0x20;
   m.mmw_mod_delay = 0x04;
   m.mmw_jam_time = 0x38;
 
-  m.mmw_encr_enable = 0;
   m.mmw_des_io_invert = 0;
-  m.mmw_freeze = 0;
   m.mmw_decay_prm = 0;
   m.mmw_decay_updat_prm = 0;
 
@@ -3398,39 +3457,29 @@
   /* Create a configure action. */
   memset(&cfg, 0x00, sizeof(cfg));
 
-#if	0
-  /*
-   * The default board configuration
-   */
-  cfg.fifolim_bytecnt 	= 0x080c;
-  cfg.addrlen_mode  	= 0x2600;
-  cfg.linprio_interframe	= 0x7820;	/* IFS=120, ACS=2 */
-  cfg.slot_time      	= 0xf00c;	/* slottime=12    */
-  cfg.hardware	     	= 0x0008;	/* tx even without CD */
-  cfg.min_frame_len   	= 0x0040;
-#endif	/* 0 */
-
   /*
    * For Linux we invert AC_CFG_ALOC() so as to conform
    * to the way that net packets reach us from above.
    * (See also ac_tx_t.)
+   *
+   * Updated from Wavelan Manual WCIN085B
    */
   cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
-  cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
-  cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
+  cfg.cfg_fifolim = AC_CFG_FIFOLIM(4);
+  cfg.cfg_byte8 = AC_CFG_SAV_BF(1) |
 		  AC_CFG_SRDY(0);
   cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
 		  AC_CFG_ILPBCK(0) |
 		  AC_CFG_PRELEN(AC_CFG_PLEN_2) |
 		  AC_CFG_ALOC(1) |
 		  AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
-  cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
-		   AC_CFG_ACR(0) |
+  cfg.cfg_byte10 = AC_CFG_BOFMET(1) |
+		   AC_CFG_ACR(6) |
 		   AC_CFG_LINPRIO(0);
-  cfg.cfg_ifs = 32;
-  cfg.cfg_slotl = 0;
+  cfg.cfg_ifs = 0x20;
+  cfg.cfg_slotl = 0x0C;
   cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
-		   AC_CFG_SLTTMHI(2);
+		   AC_CFG_SLTTMHI(0);
   cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
 		   AC_CFG_BTSTF(0) |
 		   AC_CFG_CRC16(0) |
@@ -4016,6 +4065,10 @@
 #endif
 	  psa_write(ioaddr, HACR_DEFAULT,
 		    psaoff(0, psa_int_req_no), &irq_mask, 1);
+#ifdef SET_PSA_CRC
+	  /* update the Wavelan checksum */
+	  update_psa_checksum(dev, ioaddr, HACR_DEFAULT);
+#endif
 	  wv_hacr_reset(ioaddr);
 	}
     }
@@ -4196,7 +4249,7 @@
 init_module(void)
 {
   mac_addr	mac;		/* MAC address (check WaveLAN existence) */
-  int		ret = 0;
+  int		ret = -EIO;	/* Return error if no cards found */
   int		i;
 
 #ifdef DEBUG_MODULE_TRACE
@@ -4241,7 +4294,11 @@
 	      /* Deallocate everything. */
 	      /* Note: if dev->priv is mallocated, there is no way to fail. */
 	      kfree_s(dev, sizeof(struct device));
-	      ret = -EIO;
+	    }
+	  else
+	    {
+              /* If at least one device OK, we do not fail */
+              ret = 0;
 	    }
 	}	/* if there is something at the address */
     }		/* Loop on all addresses. */
diff -u --recursive --new-file v2.2.3/linux/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- v2.2.3/linux/drivers/net/wavelan.h	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/wavelan.h	Wed Mar 10 16:51:35 1999
@@ -28,6 +28,8 @@
 {
   { 0x08, 0x00, 0x0E },		/* AT&T WaveLAN (standard) & DEC RoamAbout */
   { 0x08, 0x00, 0x6A },		/* AT&T WaveLAN (alternate) */
+  { 0x00, 0x00, 0xE1 },		/* Hitachi Wavelan */
+  { 0x00, 0x60, 0x1D }		/* Lucent Wavelan (another one) */
   /* Add your card here and send me the patch! */
 };
 
@@ -293,6 +295,7 @@
 #define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
 #define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */
 #define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
+#define MMR_DCE_STATUS			0x0F	/* mask to get the bits */
   unsigned char	mmr_dsp_id;		/* DSP ID (AA = Daedalus rev A) */
   unsigned char	mmr_unused2[2];		/* unused */
   unsigned char	mmr_correct_nwid_l;	/* # of correct NWIDs rxd (low) */
diff -u --recursive --new-file v2.2.3/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- v2.2.3/linux/drivers/net/wavelan.p.h	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/wavelan.p.h	Wed Mar 10 16:51:35 1999
@@ -18,13 +18,7 @@
  * This driver provides a Linux interface to the WaveLAN ISA hardware.
  * The WaveLAN is a product of Lucent (http://www.wavelan.com/).
  * This division was formerly part of NCR and then AT&T.
- * WaveLANs are also distributed by DEC (RoamAbout), Digital Ocean and
- * Aironet (Arlan).  If you have one of those products, you will need to
- * make some changes below.
- *
- * This driver is still beta software.  A lot of bugs have been corrected,
- * a lot of functionality is implemented, and the whole appears stable,
- * but there is still room for improvement (encryption, performance).
+ * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean.
  *
  * To learn how to use this driver, read the NET3 HOWTO.
  * If you want to exploit the many other functionalities, read the comments
@@ -35,12 +29,22 @@
 
 /* ------------------------ SPECIFIC NOTES ------------------------ */
 /*
+ * Web page
+ * --------
+ *	I try to maintain a web page with the Wireless LAN Howto at :
+ *		http://www-uk.hpl.hp.com/people/jt/Linux/Wavelan.html
+ *
  * wavelan.o is too darned big
  * ---------------------------
  *	That's true!  There is a very simple way to reduce the driver
  *	object by 33%!  Comment out the following line:
  *		#include <linux/wireless.h>
  *
+ * Debugging and options
+ * ---------------------
+ *	You will find below a set of '#define" allowing a very fine control
+ *	on the driver behaviour and the debug messages printed.
+ *
  * MAC address and hardware detection:
  * -----------------------------------
  *	The detection code for the WaveLAN checks that the first three
@@ -60,23 +64,6 @@
  *	3) Compile and verify
  *	4) Send me the MAC code.  I will include it in the next version.
  *
- * "CU Inactive" message at boot up:
- * -----------------------------------
- *	It seems that there is some weird timing problem with the
- *	Intel microcontroller.  In fact, this message is triggered by a
- *	bad reading of the onboard RAM the first time we read the
- *	control block.  If you ignore this message, all is OK (but in
- *	fact, currently, it resets the WaveLAN hardware).
- *
- *	There are two ways to get rid of that problem.  The first
- *	is to add a dummy read of the scb at the end of
- *	wv_82586_config.  The second is to add the timers
- *	wv_synchronous_cmd and wv_ack (the udelay just after the
- *	waiting loops--it seems that the controller is not totally ready
- *	when it says it is).
- *
- *	In the current code, I use the second solution (to be
- *	consistent with the original solution of Bruce Janson).
  */
 
 /* --------------------- WIRELESS EXTENSIONS --------------------- */
@@ -273,6 +260,22 @@
  *	- encryption setting from Brent Elphick (thanks a lot!)
  *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
  *
+ * Other changes (not by me) :
+ * -------------------------
+ *	- Spelling and gramar "rectification".
+ *
+ * Changes made for release in 2.0.37 & 2.2.2 :
+ * ------------------------------------------
+ *	- Correct status in /proc/net/wireless
+ *	- Set PSA CRC to make PtP diagnostic tool happy (Bob Gray)
+ *	- Module init code don't fail if we found at least one card in
+ *	  the address list (Karlis Peisenieks)
+ *	- Missing parenthesis (Christopher Peterson)
+ *	- Correct i82586 configuration parameters
+ *	- Encryption initialisation bug (Robert McCormack)
+ *	- New mac addresses detected in the probe
+ *	- Increase watchdog for busy envirnoments
+ *
  * Wishes & dreams:
  * ----------------
  *	- roaming
@@ -342,9 +345,9 @@
 
 /* Options */
 #define USE_PSA_CONFIG		/* Use info from the PSA. */
+#define SET_PSA_CRC		/* Calculate and set the CRC on PSA */
 #define IGNORE_NORMAL_XMIT_ERRS	/* Don't bother with normal conditions. */
 #undef STRUCT_CHECK		/* Verify padding of structures. */
-#undef PSA_CRC			/* Check CRC in PSA. */
 #undef OLDIES			/* old code (to redo) */
 #undef RECORD_SNR		/* to redo */
 #undef EEPROM_IS_PROTECTED	/* doesn't seem to be necessary */
@@ -359,11 +362,11 @@
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v16 (wireless extensions) 17/4/97\n";
+static const char	*version	= "wavelan.c : v18 (wireless extensions) 18/2/99\n";
 #endif
 
 /* Watchdog temporisation */
-#define	WATCHDOG_JIFFIES	32	/* TODO:  express in HZ. */
+#define	WATCHDOG_JIFFIES	256	/* TODO:  express in HZ. */
 
 /* Macro to get the number of elements in an array */
 #define	NELS(a)				(sizeof(a) / sizeof(a[0]))
diff -u --recursive --new-file v2.2.3/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.2.3/linux/drivers/pci/oldproc.c	Tue Jan 19 11:32:51 1999
+++ linux/drivers/pci/oldproc.c	Sun Mar 21 09:28:24 1999
@@ -101,6 +101,7 @@
 	DEVICE( DEC,		DEC_21052,	"DC21052"),
 	DEVICE( DEC,		DEC_21150,	"DC21150"),
 	DEVICE( DEC,		DEC_21152,	"DC21152"),
+	DEVICE( DEC,		DEC_21153,	"DC21153"),
 	DEVICE( CIRRUS,		CIRRUS_7548,	"GD 7548"),
 	DEVICE( CIRRUS,		CIRRUS_5430,	"GD 5430"),
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
@@ -120,6 +121,7 @@
 	DEVICE( IBM,		IBM_82G2675,	"82G2675"),
 	DEVICE( IBM,		IBM_MCA,	"MicroChannel"),
 	DEVICE( IBM,		IBM_82351,	"82351"),
+	DEVICE( IBM,		IBM_PYTHON,	"Python"),
 	DEVICE( IBM,		IBM_SERVERAID,	"ServeRAID"),
 	DEVICE( IBM,		IBM_TR_WAKE,	"Wake On LAN Token Ring"),
 	DEVICE( IBM,		IBM_MPIC,	"MPIC-2 Interrupt Controller"),
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c
--- v2.2.3/linux/drivers/sbus/audio/audio.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/sbus/audio/audio.c	Sun Mar 21 07:23:38 1999
@@ -136,7 +136,7 @@
 
 	drv->output_buffer = kmalloc((drv->output_buffer_size * 
 					       drv->num_output_buffers),
-					      GFP_KERNEL);
+					      (GFP_DMA | GFP_KERNEL));
 	if (!drv->output_buffer) goto kmalloc_failed2;
 
         /* Allocate the pages for each output buffer. */
@@ -167,7 +167,7 @@
 	if (duplex == 1) {
 	  drv->input_buffer = kmalloc((drv->input_buffer_size * 
 						drv->num_input_buffers), 
-					       GFP_KERNEL);
+					       (GFP_DMA | GFP_KERNEL));
 	  if (!drv->input_buffer) goto kmalloc_failed4;
 
 	  for (i = 0; i < drv->num_input_buffers; i++) {
@@ -634,7 +634,29 @@
 					   SPARCAUDIO_DEVICE_SHIFT)];
   unsigned long i = 0, j = 0, k = 0;
 
-  k = (unsigned long) &arg;
+  k = arg;
+
+  if(cmd == SOUND_MIXER_INFO) {
+          audio_device_t tmp;
+          mixer_info info;
+          int retval = -EINVAL;
+
+          if(drv->ops->sunaudio_getdev) {
+                  drv->ops->sunaudio_getdev(drv, &tmp);
+                  memset(&info, 0, sizeof(info));
+                  strncpy(info.id, tmp.name, sizeof(info.id));
+                  strncpy(info.name, "Sparc Audio", sizeof(info.name));
+
+                  /* XXX do this right... */
+                  info.modify_counter = 0;
+
+                  if(copy_to_user((char *)arg, &info, sizeof(info)))
+                          retval = -EFAULT;
+                  else
+                          retval = 0;
+          }
+          return retval;
+  }
 
   switch (cmd) {
   case SOUND_MIXER_WRITE_RECLEV:
@@ -1022,12 +1044,14 @@
 		  break;
 		}
 	      } else if (k == 16) {
+		switch (j) {
 		case AUDIO_ENCODING_LINEAR:
 		  i = AFMT_S16_BE;
 		  break;
 		case AUDIO_ENCODING_LINEARLE:
 		  i = AFMT_S16_LE;
 		  break;
+                }
 	      } 
 	      COPY_OUT(arg, i);
 	      break;
@@ -1872,6 +1896,11 @@
 	/* A low-level audio driver must exist. */
 	if (!drv)
 		return -ENODEV;
+
+#ifdef S_ZERO_WR
+        /* This is how 2.0 ended up dealing with 0 len writes */
+        inode->i_flags |= S_ZERO_WR;
+#endif
 
 	switch (minor & 0xf) {
 	case SPARCAUDIO_AUDIOCTL_MINOR:
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/cs4215.h linux/drivers/sbus/audio/cs4215.h
--- v2.2.3/linux/drivers/sbus/audio/cs4215.h	Mon Oct  5 13:13:40 1998
+++ linux/drivers/sbus/audio/cs4215.h	Mon Mar 15 16:11:30 1999
@@ -13,8 +13,9 @@
 	__u8	ctrl[4];	/* Ctrl mode: Time slots 1-4 */
 	__volatile__ struct dbri_mem td;
 	__volatile__ struct dbri_mem rd;
-	__u8	version;
 	__u8	onboard;
+	__u32	status;
+        __u32	version;
 };
 
 
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c
--- v2.2.3/linux/drivers/sbus/audio/cs4231.c	Fri Nov 27 13:09:24 1998
+++ linux/drivers/sbus/audio/cs4231.c	Mon Mar 15 16:11:30 1999
@@ -14,6 +14,7 @@
  * The APC DMA controller support unfortunately is not documented. Thanks, Sun
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -24,6 +25,7 @@
 #include <linux/delay.h>
 #include <linux/soundcard.h>
 #include <linux/version.h>
+#include <linux/ioport.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/system.h>
@@ -31,6 +33,10 @@
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/sbus.h>
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff && defined(CONFIG_PCI)
+#define EB4231_SUPPORT
+#include <asm/ebus.h>
+#endif
 
 #include <asm/audioio.h>
 #include "cs4231.h"
@@ -120,6 +126,7 @@
   tprintk(("enabling play\n"));
   save_flags(flags);
   cli();
+  
   cs4231_chip->regs->iar = 0x9;
   cs4231_chip->regs->idr |= PEN_ENABLE;
   restore_flags(flags);
@@ -261,14 +268,14 @@
   if (value != 0) {
     set_bits = cs4231_encoding_to_bits(drv, value);
     if (set_bits >= 0) {
-      cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8;
-      tmp_bits = cs4231_chip->regs->idr;
-      cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits);
+        cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8;
+        tmp_bits = cs4231_chip->regs->idr;
+        cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits);
     
-      CHIP_READY
+        CHIP_READY
 
-      cs4231_chip->perchip_info.play.encoding = value;
-      return 0;
+        cs4231_chip->perchip_info.play.encoding = value;
+        return 0;
     }
   }
   dprintk(("output enc failed\n"));
@@ -291,14 +298,14 @@
   if (value != 0) {
     set_bits = cs4231_encoding_to_bits(drv, value);
     if (set_bits >= 0) {
-      cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
-      tmp_bits = cs4231_chip->regs->idr;
-      cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits);
+        cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+        tmp_bits = cs4231_chip->regs->idr;
+        cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits);
 
-      CHIP_READY
+        CHIP_READY
 
-      cs4231_chip->perchip_info.record.encoding = value;
-      return 0;
+        cs4231_chip->perchip_info.record.encoding = value;
+        return 0;
     }
   }
   dprintk(("input enc failed\n"));
@@ -321,14 +328,14 @@
   if (value != 0) {
     set_bits = cs4231_rate_to_bits(drv, value);
     if (set_bits >= 0) {
-      cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8;
-      tmp_bits = cs4231_chip->regs->idr;
-      cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits);
+        cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8;
+        tmp_bits = cs4231_chip->regs->idr;
+        cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits);
 
-      CHIP_READY
+        CHIP_READY
 
-      cs4231_chip->perchip_info.play.sample_rate = value;
-      return 0;
+        cs4231_chip->perchip_info.play.sample_rate = value;
+        return 0;
     }
   }
   dprintk(("output rate failed\n"));
@@ -351,14 +358,14 @@
   if (value != 0) {
     set_bits = cs4231_rate_to_bits(drv, value);
     if (set_bits >= 0) {
-      cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
-      tmp_bits = cs4231_chip->regs->idr;
-      cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits);
+        cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+        tmp_bits = cs4231_chip->regs->idr;
+        cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits);
       
-      CHIP_READY
+        CHIP_READY
 
-      cs4231_chip->perchip_info.record.sample_rate = value;
-      return 0;
+        cs4231_chip->perchip_info.record.sample_rate = value;
+        return 0;
     }
   }
   dprintk(("input rate failed\n"));
@@ -383,14 +390,14 @@
   tmp_bits = cs4231_chip->regs->idr;
   switch (value) {
   case 1:
-    cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits);
-    break;
+      cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits);
+      break;
   case 2:
-    cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
-    break;
+      cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
+      break;
   default:
-    dprintk(("input chan failed\n"));
-    return -(EINVAL);
+      dprintk(("input chan failed\n"));
+      return -(EINVAL);
   }  
 
   CHIP_READY
@@ -417,14 +424,14 @@
   tmp_bits = cs4231_chip->regs->idr;
   switch (value) {
   case 1:
-    cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits);
-    break;
+      cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits);
+      break;
   case 2:
-    cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
-    break;
+      cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
+      break;
   default:
-    dprintk(("output chan failed\n"));
-    return -(EINVAL);
+      dprintk(("output chan failed\n"));
+      return -(EINVAL);
   }  
 
   CHIP_READY
@@ -477,7 +484,7 @@
   unsigned int x = 0;
 
   cs4231_chip->regs->iar = IAR_AUTOCAL_END;
-  while (cs4231_chip->regs->iar == IAR_NOT_READY && x <= CS_TIMEOUT) {
+  while (cs4231_chip->regs->idr == IAR_NOT_READY && x <= CS_TIMEOUT) {
     x++;
   }
 
@@ -494,17 +501,17 @@
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   tprintk(("in cs4231_output_muted: %d\n", value));
   if (!value) {
-    cs4231_chip->regs->iar = 0x7;
-    cs4231_chip->regs->idr &= OUTCR_UNMUTE;
-    cs4231_chip->regs->iar = 0x6;
-    cs4231_chip->regs->idr &= OUTCR_UNMUTE;
-    cs4231_chip->perchip_info.output_muted = 0;
+      cs4231_chip->regs->iar = 0x7;
+      cs4231_chip->regs->idr &= OUTCR_UNMUTE;
+      cs4231_chip->regs->iar = 0x6;
+      cs4231_chip->regs->idr &= OUTCR_UNMUTE;
+      cs4231_chip->perchip_info.output_muted = 0;
   } else {
-    cs4231_chip->regs->iar = 0x7;
-    cs4231_chip->regs->idr |= OUTCR_MUTE;
-    cs4231_chip->regs->iar = 0x6;
-    cs4231_chip->regs->idr |= OUTCR_MUTE;
-    cs4231_chip->perchip_info.output_muted = 1;
+      cs4231_chip->regs->iar = 0x7;
+      cs4231_chip->regs->idr |= OUTCR_MUTE;
+      cs4231_chip->regs->iar = 0x6;
+      cs4231_chip->regs->idr |= OUTCR_MUTE;
+      cs4231_chip->perchip_info.output_muted = 1;
   }
   return 0;
 }
@@ -555,21 +562,21 @@
   cs4231_chip->regs->idr |= PINCR_HDPH_MUTE;
 
   if (value & AUDIO_SPEAKER) {
-    cs4231_chip->regs->iar = 0x1a;
-    cs4231_chip->regs->idr &= ~MONO_IOCR_MUTE;
-    retval |= AUDIO_SPEAKER;
+      cs4231_chip->regs->iar = 0x1a;
+      cs4231_chip->regs->idr &= ~MONO_IOCR_MUTE;
+      retval |= AUDIO_SPEAKER;
   }
 
   if (value & AUDIO_HEADPHONE) {
-   cs4231_chip->regs->iar = 0x0a;
-   cs4231_chip->regs->idr &= ~PINCR_HDPH_MUTE;
-   retval |= AUDIO_HEADPHONE;
+      cs4231_chip->regs->iar = 0x0a;
+      cs4231_chip->regs->idr &= ~PINCR_HDPH_MUTE;
+      retval |= AUDIO_HEADPHONE;
   }
 
   if (value & AUDIO_LINE_OUT) {
-    cs4231_chip->regs->iar = 0x0a;
-    cs4231_chip->regs->idr &= ~PINCR_LINE_MUTE;
-    retval |= AUDIO_LINE_OUT;
+      cs4231_chip->regs->iar = 0x0a;
+      cs4231_chip->regs->idr &= ~PINCR_LINE_MUTE;
+      retval |= AUDIO_LINE_OUT;
   }
   
   cs4231_chip->perchip_info.play.port = retval;
@@ -600,31 +607,31 @@
   /* This apparently applies only to APC ultras, not ebus ultras */
   if (!cs4231_chip->status & CS_STATUS_IS_ULTRA) {
     if (value & AUDIO_INTERNAL_CD_IN) {
-      cs4231_chip->regs->iar = 0x1;
-      cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr);
-      cs4231_chip->regs->iar = 0x0;
-      cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr);
-      retval = AUDIO_INTERNAL_CD_IN;
+        cs4231_chip->regs->iar = 0x1;
+        cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr);
+        cs4231_chip->regs->iar = 0x0;
+        cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr);
+        retval = AUDIO_INTERNAL_CD_IN;
     }
   }
   if ((value & AUDIO_LINE_IN)) {
-    cs4231_chip->regs->iar = 0x1;
-    cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr);
-    cs4231_chip->regs->iar = 0x0;
-    cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr);
-    retval = AUDIO_LINE_IN;
+      cs4231_chip->regs->iar = 0x1;
+      cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr);
+      cs4231_chip->regs->iar = 0x0;
+      cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr);
+      retval = AUDIO_LINE_IN;
   } else if (value & AUDIO_MICROPHONE) {
-    cs4231_chip->regs->iar = 0x1;
-    cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr);
-    cs4231_chip->regs->iar = 0x0;
-    cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr);
-    retval = AUDIO_MICROPHONE;
+      cs4231_chip->regs->iar = 0x1;
+      cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr);
+      cs4231_chip->regs->iar = 0x0;
+      cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr);
+      retval = AUDIO_MICROPHONE;
   } else if (value & AUDIO_ANALOG_LOOPBACK) {
-    cs4231_chip->regs->iar = 0x1;
-    cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr);
-    cs4231_chip->regs->iar = 0x0;
-    cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr);
-    retval = AUDIO_ANALOG_LOOPBACK;
+      cs4231_chip->regs->iar = 0x1;
+      cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr);
+      cs4231_chip->regs->iar = 0x0;
+      cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr);
+      retval = AUDIO_ANALOG_LOOPBACK;
   }
 
   cs4231_chip->perchip_info.record.port = retval;
@@ -654,9 +661,9 @@
 
   cs4231_chip->regs->iar = 0x0d;
   if (a >= CS4231_MON_MAX_ATEN) 
-    cs4231_chip->regs->idr = LOOPB_OFF;
+      cs4231_chip->regs->idr = LOOPB_OFF;
   else 
-    cs4231_chip->regs->idr = ((a << 2) | LOOPB_ON);
+      cs4231_chip->regs->idr = ((a << 2) | LOOPB_ON);
 
   if (value == AUDIO_MAX_GAIN) 
     cs4231_chip->perchip_info.monitor_gain = AUDIO_MAX_GAIN;
@@ -689,6 +696,32 @@
         return (int)cs4231_chip->perchip_info.record.error;
 }
 
+#ifdef EB4231_SUPPORT
+static int eb4231_get_output_samples(struct sparcaudio_driver *drv)
+{
+        struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+        int count = 
+          cs4231_length_to_samplecount(&cs4231_chip->perchip_info.play, 
+                                       readl(&cs4231_chip->eb2p->dbcr));
+
+        return (cs4231_chip->perchip_info.play.samples - 
+                ((count > cs4231_chip->perchip_info.play.samples) 
+                 ? 0 : count));
+}
+
+static int eb4231_get_input_samples(struct sparcaudio_driver *drv)
+{
+        struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+        int count = 
+          cs4231_length_to_samplecount(&cs4231_chip->perchip_info.record, 
+                                       readl(&cs4231_chip->eb2c->dbcr));
+
+        return (cs4231_chip->perchip_info.record.samples - 
+                ((count > cs4231_chip->perchip_info.record.samples) ?
+                0 : count));
+}
+#endif
+
 static int cs4231_get_output_samples(struct sparcaudio_driver *drv)
 {
         struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
@@ -890,13 +923,21 @@
 
   tprintk(("in cs4231_chip_reset\n"));
 
-  cs4231_chip->regs->dmacsr = CS_CHIP_RESET;
-  cs4231_chip->regs->dmacsr = 0x00;
-  cs4231_chip->regs->dmacsr |= CS_CDC_RESET;
+  if (cs4231_chip->status & CS_STATUS_IS_EBUS) {
+#ifdef EB4231_SUPPORT
+    writel(EBUS_DCSR_RESET, &cs4231_chip->eb2p->dcsr);
+    writel(EBUS_DCSR_RESET, &cs4231_chip->eb2c->dcsr);
+#endif
+  } else {
+    cs4231_chip->regs->dmacsr = APC_CHIP_RESET;
+    cs4231_chip->regs->dmacsr = 0x00;
+    cs4231_chip->regs->dmacsr |= APC_CDC_RESET;
   
-  udelay(20);
+    udelay(20);
   
-  cs4231_chip->regs->dmacsr &= ~(CS_CDC_RESET);
+    cs4231_chip->regs->dmacsr &= ~(APC_CDC_RESET);
+  }
+
   cs4231_chip->regs->iar |= IAR_AUTOCAL_BEGIN;
   
   CHIP_READY
@@ -928,9 +969,9 @@
   
   cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x11;
   if (cs4231_chip->status & CS_STATUS_REV_A)
-    cs4231_chip->regs->idr = (HPF_ON | XTALE_ON);
+      cs4231_chip->regs->idr = (HPF_ON | XTALE_ON);
   else
-    cs4231_chip->regs->idr = (HPF_ON);
+      cs4231_chip->regs->idr = HPF_ON;
   
   cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1a;
   cs4231_chip->regs->idr = 0x00;
@@ -990,6 +1031,36 @@
   return count;
 }
 
+#ifdef EB4231_SUPPORT
+static void eb4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int length, unsigned int direction)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  struct audio_prinfo *thisdir;
+  unsigned int count, nextcount, curcount;
+
+  if (direction == 1) /* record */ 
+    {
+      thisdir = &cs4231_chip->perchip_info.record;
+      curcount = 
+	cs4231_length_to_samplecount(thisdir, readl(&cs4231_chip->eb2c->dbcr));
+      nextcount = 
+	cs4231_length_to_samplecount(thisdir, 0/*cs4231_chip->regs->dmacnc*/);
+    }
+  else /* play */
+    {
+      thisdir = &cs4231_chip->perchip_info.play;
+      curcount = 
+	cs4231_length_to_samplecount(thisdir, readl(&cs4231_chip->eb2p->dbcr));
+      nextcount = 
+	cs4231_length_to_samplecount(thisdir, 0/*cs4231_chip->regs->dmapnc*/);
+    }
+  count = thisdir->samples;
+  length = cs4231_length_to_samplecount(thisdir, length);
+  /* normalize for where we are. */
+  thisdir->samples = ((count - nextcount) + (length - curcount));
+}
+#endif
+
 static void cs4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int length, unsigned int direction)
 {
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
@@ -1060,14 +1131,18 @@
     /* stop capture here or midlevel? */
     cs4231_chip->perchip_info.record.open = 0;
     if (cs4231_chip->input_dma_handle) {
-      mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->input_dma_handle),
+#if 0
+      mmu_release_scsi_one(cs4231_chip->input_dma_handle,
                            cs4231_chip->input_dma_size, drv->dev->my_bus);
+#endif
       cs4231_chip->input_dma_handle = 0;
       cs4231_chip->input_dma_size = 0;
     }
     if (cs4231_chip->input_next_dma_handle) {
-      mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle),
+#if 0
+      mmu_release_scsi_one(cs4231_chip->input_next_dma_handle,
                            cs4231_chip->input_next_dma_size, drv->dev->my_bus);
+#endif
       cs4231_chip->input_next_dma_handle = 0;
       cs4231_chip->input_next_dma_size = 0;
     }
@@ -1077,15 +1152,19 @@
     cs4231_chip->perchip_info.play.active =
       cs4231_chip->perchip_info.play.open = 0;
     if (cs4231_chip->output_dma_handle) {
-      mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
+#if 0
+      mmu_release_scsi_one(cs4231_chip->output_dma_handle,
                            cs4231_chip->output_dma_size, drv->dev->my_bus);
+#endif
       cs4231_chip->output_dma_handle = 0;
       cs4231_chip->output_dma_size = 0;
     }
     if (cs4231_chip->output_next_dma_handle) {
-      mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle),
+#if 0
+      mmu_release_scsi_one(cs4231_chip->output_next_dma_handle,
                            cs4231_chip->output_next_dma_size, 
                            drv->dev->my_bus);
+#endif
       cs4231_chip->output_next_dma_handle = 0;
       cs4231_chip->output_next_dma_size = 0;
     }
@@ -1110,7 +1189,7 @@
     cs4231_chip->playlen = cs4231_chip->output_size;
 
   if (cs4231_chip->output_dma_handle) {
-    mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle), 
+    mmu_release_scsi_one(cs4231_chip->output_dma_handle, 
                          cs4231_chip->output_dma_size, drv->dev->my_bus);
     cs4231_chip->output_dma_handle = 0;
     cs4231_chip->output_dma_size = 0;
@@ -1126,11 +1205,10 @@
 
   if ((cs4231_chip->output_ptr && cs4231_chip->output_size > 0) && 
       !(cs4231_chip->perchip_info.play.pause)) {
-    cs4231_chip->output_next_dma_handle = (u32 *) (unsigned long)
+    cs4231_chip->output_next_dma_handle =
       mmu_get_scsi_one((char *) cs4231_chip->output_ptr, 
                        cs4231_chip->output_size, drv->dev->my_bus);
-    cs4231_chip->regs->dmapnva = (u32) (unsigned long)
-      cs4231_chip->output_next_dma_handle;
+    cs4231_chip->regs->dmapnva = cs4231_chip->output_next_dma_handle;
     cs4231_chip->output_next_dma_size = cs4231_chip->regs->dmapnc = 
       cs4231_chip->output_size;
     cs4231_chip->output_size = 0;
@@ -1147,6 +1225,62 @@
   return;
 }
 
+#ifdef EB4231_SUPPORT
+static void eb4231_playintr(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int status = 0;
+  unsigned int dcsr;
+
+  dcsr = readl(&cs4231_chip->eb2p->dcsr);
+
+  printk("pintr\ncsr 0x%x acr 0x%x bcr %d\n", readl(&cs4231_chip->eb2p->dcsr), readl(&cs4231_chip->eb2p->dacr), readl(&cs4231_chip->eb2p->dbcr));
+  if (cs4231_chip->playlen == 0 && cs4231_chip->output_size > 0) 
+    cs4231_chip->playlen = cs4231_chip->output_size;
+
+  if (cs4231_chip->output_dma_handle) {
+    cs4231_chip->output_dma_handle = 0;
+    cs4231_chip->output_dma_size = 0;
+    cs4231_chip->playing_count--;
+    status++;
+  }
+
+#if 0
+  if (cs4231_chip->output_next_dma_handle) {
+    cs4231_chip->output_dma_handle = cs4231_chip->output_next_dma_handle;
+    cs4231_chip->output_dma_size = cs4231_chip->output_next_dma_size;
+    cs4231_chip->output_next_dma_size = 0;
+    cs4231_chip->output_next_dma_handle = 0;
+  }
+#endif
+
+  if ((cs4231_chip->output_ptr && cs4231_chip->output_size > 0) && 
+      !(cs4231_chip->perchip_info.play.pause)) {
+#if 0
+    if (dcsr & EBUS_DCSR_A_LOADED) {
+      cs4231_chip->output_next_dma_handle = virt_to_bus(cs4231_chip->output_ptr);
+      cs4231_chip->output_next_dma_size = cs4231_chip->output_size;
+    } else {
+#endif
+      cs4231_chip->output_dma_handle = virt_to_bus(cs4231_chip->output_ptr);
+      cs4231_chip->output_dma_size = cs4231_chip->output_size;
+#if 0
+    }
+#endif
+    writel(virt_to_bus(cs4231_chip->output_ptr), &cs4231_chip->eb2p->dacr);
+    writel(cs4231_chip->output_size, &cs4231_chip->eb2p->dbcr);
+    cs4231_chip->output_size = 0;
+    cs4231_chip->output_ptr = NULL;
+    cs4231_chip->playing_count++;
+    status+=2;
+  }
+
+  sparcaudio_output_done(drv, status);
+
+  return;
+}
+#endif
+
 static void cs4231_recclear(int fmt, char *dmabuf, int length)
 {
   switch (fmt) {
@@ -1174,7 +1308,7 @@
   } 
 
   if (cs4231_chip->input_dma_handle) {
-    mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle),
+    mmu_release_scsi_one(cs4231_chip->input_dma_handle, 
                          cs4231_chip->input_dma_size, drv->dev->my_bus);
     cs4231_chip->input_dma_handle = 0;
     cs4231_chip->input_dma_size = 0;
@@ -1192,11 +1326,10 @@
       !(cs4231_chip->perchip_info.record.pause)) {
     cs4231_recclear(cs4231_chip->perchip_info.record.encoding, 
                     (char *)cs4231_chip->input_ptr, cs4231_chip->input_size);
-    cs4231_chip->input_next_dma_handle = (u32*) (unsigned long)
+    cs4231_chip->input_next_dma_handle =
       mmu_get_scsi_one((char *) cs4231_chip->input_ptr, 
                        cs4231_chip->input_size, drv->dev->my_bus);
-    cs4231_chip->regs->dmacnva = (u32) (unsigned long)
-      cs4231_chip->input_next_dma_handle;
+    cs4231_chip->regs->dmacnva = cs4231_chip->input_next_dma_handle;
     cs4231_chip->input_next_dma_size = cs4231_chip->regs->dmacnc = 
       cs4231_chip->input_size;
     cs4231_chip->input_size = 0;
@@ -1213,6 +1346,107 @@
   return 1;
 }
 
+#ifdef EB4231_SUPPORT
+static int eb4231_recintr(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int status = 0;
+
+  if (cs4231_chip->perchip_info.record.active == 0) {
+    dprintk(("going inactive\n"));
+    cs4231_disable_rec(drv);    
+  } 
+
+  if (cs4231_chip->input_dma_handle) {
+#if 0
+    mmu_release_scsi_one(cs4231_chip->input_dma_handle, 
+                         cs4231_chip->input_dma_size, drv->dev->my_bus);
+#endif
+    cs4231_chip->input_dma_handle = 0;
+    cs4231_chip->input_dma_size = 0;
+    cs4231_chip->recording_count--;
+    status++;
+  }
+  if (cs4231_chip->input_next_dma_handle) {
+    cs4231_chip->input_dma_handle = cs4231_chip->input_next_dma_handle;
+    cs4231_chip->input_dma_size = cs4231_chip->input_next_dma_size;
+    cs4231_chip->input_next_dma_size = 0;
+    cs4231_chip->input_next_dma_handle = 0;
+  }
+
+  if ((cs4231_chip->input_ptr && cs4231_chip->input_size > 0) && 
+      !(cs4231_chip->perchip_info.record.pause)) {
+    cs4231_recclear(cs4231_chip->perchip_info.record.encoding, 
+                    (char *)cs4231_chip->input_ptr, cs4231_chip->input_size);
+#if 0
+    cs4231_chip->input_next_dma_handle =
+      mmu_get_scsi_one((char *) cs4231_chip->input_ptr, 
+                       cs4231_chip->input_size, drv->dev->my_bus);
+    cs4231_chip->regs->dmacnva = cs4231_chip->input_next_dma_handle;
+    cs4231_chip->input_next_dma_size = cs4231_chip->regs->dmacnc = 
+      cs4231_chip->input_size;
+#else
+    cs4231_chip->input_next_dma_handle = cs4231_chip->eb2c->dacr = virt_to_bus(cs4231_chip->input_ptr);
+    cs4231_chip->input_next_dma_size = cs4231_chip->eb2c->dbcr = cs4231_chip->input_size;
+#endif
+    cs4231_chip->input_size = 0;
+    cs4231_chip->input_ptr = NULL;
+    cs4231_chip->recording_count++;
+    status += 2;
+  } else {
+#if 0
+    cs4231_chip->regs->dmacnva = 0;
+    cs4231_chip->regs->dmacnc = 0;
+#else
+    cs4231_chip->eb2c->dacr = 0;
+    cs4231_chip->eb2c->dbcr = 0;
+#endif
+  }
+
+  sparcaudio_input_done(drv, 1);
+
+  return 1;
+}
+#endif
+
+#ifdef EB4231_SUPPORT
+static void eb4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
+                                unsigned long count)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  unsigned int dcsr;
+
+  dprintk(("in eb4231 start output act %d pau %d\n", cs4231_chip->perchip_info.play.active, cs4231_chip->perchip_info.play.pause));
+  cs4231_chip->output_ptr = buffer;
+  cs4231_chip->output_size = count;
+
+  if (cs4231_chip->perchip_info.play.active || 
+      (cs4231_chip->perchip_info.play.pause))
+    return;
+
+  cs4231_ready(drv);
+
+  cs4231_chip->perchip_info.play.active = 1;
+  cs4231_chip->playing_count = 0;
+
+  dcsr = readl(&cs4231_chip->eb2p->dcsr);
+  if (!(dcsr & EBUS_DCSR_EN_DMA)) {
+    dprintk(("about to go setup\n"));
+
+    dcsr = EB2_PLAY_SETUP;
+    writel(dcsr, (unsigned long)&cs4231_chip->eb2p->dcsr);
+    eb4231_playintr(drv);
+    dprintk(("enabling\n"));
+    cs4231_enable_play(drv);
+
+    cs4231_ready(drv);
+  } else {
+    dprintk(("playing next block\n"));
+    eb4231_playintr(drv);
+  }
+}
+#endif
+
 static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
                                 unsigned long count)
 {
@@ -1231,14 +1465,14 @@
   cs4231_chip->perchip_info.play.active = 1;
   cs4231_chip->playing_count = 0;
 
-  if ((cs4231_chip->regs->dmacsr & CS_PPAUSE) || 
-      !(cs4231_chip->regs->dmacsr & PDMA_READY)) {
-    cs4231_chip->regs->dmacsr &= ~CS_XINT_PLAY;
-    cs4231_chip->regs->dmacsr &= ~CS_PPAUSE;
+  if ((cs4231_chip->regs->dmacsr & APC_PPAUSE) || 
+      !(cs4231_chip->regs->dmacsr & APC_PDMA_READY)) {
+    cs4231_chip->regs->dmacsr &= ~APC_XINT_PLAY;
+    cs4231_chip->regs->dmacsr &= ~APC_PPAUSE;
     
     cs4231_playintr(drv);
 
-    cs4231_chip->regs->dmacsr |= CS_PLAY_SETUP;
+    cs4231_chip->regs->dmacsr |= APC_PLAY_SETUP;
     cs4231_enable_play(drv);
 
     cs4231_ready(drv);
@@ -1246,6 +1480,28 @@
     cs4231_playintr(drv);
 }
 
+#ifdef EB4231_SUPPORT
+static void eb4231_stop_output(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  dprintk(("in cs4231_stop_output\n"));
+  dprintk(("csr 0x%x acr 0x%x bcr %d\n", readl(&cs4231_chip->eb2p->dcsr), readl(&cs4231_chip->eb2p->dacr), readl(&cs4231_chip->eb2p->dbcr)));
+  cs4231_chip->output_ptr = NULL;
+  cs4231_chip->output_size = 0;
+  if (cs4231_chip->output_dma_handle) {
+    cs4231_chip->output_dma_handle = 0;
+    cs4231_chip->output_dma_size = 0;
+  }
+#if 0
+  if (cs4231_chip->output_next_dma_handle) {
+    cs4231_chip->output_next_dma_handle = 0;
+    cs4231_chip->output_next_dma_size = 0;
+  }
+#endif
+}
+#endif
+
 static void cs4231_stop_output(struct sparcaudio_driver *drv)
 {
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
@@ -1254,13 +1510,13 @@
   cs4231_chip->output_ptr = NULL;
   cs4231_chip->output_size = 0;
   if (cs4231_chip->output_dma_handle) {
-    mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
+    mmu_release_scsi_one(cs4231_chip->output_dma_handle,
                          cs4231_chip->output_dma_size, drv->dev->my_bus);
     cs4231_chip->output_dma_handle = 0;
     cs4231_chip->output_dma_size = 0;
   }
   if (cs4231_chip->output_next_dma_handle) {
-    mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle),
+    mmu_release_scsi_one(cs4231_chip->output_next_dma_handle,
                          cs4231_chip->output_next_dma_size, drv->dev->my_bus);
     cs4231_chip->output_next_dma_handle = 0;
     cs4231_chip->output_next_dma_size = 0;
@@ -1272,10 +1528,10 @@
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   int x = 0;
 
-  while (!(cs4231_chip->regs->dmacsr & CS_XINT_COVF) && x <= CS_TIMEOUT) {
+  while (!(cs4231_chip->regs->dmacsr & APC_XINT_COVF) && x <= CS_TIMEOUT) {
     x++;
   }
-  cs4231_chip->regs->dmacsr |= CS_XINT_CEMP;
+  cs4231_chip->regs->dmacsr |= APC_XINT_CEMP;
 }
 
 static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer, 
@@ -1295,14 +1551,14 @@
   cs4231_chip->perchip_info.record.active = 1;
   cs4231_chip->recording_count = 0;
 
-  if ((cs4231_chip->regs->dmacsr & CS_CPAUSE) || 
-      !(cs4231_chip->regs->dmacsr & CDMA_READY)) {
-    cs4231_chip->regs->dmacsr &= ~CS_XINT_CAPT;
-    cs4231_chip->regs->dmacsr &= ~CS_CPAUSE;
+  if ((cs4231_chip->regs->dmacsr & APC_CPAUSE) || 
+      !(cs4231_chip->regs->dmacsr & APC_CDMA_READY)) {
+    cs4231_chip->regs->dmacsr &= ~APC_XINT_CAPT;
+    cs4231_chip->regs->dmacsr &= ~APC_CPAUSE;
 
     cs4231_recintr(drv);
 
-    cs4231_chip->regs->dmacsr |= CS_CAPT_SETUP;
+    cs4231_chip->regs->dmacsr |= APC_CAPT_SETUP;
     cs4231_enable_rec(drv);
 
     cs4231_ready(drv);
@@ -1315,18 +1571,18 @@
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
   cs4231_chip->perchip_info.record.active = 0;
-  cs4231_chip->regs->dmacsr |= (CS_CPAUSE);
+  cs4231_chip->regs->dmacsr |= (APC_CPAUSE);
 
   cs4231_chip->input_ptr = NULL;
   cs4231_chip->input_size = 0;
   if (cs4231_chip->input_dma_handle) {
-    mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle),
+    mmu_release_scsi_one(cs4231_chip->input_dma_handle,
                          cs4231_chip->input_dma_size, drv->dev->my_bus);
     cs4231_chip->input_dma_handle = 0;
     cs4231_chip->input_dma_size = 0;
   }
   if (cs4231_chip->input_next_dma_handle) {
-    mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle),
+    mmu_release_scsi_one(cs4231_chip->input_next_dma_handle,
                          cs4231_chip->input_next_dma_size, drv->dev->my_bus);
     cs4231_chip->input_next_dma_handle = 0;
     cs4231_chip->input_next_dma_size = 0;
@@ -1408,7 +1664,11 @@
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
   strncpy(audinfo->name, "SUNW,CS4231", sizeof(audinfo->name) - 1);
-  /* versions: SPARCstation 4/5=a, Ultra=b */
+  /* versions */
+  /* a: SPARCstation 4/5          b: Ultra 1/2 (electron)       */
+  /* c: Ultra 1/2 PCI? (positron) d: ppc                        */
+  /* e: x86                       f: Ultra Enterprise? (tazmo)  */
+  /* g: Ultra 30? (quark)         h: Ultra 5/10? (darwin)       */
   /* apparently Ultra 1, Ultra 2 don't have internal CD input */
   if (cs4231_chip->status & CS_STATUS_IS_ULTRA)
     strncpy(audinfo->version, "b", sizeof(audinfo->version) - 1);
@@ -1450,6 +1710,114 @@
   return retval;
 }
 
+#ifdef EB4231_SUPPORT
+/* ebus audio capture interrupt handler. */
+void eb4231_cinterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int dummy1, dummy2;
+  
+  printk("in eb4231_interrupt\n");
+
+  /* Clear the interrupt. */
+  dummy1 = readl(&cs4231_chip->eb2p->dcsr);
+  dummy2 = readl(&cs4231_chip->eb2c->dcsr);
+
+  printk("play csr 0x%x capt csr 0x%x\n", dummy1, dummy2);
+
+  cs4231_chip->eb2p->dcsr = dummy1;
+  cs4231_chip->eb2c->dcsr = dummy2;
+#if 0
+  if (dummy & APC_PLAY_INT) {
+    if (dummy & APC_XINT_PNVA) {
+      cs4231_chip->perchip_info.play.samples += 
+	cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), 
+				     cs4231_chip->playlen); 
+      eb4231_playintr(drv);
+    } 
+    /* Any other conditions we need worry about? */
+  }
+
+  if (dummy & APC_CAPT_INT) {
+    if (dummy & APC_XINT_CNVA) {
+      cs4231_chip->perchip_info.record.samples += 
+	cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.record), 
+				     cs4231_chip->reclen);
+      eb4231_recintr(drv);
+    }
+    /* Any other conditions we need worry about? */
+  }
+
+  
+  if (dummy & APC_XINT_CEMP) {
+    if (cs4231_chip->perchip_info.record.active == 0) {
+      /* Fix me */
+      cs4231_chip->perchip_info.record.active = 0;
+      cs4231_chip->perchip_info.record.error = 1;
+      eb4231_recintr(drv);
+    }
+  }
+
+  if (dummy & APC_XINT_EMPT) {
+    if (!cs4231_chip->output_next_dma_handle) {
+      cs4231_chip->regs->dmacsr |= (APC_PPAUSE);
+      cs4231_disable_play(drv);
+      cs4231_chip->perchip_info.play.error = 1;
+    }
+    cs4231_chip->perchip_info.play.active = 0;
+    eb4231_playintr(drv);
+
+    eb4231_getsamplecount(drv, cs4231_chip->playlen, 0);
+  }
+#endif
+}
+
+/* ebus audio play interrupt handler. */
+void eb4231_pinterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  unsigned int dummy;
+  
+  dprintk(("in eb4231_interrupt2\n"));
+
+  /* Clear the interrupt. */
+  dummy = readl(&cs4231_chip->eb2p->dcsr);
+  dprintk(("play csr 0x%x\n", dummy));
+
+  if (dummy & EBUS_DCSR_INT_PEND) {
+#if 0
+    if (!(dummy & EBUS_DCSR_NA_LOADED)) {
+#endif
+      cs4231_chip->perchip_info.play.samples += 
+        cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), 
+                                     cs4231_chip->playlen); 
+      eb4231_playintr(drv);
+#if 0
+    } 
+#endif
+  }
+
+  if (!(dummy & EBUS_DCSR_A_LOADED)) {
+#if 0
+    if (!cs4231_chip->output_next_dma_handle) {
+#endif
+      writel((dummy & ~EBUS_DCSR_EN_DMA), &cs4231_chip->eb2p->dcsr);
+      cs4231_disable_play(drv);
+      cs4231_chip->perchip_info.play.error = 1;
+#if 0
+    }
+#endif
+    cs4231_chip->perchip_info.play.active = 0;
+#if 0
+    eb4231_playintr(drv);
+#endif
+    eb4231_getsamplecount(drv, cs4231_chip->playlen, 0);
+  }
+
+}
+#endif
 
 /* Audio interrupt handler. */
 void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -1458,7 +1826,7 @@
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   __u32 dummy;
   
-  tprintk(("in cs4231_interrupt\n"));
+  dprintk(("in cs4231_interrupt\n"));
 
   /* Clear the interrupt. */
   dummy = cs4231_chip->regs->dmacsr;
@@ -1468,8 +1836,8 @@
    * if anything since we may be doing shared interrupts 
    */
 
-  if (dummy & CS_PLAY_INT) {
-    if (dummy & CS_XINT_PNVA) {
+  if (dummy & APC_PLAY_INT) {
+    if (dummy & APC_XINT_PNVA) {
       cs4231_chip->perchip_info.play.samples += 
 	cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), 
 				     cs4231_chip->playlen); 
@@ -1478,8 +1846,8 @@
     /* Any other conditions we need worry about? */
   }
 
-  if (dummy & CS_CAPT_INT) {
-    if (dummy & CS_XINT_CNVA) {
+  if (dummy & APC_CAPT_INT) {
+    if (dummy & APC_XINT_CNVA) {
       cs4231_chip->perchip_info.record.samples += 
 	cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.record), 
 				     cs4231_chip->reclen);
@@ -1489,7 +1857,7 @@
   }
 
   
-  if (dummy & CS_XINT_CEMP) {
+  if (dummy & APC_XINT_CEMP) {
     if (cs4231_chip->perchip_info.record.active == 0) {
       /* Fix me */
       cs4231_chip->perchip_info.record.active = 0;
@@ -1498,9 +1866,9 @@
     }
   }
 
-  if (dummy & CS_XINT_EMPT) {
+  if (dummy & APC_XINT_EMPT) {
     if (!cs4231_chip->output_next_dma_handle) {
-      cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
+      cs4231_chip->regs->dmacsr |= (APC_PPAUSE);
       cs4231_disable_play(drv);
       cs4231_chip->perchip_info.play.error = 1;
     }
@@ -1510,7 +1878,7 @@
     cs4231_getsamplecount(drv, cs4231_chip->playlen, 0);
   }
 
-  if (dummy & CS_GENL_INT) {
+  if (dummy & APC_GENL_INT) {
     /* If we get here we must be sharing an interrupt, but I haven't code 
        to handle this right now */
   }
@@ -1576,6 +1944,67 @@
         cs4231_get_formats,
 };
 
+#ifdef EB4231_SUPPORT
+static struct sparcaudio_operations eb4231_ops = {
+	cs4231_open,
+	cs4231_release,
+	cs4231_ioctl,
+	eb4231_start_output,
+	eb4231_stop_output,
+	cs4231_start_input,
+        cs4231_stop_input,
+	cs4231_audio_getdev,
+        cs4231_set_output_volume,
+        cs4231_get_output_volume,
+        cs4231_set_input_volume,
+        cs4231_get_input_volume,
+        cs4231_set_monitor_volume,
+        cs4231_get_monitor_volume,
+	cs4231_set_output_balance,
+	cs4231_get_output_balance,
+        cs4231_set_input_balance,
+        cs4231_get_input_balance,
+        cs4231_set_output_channels,
+        cs4231_get_output_channels,
+        cs4231_set_input_channels,
+        cs4231_get_input_channels,
+        cs4231_set_output_precision,
+        cs4231_get_output_precision,
+        cs4231_set_input_precision,
+        cs4231_get_input_precision,
+        cs4231_set_output_port,
+        cs4231_get_output_port,
+        cs4231_set_input_port,
+        cs4231_get_input_port,
+        cs4231_set_output_encoding,
+        cs4231_get_output_encoding,
+        cs4231_set_input_encoding,
+        cs4231_get_input_encoding,
+        cs4231_set_output_rate,
+        cs4231_get_output_rate,
+        cs4231_set_input_rate,
+	cs4231_get_input_rate,
+	cs4231_audio_getdev_sunos,
+	cs4231_get_output_ports,
+	cs4231_get_input_ports,
+	cs4231_output_muted,
+	cs4231_get_output_muted,
+	cs4231_set_output_pause,
+	cs4231_get_output_pause,
+	cs4231_set_input_pause,
+	cs4231_get_input_pause,
+	cs4231_set_output_samples,
+	eb4231_get_output_samples,
+	cs4231_set_input_samples,
+	eb4231_get_input_samples,
+	cs4231_set_output_error,
+	cs4231_get_output_error,
+	cs4231_set_input_error,
+	cs4231_get_input_error,
+        cs4231_get_formats,
+};
+#endif
+
 /* Attach to an cs4231 chip given its PROM node. */
 static int cs4231_attach(struct sparcaudio_driver *drv, 
 			 struct linux_sbus_device *sdev)
@@ -1642,6 +2071,8 @@
 
   enable_irq(cs4231_chip->irq);
 
+  cs4231_chip->nirqs = 1;
+
   cs4231_enable_interrupts(drv);
 
   /* Reset the audio chip. */
@@ -1676,13 +2107,149 @@
 						  AUDIO_ANALOG_LOOPBACK);
 
   /* Announce the hardware to the user. */
-  printk(KERN_INFO "audio%d: cs4231%c at 0x%lx irq %d\n",
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+  printk(KERN_INFO "audio%d: cs4231%c at 0x%x irq %d\n",
          drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', 
 	 (unsigned long)cs4231_chip->regs, cs4231_chip->irq);
+#else	 
+  printk(KERN_INFO "audio%d: cs4231%c at %p irq %s\n",
+         drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', 
+	 cs4231_chip->regs, __irq_itoa(cs4231_chip->irq));
+#endif
+  
+  /* Success! */
+  return 0;
+}
+
+#ifdef EB4231_SUPPORT
+/* Attach to an cs4231 chip given its PROM node. */
+static int eb4231_attach(struct sparcaudio_driver *drv, 
+			 struct linux_ebus_device *edev)
+{
+  struct cs4231_chip *cs4231_chip;
+  int len, err, nregs;
+  struct linux_prom_registers regs[4];
+
+  /* Allocate our private information structure. */
+  drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
+  if (!drv->private)
+    return -ENOMEM;
+
+  /* Point at the information structure and initialize it. */
+  drv->ops = &eb4231_ops;
+  cs4231_chip = (struct cs4231_chip *)drv->private;
+  cs4231_chip->input_ptr = cs4231_chip->output_ptr = NULL;
+  cs4231_chip->input_size = cs4231_chip->output_size = 0;
+  cs4231_chip->status = 0;
+
+  len = prom_getproperty(edev->prom_node, "reg", (void *)regs, sizeof(regs));
+
+  if ((len % sizeof(regs[0])) != 0) {
+    printk("eb4231: Strange reg property size %d\n", len);
+    return -ENODEV;
+  }
+
+  nregs = len / sizeof(regs[0]);
+
+  /* Make sure we can map the registers first */
+  if (check_region(edev->base_address[0],
+                   sizeof(struct cs4231_regs))) {
+    printk("eb4231_attach: can't get region at %016lx\n",
+           edev->base_address[0]);
+    goto cleanup;
+  }
+  if (check_region(edev->base_address[1],
+                   sizeof(struct linux_ebus_dma))) {
+    printk("eb4231_attach: can't get region at %016lx\n",
+           edev->base_address[1]);
+    goto cleanup;
+  }
+  if (check_region(edev->base_address[2],
+                   sizeof(struct linux_ebus_dma))) {
+    printk("eb4231_attach: can't get region at %016lx\n",
+           edev->base_address[2]);
+    goto cleanup;
+  }
+
+  cs4231_chip->regs = (struct cs4231_regs *)edev->base_address[0];
+  cs4231_chip->eb2p = (struct linux_ebus_dma *)edev->base_address[1];
+  cs4231_chip->eb2c = (struct linux_ebus_dma *)edev->base_address[2];
+
+  request_region((unsigned long)cs4231_chip->regs, 
+                 sizeof(struct cs4231_regs), "cs4231 regs");
+  request_region((unsigned long)cs4231_chip->eb2c, 
+                 sizeof(struct linux_ebus_dma), "4231 capture DMA");
+  request_region((unsigned long)cs4231_chip->eb2p, 
+                 sizeof(struct linux_ebus_dma), "4231 playback DMA");
+
+  cs4231_chip->status |= CS_STATUS_IS_EBUS;
+
+  /* Attach the interrupt handler to the audio interrupt. */
+  cs4231_chip->irq = edev->irqs[0];
+  cs4231_chip->irq2 = edev->irqs[1];
+
+  request_irq(cs4231_chip->irq, eb4231_cinterrupt, SA_SHIRQ, "cs4231", drv);
+#if 0
+  enable_irq(cs4231_chip->irq);
+#endif
+
+  request_irq(cs4231_chip->irq2, eb4231_pinterrupt, SA_SHIRQ, "cs4231", drv);
+  enable_irq(cs4231_chip->irq2);
+
+  cs4231_chip->nirqs = 2;
+
+  cs4231_enable_interrupts(drv);
+
+  /* Reset the audio chip. */
+  cs4231_chip_reset(drv);
+
+  /* Register ourselves with the midlevel audio driver. */
+  err = register_sparcaudio_driver(drv, 1);
+
+  if (err < 0) {
+    printk(KERN_ERR "cs4231: unable to register\n");
+    cs4231_disable_interrupts(drv);
+    disable_irq(cs4231_chip->irq);
+    free_irq(cs4231_chip->irq, drv);
+    if (cs4231_chip->nirqs == 2) {
+      disable_irq(cs4231_chip->irq2);
+      free_irq(cs4231_chip->irq2, drv);
+    }
+    release_region((unsigned long)cs4231_chip->regs, 
+                   sizeof(struct cs4231_regs));
+    release_region((unsigned long)cs4231_chip->eb2c, 
+                   sizeof(struct linux_ebus_dma));
+    release_region((unsigned long)cs4231_chip->eb2p, 
+                   sizeof(struct linux_ebus_dma));
+  cleanup:
+    kfree(drv->private);
+    return -EIO;
+  }
+
+  cs4231_chip->perchip_info.play.active = 
+    cs4231_chip->perchip_info.play.pause = 0;
+
+  cs4231_chip->perchip_info.record.active = 
+    cs4231_chip->perchip_info.record.pause = 0;
+
+  cs4231_chip->perchip_info.play.avail_ports = (AUDIO_HEADPHONE |
+						AUDIO_SPEAKER |
+						AUDIO_LINE_OUT);
+
+  cs4231_chip->perchip_info.record.avail_ports = (AUDIO_INTERNAL_CD_IN |
+						  AUDIO_LINE_IN | 
+						  AUDIO_MICROPHONE |
+						  AUDIO_ANALOG_LOOPBACK);
+
+  /* Announce the hardware to the user. */
+  printk(KERN_INFO "audio%d: cs4231%c(eb2) at %p irq %s\n",
+         drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', 
+	 cs4231_chip->regs, __irq_itoa(cs4231_chip->irq));
   
   /* Success! */
   return 0;
 }
+#endif
 
 /* Probe for the cs4231 chip and then attach the driver. */
 #ifdef MODULE
@@ -1691,13 +2258,17 @@
 __initfunc(int cs4231_init(void))
 #endif
 {
-  struct linux_sbus *bus;
+  struct linux_sbus *sbus;
   struct linux_sbus_device *sdev;
-  
+#ifdef EB4231_SUPPORT
+  struct linux_ebus *ebus;
+  struct linux_ebus_device *edev;
+#endif
+
   num_drivers = 0;
   
   /* Probe each SBUS for cs4231 chips. */
-  for_all_sbusdev(sdev,bus) {
+  for_all_sbusdev(sdev,sbus) {
     if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
       /* Don't go over the max number of drivers. */
       if (num_drivers >= MAX_DRIVERS)
@@ -1708,6 +2279,21 @@
     }
   }
   
+#ifdef EB4231_SUPPORT
+  for_each_ebus(ebus) {
+    for_each_ebusdev(edev, ebus) {
+      if (!strcmp(edev->prom_name, "SUNW,CS4231")) {
+        /* Don't go over the max number of drivers. */
+        if (num_drivers >= MAX_DRIVERS)
+          continue;
+      
+        if (eb4231_attach(&drivers[num_drivers], edev) == 0)
+          num_drivers++;
+      }
+    }
+  }
+#endif
+
   /* Only return success if we found some cs4231 chips. */
   return (num_drivers > 0) ? 0 : -EIO;
 }
@@ -1716,13 +2302,21 @@
 /* Detach from an cs4231 chip given the device structure. */
 static void cs4231_detach(struct sparcaudio_driver *drv)
 {
-        struct cs4231_chip *info = (struct cs4231_chip *)drv->private;
+        struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
 	cs4231_disable_interrupts(drv);
         unregister_sparcaudio_driver(drv, 1);
-        disable_irq(info->irq);
-        free_irq(info->irq, drv);
-        sparc_free_io(info->regs, info->regs_size);
+        disable_irq(cs4231_chip->irq);
+        free_irq(cs4231_chip->irq, drv);
+        if (!(cs4231_chip->status & CS_STATUS_IS_EBUS)) {
+          sparc_free_io(cs4231_chip->regs, cs4231_chip->regs_size);
+        } else {
+#ifdef EB4231_SUPPORT
+          release_region(cs4231_chip->regs, sizeof(struct cs4231_regs));
+          release_region(cs4231_chip->eb2c, sizeof(struct linux_ebus_dma));
+          release_region(cs4231_chip->eb2p, sizeof(struct linux_ebus_dma));
+#endif
+        }
         kfree(drv->private);
 }
 
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/cs4231.h linux/drivers/sbus/audio/cs4231.h
--- v2.2.3/linux/drivers/sbus/audio/cs4231.h	Thu Nov 19 09:56:28 1998
+++ linux/drivers/sbus/audio/cs4231.h	Mon Mar 15 16:11:30 1999
@@ -37,9 +37,11 @@
 
 struct cs4231_chip {
   struct cs4231_regs *regs;
+  struct linux_ebus_dma *eb2c;
+  struct linux_ebus_dma *eb2p;
   struct audio_info perchip_info;
   unsigned int playlen, reclen;
-  int irq;
+  int irq, irq2, nirqs;
   unsigned long regs_size;
   
   /* Keep track of various info */
@@ -48,13 +50,13 @@
   /* Current buffer that the driver is playing. */
   volatile __u8 * output_ptr;
   volatile unsigned long output_size;
-  volatile __u32 * output_dma_handle, * output_next_dma_handle;
+  volatile __u32 output_dma_handle, output_next_dma_handle;
   volatile unsigned long output_dma_size, output_next_dma_size;
 
   /* Current record buffer. */
   volatile __u8 * input_ptr;
   volatile unsigned long input_size;
-  volatile __u32 * input_dma_handle, * input_next_dma_handle;
+  volatile __u32 input_dma_handle, input_next_dma_handle;
   volatile unsigned long input_dma_size, input_next_dma_size;
 
   /* Number of buffers in the pipe. */
@@ -68,6 +70,7 @@
 #define CS_STATUS_REV_A 0x04
 #define CS_STATUS_INTS_ON 0x08
 #define CS_STATUS_IS_ULTRA 0x10
+#define CS_STATUS_IS_EBUS 0x20
 
 #define CS_TIMEOUT      9000000
 
@@ -221,36 +224,43 @@
 /* 30 - Capture Upper */
 /* 31 - Capture Lower */
 
-/* Following are CSR register definitions for the Sparc */
+/* Following are APC CSR register definitions for the Sparc */
 
-#define CS_INT_PENDING 0x800000 /* Interrupt Pending */
-#define CS_PLAY_INT    0x400000 /* Playback interrupt */
-#define CS_CAPT_INT    0x200000 /* Capture interrupt */
-#define CS_GENL_INT    0x100000 /* General interrupt */
-#define CS_XINT_ENA    0x80000  /* General ext int. enable */
-#define CS_XINT_PLAY   0x40000  /* Playback ext intr */
-#define CS_XINT_CAPT   0x20000  /* Capture ext intr */
-#define CS_XINT_GENL   0x10000  /* Error ext intr */
-#define CS_XINT_EMPT   0x8000   /* Pipe empty interrupt */
-#define CS_XINT_PEMP   0x4000   /* Play pipe empty */
-#define CS_XINT_PNVA   0x2000   /* Playback NVA dirty */
-#define CS_XINT_PENA   0x1000   /* play pipe empty Int enable */
-#define CS_XINT_COVF   0x800    /* Cap data dropped on floor */
-#define CS_XINT_CNVA   0x400    /* Capture NVA dirty */
-#define CS_XINT_CEMP   0x200    /* Capture pipe empty interrupt */
-#define CS_XINT_CENA   0x100    /* Cap. pipe empty int enable */
-#define CS_PPAUSE      0x80     /* Pause the play DMA */
-#define CS_CPAUSE      0x40     /* Pause the capture DMA */
-#define CS_CDC_RESET   0x20     /* CODEC RESET */
-#define PDMA_READY     0x08     /* Play DMA Go */
-#define CDMA_READY     0x04     /* Capture DMA Go */
-#define CS_CHIP_RESET  0x01     /* Reset the chip */
+#define APC_INT_PENDING 0x800000 /* Interrupt Pending */
+#define APC_PLAY_INT    0x400000 /* Playback interrupt */
+#define APC_CAPT_INT    0x200000 /* Capture interrupt */
+#define APC_GENL_INT    0x100000 /* General interrupt */
+#define APC_XINT_ENA    0x80000  /* General ext int. enable */
+#define APC_XINT_PLAY   0x40000  /* Playback ext intr */
+#define APC_XINT_CAPT   0x20000  /* Capture ext intr */
+#define APC_XINT_GENL   0x10000  /* Error ext intr */
+#define APC_XINT_EMPT   0x8000   /* Pipe empty interrupt */
+#define APC_XINT_PEMP   0x4000   /* Play pipe empty */
+#define APC_XINT_PNVA   0x2000   /* Playback NVA dirty */
+#define APC_XINT_PENA   0x1000   /* play pipe empty Int enable */
+#define APC_XINT_COVF   0x800    /* Cap data dropped on floor */
+#define APC_XINT_CNVA   0x400    /* Capture NVA dirty */
+#define APC_XINT_CEMP   0x200    /* Capture pipe empty interrupt */
+#define APC_XINT_CENA   0x100    /* Cap. pipe empty int enable */
+#define APC_PPAUSE      0x80     /* Pause the play DMA */
+#define APC_CPAUSE      0x40     /* Pause the capture DMA */
+#define APC_CDC_RESET   0x20     /* CODEC RESET */
+#define APC_PDMA_READY     0x08     /* Play DMA Go */
+#define APC_CDMA_READY     0x04     /* Capture DMA Go */
+#define APC_CHIP_RESET  0x01     /* Reset the chip */
+
+#define APC_INIT_SETUP  (APC_CDMA_READY | APC_PDMA_READY | APC_XINT_ENA | APC_XINT_PLAY | APC_XINT_GENL | APC_INT_PENDING | APC_PLAY_INT | APC_CAPT_INT | APC_GENL_INT) 
+
+#define APC_PLAY_SETUP  (APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA | APC_XINT_PLAY | APC_XINT_EMPT | APC_XINT_GENL | APC_XINT_PENA | APC_PDMA_READY)
 
-#define CS_INIT_SETUP  (CDMA_READY | PDMA_READY | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_GENL | CS_INT_PENDING | CS_PLAY_INT | CS_CAPT_INT | CS_GENL_INT) 
+#define APC_CAPT_SETUP  (APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA | APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL | APC_CDMA_READY)
 
-#define CS_PLAY_SETUP  (CS_GENL_INT | CS_PLAY_INT | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_EMPT | CS_XINT_GENL | CS_XINT_PENA | PDMA_READY)
+/* Following are EB2 CSR register definitions for the Sparc */
 
-#define CS_CAPT_SETUP  (CS_GENL_INT | CS_CAPT_INT | CS_XINT_ENA | CS_XINT_CAPT | CS_XINT_CEMP | CS_XINT_GENL | CDMA_READY)
+/* asm/ebus.h has the base settings */
+
+#define EB2_PLAY_SETUP (EBUS_DCSR_BURST_SZ_8|EBUS_DCSR_INT_EN|EBUS_DCSR_EN_DMA|EBUS_DCSR_EN_CNT|EBUS_DCSR_TC)
+#define EB2_CAPT_SETUP (EBUS_DCSR_BURST_SZ_8|EBUS_DCSR_INT_EN|EBUS_DCSR_EN_DMA|EBUS_DCSR_EN_CNT|EBUS_DCSR_TC|EBUS_DCSR_WRITE)
 
 #define CS4231_MIN_ATEN     (0)
 #define CS4231_MAX_ATEN     (31)
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c
--- v2.2.3/linux/drivers/sbus/audio/dbri.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/sbus/audio/dbri.c	Mon Mar 15 16:11:30 1999
@@ -2,11 +2,10 @@
  * drivers/sbus/audio/dbri.c
  *
  * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
- * The SparcLinux interface was adopted from the CS4231 driver.
+ * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
  *
  * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
  * on Sun SPARCstation 10, 20, LX and Voyager models.
- * NOTE: This driver only supports audio for now, there is NO SUPPORT for ISDN.
  *
  * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
  *   data time multiplexer with ISDN support (aka T7259)
@@ -59,6 +58,7 @@
 #include <asm/io.h>
 #include <asm/delay.h>
 #include <asm/sbus.h>
+#include <asm/pgtable.h>
 
 #include <asm/audioio.h>
 #include "dbri.h"
@@ -69,7 +69,7 @@
 #include "../../isdn/hisax/foreign.h"
 #endif
 
-/* #define DBRI_DEBUG */
+#define DBRI_DEBUG
 
 #ifdef DBRI_DEBUG
 
@@ -80,14 +80,17 @@
 #define D_MM	(1<<3)
 #define D_USR	(1<<4)
 
-static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR;
+/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */
+static int dbri_debug = 0;
+MODULE_PARM(dbri_debug, "i");
+
 static char *cmds[] = { 
   "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
   "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
 };
 
 /* Bit hunting */
-#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->cmd[i]); }
+#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->dma->cmd[i]); }
 
 #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value)
 
@@ -103,14 +106,16 @@
 
 #define MAX_DRIVERS	2	/* Increase this if need more than 2 DBRI's */
 
-#define WAIT_INTR1	0xbe
-#define WAIT_INTR2	0xbf
-
 static struct sparcaudio_driver drivers[MAX_DRIVERS];
-static char drv_name[] = "DBRI/audio";
-static int num_drivers;
+static int num_drivers = 0;
+
+
+/*
+****************************************************************************
+************** DBRI initialization and command synchronization *************
+****************************************************************************
+*/
 
-static void * output_callback_arg;
 
 /*
  * Commands are sent to the DBRI by building a list of them in memory,
@@ -137,35 +142,46 @@
  * in DBRI register 0.  I've tried to implement this in such a way
  * that might make implementing a more sophisticated scheme easier.
  *
- * Every time a routine wants to write commands to the DBRI, it
- * must first call dbri_cmdlock() and get an initial index into dbri->cmd
- * (currently always 0) in return.  After the commands have been
- * write (index incremented after each one), dbri_cmdsend() is called
- * with the final index value.
+ * Every time a routine wants to write commands to the DBRI, it must
+ * first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
+ * in return.  After the commands have been writen, dbri_cmdsend() is
+ * called with the final pointer value.
  */
 
-static int dbri_cmdlock(struct dbri *dbri)
+static int dbri_locked = 0;			/* XXX not SMP safe! XXX */
+
+static volatile int * dbri_cmdlock(struct dbri *dbri)
 {
-       return 0;
+        if (dbri_locked) {
+                printk("DBRI: Command buffer locked! (bug in driver)\n");
+        }
+        dbri_locked ++;
+        return dbri->dma->cmd;
 }
 
-static void dbri_cmdsend(struct dbri *dbri, int n)
+static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd)
 {
         int maxloops = 1000000;
 
-        dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
-        dbri->regs->reg8 = (int)dbri->cmd;
-
-        while (maxloops > 0 && (dbri->regs->reg0 & D_P));
+        dbri_locked --;
+        if (dbri_locked != 0) {
+                printk("DBRI: Command buffer improperly locked! (bug in driver)\n");
+        } else if ((cmd - dbri->dma->cmd) >= DBRI_NO_CMDS-1) {
+                printk("DBRI: Command buffer overflow! (bug in driver)\n");
+        } else {
+                *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+                *(cmd++) = DBRI_CMD(D_WAIT, 0, 0);
+                dbri->regs->reg8 = (int)dbri->dma_dvma->cmd;
+                while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P));
+        }
 
         if (maxloops == 0) {
-                printk("DBRI: Maxloops exceeded in dbri_cmdsend\n");
+                printk("DBRI: Chip never completed command buffer\n");
         }
 }
 
-static void dbri_reset(struct sparcaudio_driver *drv)
+static void dbri_reset(struct dbri *dbri)
 {
-	struct dbri *dbri = (struct dbri *)drv->private;
 	int i;
 
 	dprintk(D_GEN, ("DBRI: reset 0:%x 2:%x 8:%x 9:%x\n",
@@ -177,75 +193,662 @@
 		udelay(10);
 }
 
-static void dbri_detach(struct sparcaudio_driver *drv)
+static void dbri_detach(struct dbri *dbri)
 {
-        struct dbri *info = (struct dbri *)drv->private;
-
-	dbri_reset(drv);
-        unregister_sparcaudio_driver(drv, 1);
-        free_irq(info->irq, drv);
-        sparc_free_io(info->regs, info->regs_size);
-        kfree(drv->private);
+	dbri_reset(dbri);
+        free_irq(dbri->irq, dbri);
+        sparc_free_io(dbri->regs, dbri->regs_size);
+        /* Should we release the DMA structure dbri->dma here? */
+        kfree(dbri);
 }
 
 
-static void dbri_initialize(struct sparcaudio_driver *drv)
+static void dbri_initialize(struct dbri *dbri)
 {
-	struct dbri *dbri = (struct dbri *)drv->private;
-	int n;
+        int n;
+	volatile int *cmd;
 
-        dbri_reset(drv);
-	dbri->wait = NULL;
+        dbri_reset(dbri);
 
 	dprintk(D_GEN, ("DBRI: init: cmd: %x, int: %x\n",
-			(int)dbri->cmd, (int)dbri->intr));
+			(int)dbri->dma->cmd, (int)dbri->dma->intr));
 
 	/*
 	 * Initialize the interrupt ringbuffer.
 	 */
 	for(n = 0; n < DBRI_NO_INTS-1; n++)
-		dbri->intr[n * DBRI_INT_BLK] = 
-					(int)(&dbri->intr[(n+1)*DBRI_INT_BLK]);
-	dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr);
+		dbri->dma->intr[n * DBRI_INT_BLK] = 
+                        (int)(&dbri->dma_dvma->intr[(n+1)*DBRI_INT_BLK]);
+	dbri->dma->intr[n * DBRI_INT_BLK] = (int)(dbri->dma_dvma->intr);
 	dbri->dbri_irqp = 1;
 
-#ifdef USE_SBUS_BURSTS
-        /* Enable 4-word, 8-word, and 16-word SBus Bursts */
-	dbri->regs->reg0 |= (D_G|D_S|D_E);
-#else
-        /* Disable 4-word, 8-word, and 16-word SBus Bursts */
+        /* We should query the openprom to see what burst sizes this
+         * SBus supports.  For now, just disable all SBus bursts */
         dbri->regs->reg0 &= ~(D_G|D_S|D_E);
-#endif
 
 	/*
 	 * Set up the interrupt queue
 	 */
-	n = dbri_cmdlock(dbri);
+	cmd = dbri_cmdlock(dbri);
 
-	dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0);
-	dbri->cmd[n++] = (int)(dbri->intr);
+	*(cmd++) = DBRI_CMD(D_IIQ, 0, 0);
+	*(cmd++) = (int)(dbri->dma_dvma->intr);
 
-        dbri_cmdsend(dbri, n);
+        dbri_cmdsend(dbri, cmd);
 }
 
 
+/*
+****************************************************************************
+*************************** DBRI interrupt handler *************************
+****************************************************************************
+*/
+
 
 /*
  * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
- * So we have to reverse the bits. Note: only 1, 2 or 4 bytes are supported.
+ * So we have to reverse the bits. Note: not all bit lengths are supported
  */
 static __u32 reverse_bytes(__u32 b, int len)
 {
 	switch(len) {
-		case 4: b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
-		case 2: b = ((b & 0xff00ff00) >>  8) | ((b & 0x00ff00ff) <<  8);
-		case 1: b = ((b & 0xf0f0f0f0) >>  4) | ((b & 0x0f0f0f0f) <<  4);
-			b = ((b & 0xcccccccc) >>  2) | ((b & 0x33333333) <<  2);
-			b = ((b & 0xaaaaaaaa) >>  1) | ((b & 0x55555555) <<  1);
+        case 32:
+                b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
+        case 16:
+                b = ((b & 0xff00ff00) >>  8) | ((b & 0x00ff00ff) <<  8);
+        case 8:
+                b = ((b & 0xf0f0f0f0) >>  4) | ((b & 0x0f0f0f0f) <<  4);
+        case 4:
+                b = ((b & 0xcccccccc) >>  2) | ((b & 0x33333333) <<  2);
+        case 2:
+                b = ((b & 0xaaaaaaaa) >>  1) | ((b & 0x55555555) <<  1);
+        case 1:
+                break;
+        default:
+                printk("DBRI reverse_bytes: unsupported length\n");
 	}
 	return b;
 }
 
+/* transmission_complete_intr()
+ *
+ * Called by main interrupt handler when DBRI signals transmission complete
+ * on a pipe.
+ *
+ * Walks through the pipe's list of transmit buffer descriptors, releasing
+ * each one's DMA buffer (if present) and signaling its callback routine
+ * (if present), before flaging the descriptor available and proceeding
+ * to the next one.
+ *
+ * Assumes that only the last in a chain of descriptors will have FINT
+ * sent to signal an interrupt, so that the chain will be completely
+ * transmitted by the time we get here, and there's no need to save
+ * any of the descriptors.  In particular, use of the DBRI's CDP command
+ * is precluded, but I've not been able to get CDP working reliably anyway.
+ */
+
+static void transmission_complete_intr(struct dbri *dbri, int pipe)
+{
+        int td = dbri->pipes[pipe].desc;
+        int status;
+        void *buffer;
+        void (*callback)(void *, int);
+
+        dbri->pipes[pipe].desc = -1;
+
+        for (; td >= 0; td = dbri->descs[td].next) {
+
+                if (td >= DBRI_NO_DESCS) {
+                        printk("DBRI: invalid td on pipe %d\n", pipe);
+                        return;
+                }
+
+                status = dbri->dma->desc[td].word4;
+
+                buffer = dbri->descs[td].buffer;
+                if (buffer) {
+                        mmu_release_scsi_one(sbus_dvma_addr(buffer),
+                                             dbri->descs[td].len,
+                                             dbri->sdev->my_bus);
+                }
+
+                callback = dbri->descs[td].output_callback;
+                if (callback != NULL) {
+                        callback(dbri->descs[td].output_callback_arg,
+                                 DBRI_TD_STATUS(status) & 0xe);
+                }
+
+                dbri->descs[td].inuse = 0;
+        }
+}
+
+static void reception_complete_intr(struct dbri *dbri, int pipe)
+{
+        int rd = dbri->pipes[pipe].desc;
+        int status;
+        void *buffer;
+        void (*callback)(void *, int, unsigned int);
+
+        if (rd < 0 || rd >= DBRI_NO_DESCS) {
+                printk("DBRI: invalid rd on pipe %d\n", pipe);
+                return;
+        }
+
+        dbri->descs[rd].inuse = 0;
+        dbri->pipes[pipe].desc = -1;
+        status = dbri->dma->desc[rd].word1;
+
+        buffer = dbri->descs[rd].buffer;
+        if (buffer) {
+                mmu_release_scsi_one(sbus_dvma_addr(buffer),
+                                     dbri->descs[rd].len,
+                                     dbri->sdev->my_bus);
+        }
+
+        callback = dbri->descs[rd].input_callback;
+        if (callback != NULL) {
+                callback(dbri->descs[rd].input_callback_arg,
+                         DBRI_RD_STATUS(status),
+                         DBRI_RD_CNT(status)-2);
+        }
+}
+
+static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+{
+	struct dbri *dbri = (struct dbri *)opaque;
+	int x;
+	
+	/*
+	 * Read it, so the interrupt goes away.
+	 */
+	x = dbri->regs->reg1;
+
+	if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
+		/*
+		 * What should I do here ?
+		 */
+		if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
+		if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
+		if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
+		if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+	}
+
+	if (!(x & D_IR))	/* Not for us */
+		return;
+
+	x = dbri->dma->intr[dbri->dbri_irqp];
+	while (x != 0) {
+                int val = D_INTR_GETVAL(x);
+                int channel = D_INTR_GETCHAN(x);
+
+		dbri->dma->intr[dbri->dbri_irqp] = 0;
+
+		if(D_INTR_GETCHAN(x) == D_INTR_CMD) {
+			dprintk(D_INT,("DBRI: INTR: Command: %-5s  Value:%d\n",
+				cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x)));
+		} else {
+			dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
+				D_INTR_GETCHAN(x), D_INTR_GETCODE(x),
+				D_INTR_GETRVAL(x)));
+		}
+
+                if (D_INTR_GETCODE(x) == D_INTR_SBRI) {
+
+                        /* SBRI - BRI status change */
+
+                        int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
+                        dbri->liu_state = liu_states[val & 0x7];
+                        if (dbri->liu_callback)
+                                dbri->liu_callback(dbri->liu_callback_arg);
+                }
+
+                if (D_INTR_GETCODE(x) == D_INTR_BRDY) {
+                        reception_complete_intr(dbri, channel);
+                }
+
+                if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
+                        transmission_complete_intr(dbri, channel);
+                }
+
+                if (D_INTR_GETCODE(x) == D_INTR_FXDT) {
+
+                        /* FXDT - Fixed data change */
+
+                        if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) {
+                                val = reverse_bytes(val, dbri->pipes[channel].length);
+                        }
+
+                        if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) {
+                                * dbri->pipes[channel].recv_fixed_ptr = val;
+                        }
+                }
+
+
+		dbri->dbri_irqp++;
+		if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
+			dbri->dbri_irqp = 1;
+		else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0)
+			dbri->dbri_irqp++;
+		x = dbri->dma->intr[dbri->dbri_irqp];
+	}
+}
+
+
+/*
+****************************************************************************
+************************** DBRI data pipe management ***********************
+****************************************************************************
+*/
+
+
+/* reset_pipe(dbri, pipe)
+ *
+ * Called on an in-use pipe to clear anything being transmitted or received
+ */
+
+static void reset_pipe(struct dbri *dbri, int pipe)
+{
+        int sdp;
+        volatile int *cmd;
+
+        if (pipe < 0 || pipe > 31) {
+                printk("DBRI: reset_pipe called with illegal pipe number\n");
+                return;
+        }
+
+        sdp = dbri->pipes[pipe].sdp;
+        if (sdp == 0) {
+                printk("DBRI: reset_pipe called on uninitialized pipe\n");
+                return;
+        }
+
+        cmd = dbri_cmdlock(dbri);
+        *(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P);
+        *(cmd++) = 0;
+        dbri_cmdsend(dbri, cmd);
+
+        dbri->pipes[pipe].desc = -1;
+}
+
+static void setup_pipe(struct dbri *dbri, int pipe, int sdp)
+{
+        if (pipe < 0 || pipe > 31) {
+                printk("DBRI: setup_pipe called with illegal pipe number\n");
+                return;
+        }
+
+        if ((sdp & 0xf800) != sdp) {
+                printk("DBRI: setup_pipe called with strange SDP value\n");
+                /* sdp &= 0xf800; */
+        }
+
+        sdp |= D_PIPE(pipe);
+        dbri->pipes[pipe].sdp = sdp;
+
+        reset_pipe(dbri, pipe);
+}
+
+enum master_or_slave { CHImaster, CHIslave };
+
+static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
+                      int bits_per_frame)
+{
+        volatile int *cmd;
+        int val;
+
+        cmd = dbri_cmdlock(dbri);
+
+	/* Set CHI Anchor: Pipe 16 */
+
+        val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
+                D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
+	*(cmd++) = DBRI_CMD(D_DTS, 0, val);
+	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+	*(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+
+        dbri->pipes[16].sdp = 1;
+        dbri->pipes[16].nextpipe = 16;
+
+        if (master_or_slave == CHIslave) {
+                /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
+                 *
+                 * CHICM  = 0 (slave mode, 8 kHz frame rate)
+                 * IR     = give immediate CHI status interrupt
+                 * EN     = give CHI status interrupt upon change
+                 */
+                *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)
+                                    | D_CHI_IR | D_CHI_EN);
+        } else {
+                /* Setup DBRI for CHI Master - generate clock, FS
+                 *
+                 * BPF				=  bits per 8 kHz frame
+                 * 12.288 MHz / CHICM_divisor	= clock rate
+                 * FD  =  1 - drive CHIFS on rising edge of CHICK
+                 */
+
+                int clockrate = bits_per_frame * 8;
+                int divisor   = 12288 / clockrate;
+
+                if (divisor > 255 || divisor * clockrate != 12288) {
+                        printk("DBRI: illegal bits_per_frame in setup_chi\n");
+                }
+
+                *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
+                                    | D_CHI_IR | D_CHI_EN
+                                    | D_CHI_BPF(bits_per_frame));
+        }
+
+        /* CHI Data Mode
+         *
+         * RCE   =  0 - receive on falling edge of CHICK
+         * XCE   =  1 - transmit on rising edge of CHICK
+         * XEN   =  1 - enable transmitter
+         * REN   =  1 - enable receiver
+         */
+
+        *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+        *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+        dbri_cmdsend(dbri, cmd);
+}
+
+enum in_or_out { PIPEinput, PIPEoutput };
+
+static void link_time_slot(struct dbri *dbri, int pipe,
+                           enum in_or_out direction, int prevpipe,
+                           int length, int cycle)
+{
+        volatile int *cmd;
+        int val;
+        int nextpipe;
+
+        if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
+                printk("DBRI: link_time_slot called with illegal pipe number\n");
+                return;
+        }
+
+        if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) {
+                printk("DBRI: link_time_slot called on uninitialized pipe\n");
+                return;
+        }
+
+        if (pipe == prevpipe) {
+                nextpipe = pipe;
+        } else {
+                nextpipe = dbri->pipes[prevpipe].nextpipe;
+        }
+
+        dbri->pipes[pipe].nextpipe = nextpipe;
+        dbri->pipes[pipe].cycle = cycle;
+        dbri->pipes[pipe].length = length;
+
+        cmd = dbri_cmdlock(dbri);
+
+        if (direction == PIPEinput) {
+                val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+                *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+                *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+                *(cmd++) = 0;
+        } else {
+                val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+                *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+                *(cmd++) = 0;
+                *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+        }
+
+        dbri_cmdsend(dbri, cmd);
+}
+
+static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data)
+{
+        volatile int *cmd;
+
+        if (pipe < 16 || pipe > 31) {
+                printk("DBRI: xmit_fixed called with illegal pipe number\n");
+                return;
+        }
+
+        if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
+                printk("DBRI: xmit_fixed called on non-fixed pipe\n");
+                return;
+        }
+
+        if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+                printk("DBRI: xmit_fixed called on receive pipe\n");
+                return;
+        }
+
+        /* DBRI short pipes always transmit LSB first */
+
+        if (dbri->pipes[pipe].sdp & D_SDP_MSB) {
+                data = reverse_bytes(data, dbri->pipes[pipe].length);
+        }
+
+        cmd = dbri_cmdlock(dbri);
+
+        *(cmd++) = DBRI_CMD(D_SSP, 0, pipe);
+        *(cmd++) = data;
+
+        dbri_cmdsend(dbri, cmd);
+}
+
+/* recv_fixed()
+ *
+ * Receive data on a "fixed" pipe - i.e, one whose contents are not
+ * expected to change much, and which we don't need to read constantly
+ * into a buffer.  The DBRI only interrupts us when the data changes.
+ * Only short pipes (numbers 16-31) can be used in fixed data mode.
+ *
+ * Pass this function a pointer to a 32-bit field, no matter how large
+ * the actual time slot is.  The interrupt handler takes care of bit
+ * ordering and alignment.  An 8-bit time slot will always end up
+ * in the low-order 8 bits, filled either MSB-first or LSB-first,
+ * depending on the settings passed to setup_pipe()
+ */
+
+static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr)
+{
+        if (pipe < 16 || pipe > 31) {
+                printk("DBRI: recv_fixed called with illegal pipe number\n");
+                return;
+        }
+
+        if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
+                printk("DBRI: recv_fixed called on non-fixed pipe\n");
+                return;
+        }
+
+        if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+                printk("DBRI: recv_fixed called on transmit pipe\n");
+                return;
+        }
+
+        dbri->pipes[pipe].recv_fixed_ptr = ptr;
+}
+
+
+static void xmit_on_pipe(struct dbri *dbri, int pipe,
+                         void * buffer, unsigned int len,
+                         void (*callback)(void *, int), void * callback_arg)
+{
+        volatile int *cmd;
+        int td = 0;
+        int first_td = -1;
+        int last_td;
+        __u32 dvma_buffer;
+
+        if (pipe < 0 || pipe > 15) {
+                printk("DBRI: xmit_on_pipe called with illegal pipe number\n");
+                return;
+        }
+
+        if (dbri->pipes[pipe].sdp == 0) {
+                printk("DBRI: xmit_on_pipe called on uninitialized pipe\n");
+                return;
+        }
+
+        if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+                printk("DBRI: xmit_on_pipe called on receive pipe\n");
+                return;
+        }
+
+        /* XXX Fix this XXX
+         * Should be able to queue multiple buffers to send on a pipe
+         */
+
+        if (dbri->pipes[pipe].desc != -1) {
+                printk("DBRI: xmit_on_pipe called on active pipe\n");
+                return;
+        }
+
+        dvma_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus);
+
+        while (len > 0) {
+                int mylen;
+
+                for (td; td < DBRI_NO_DESCS; td ++) {
+                        if (! dbri->descs[td].inuse) break;
+                }
+                if (td == DBRI_NO_DESCS) {
+                        break;
+                }
+
+                if (len > (1 << 13) - 1) {
+                        mylen = (1 << 13) - 1;
+                } else {
+                        mylen = len;
+                }
+
+                dbri->descs[td].inuse = 1;
+                dbri->descs[td].next = -1;
+                dbri->descs[td].buffer = NULL;
+                dbri->descs[td].output_callback = NULL;
+                dbri->descs[td].input_callback = NULL;
+
+                dbri->dma->desc[td].word1 = DBRI_TD_CNT(mylen);
+                dbri->dma->desc[td].ba = dvma_buffer;
+                dbri->dma->desc[td].nda = 0;
+                dbri->dma->desc[td].word4 = 0;
+
+                if (first_td == -1) {
+                        first_td = td;
+                } else {
+                        dbri->descs[last_td].next = td;
+                        dbri->dma->desc[last_td].nda =
+                                (int) & dbri->dma_dvma->desc[td];
+                }
+
+                last_td = td;
+                dvma_buffer += mylen;
+                len -= mylen;
+        }
+
+        if (first_td == -1) {
+                printk("xmit_on_pipe: No descriptors available\n");
+                return;
+        }
+
+        if (len > 0) {
+                printk("xmit_on_pipe: Insufficient descriptors; data truncated\n");
+        }
+
+        dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
+
+        dbri->descs[last_td].buffer = buffer;
+        dbri->descs[last_td].len = len;
+        dbri->descs[last_td].output_callback = callback;
+        dbri->descs[last_td].output_callback_arg = callback_arg;
+
+        dbri->pipes[pipe].desc = first_td;
+
+        cmd = dbri_cmdlock(dbri);
+
+        *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
+        *(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+
+        dbri_cmdsend(dbri, cmd);
+}
+
+static void recv_on_pipe(struct dbri *dbri, int pipe,
+                         void * buffer, unsigned int len,
+                         void (*callback)(void *, int, unsigned int),
+                         void * callback_arg)
+{
+        volatile int *cmd;
+        int rd;
+
+        if (pipe < 0 || pipe > 15) {
+                printk("DBRI: recv_on_pipe called with illegal pipe number\n");
+                return;
+        }
+
+        if (dbri->pipes[pipe].sdp == 0) {
+                printk("DBRI: recv_on_pipe called on uninitialized pipe\n");
+                return;
+        }
+
+        if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
+                printk("DBRI: recv_on_pipe called on transmit pipe\n");
+                return;
+        }
+
+        /* XXX Fix this XXX
+         * Should be able to queue multiple buffers to send on a pipe
+         */
+
+        if (dbri->pipes[pipe].desc != -1) {
+                printk("DBRI: recv_on_pipe called on active pipe\n");
+                return;
+        }
+
+        /* XXX Fix this XXX
+         * Use multiple descriptors, if needed, to fit in all the data
+         */
+
+        if (len > (1 << 13) - 1) {
+                printk("recv_on_pipe called with len=%d; truncated\n", len);
+                len = (1 << 13) - 1;
+        }
+
+        /* Make sure buffer size is multiple of four */
+        len &= ~3;
+
+        for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
+                if (! dbri->descs[rd].inuse) break;
+        }
+        if (rd == DBRI_NO_DESCS) {
+                printk("DBRI xmit_on_pipe: No descriptors available\n");
+                return;
+        }
+
+        dbri->dma->desc[rd].word1 = 0;
+        dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len,
+                                                  dbri->sdev->my_bus);
+        dbri->dma->desc[rd].nda = 0;
+        dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len);
+
+        dbri->descs[rd].buffer = buffer;
+        dbri->descs[rd].len = len;
+        dbri->descs[rd].input_callback = callback;
+        dbri->descs[rd].input_callback_arg = callback_arg;
+
+        dbri->pipes[pipe].desc = rd;
+
+        cmd = dbri_cmdlock(dbri);
+
+        *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P);
+        *(cmd++) = (int) & dbri->dma_dvma->desc[rd];
+
+        dbri_cmdsend(dbri, cmd);
+}
+
+
+/*
+****************************************************************************
+*********************** CS4215 audio codec management **********************
+****************************************************************************
+*/
 
 
 static void mmcodec_default(struct cs4215 *mm)
@@ -266,7 +869,7 @@
 	 * Control Time Slot 1-4
 	 * 0: Default I/O voltage scale
 	 * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
-	 * 2: Serial enable, CHI master, 1 CHI device (64bit), clock 1
+	 * 2: Serial enable, CHI master, 128 bits per frame, clock 1
 	 * 3: Tests disabled
 	 */
 	mm->ctrl[0] = CS4215_RSRVD_1;
@@ -278,10 +881,6 @@
 
 static void mmcodec_init_data(struct dbri *dbri)
 {
-	int val, n;
-
-	n = dbri_cmdlock(dbri);
-
 	/*
 	 * Data mode:
 	 * Pipe  4: Send timeslots 1-4 (audio data)
@@ -295,80 +894,27 @@
          * bits.  The CS4215, it seems, observes TSIN (the delayed signal)
          * even if it's the CHI master.  Don't ask me...
 	 */
-	
-
-	/* Pipe 4: SDP */
-	val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;
-
-
-	/* Pipe 17: SDP */
-	val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;				/* Fixed data */
-
-        /* Pipe 17: SSP */
-	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
-	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4);
-
-
-	/* Pipe 6: SDP */
-	val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_6);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;
-
-
-	/* Pipe 20: SDP */
-	val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_20);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;				/* Fixed data */
-
 
 
-	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+        /* Switch CS4215 to data mode - set PIO3 to 1 */
+	dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 |
+				(dbri->mm.onboard ? D_PIO0 : D_PIO2);
 
+	reset_chi(dbri, CHIslave, 0);
 
-        /* Pipe 4: DTS */
-	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = 0;
-#if 0
-        /* Full blown, four time slots, 16 bit stereo */
-	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
-#else
-        /* Single time slot, 8 bit mono */
-	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
-#endif
+        setup_pipe(dbri,  4, D_SDP_MEM   | D_SDP_TO_SER | D_SDP_MSB);
+        setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+        setup_pipe(dbri,  6, D_SDP_MEM   | D_SDP_FROM_SER | D_SDP_MSB);
+        setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
 
-        /* Pipe 17: DTS */
-	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = 0;
-	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(40) | D_TS_NEXT(D_P_16);
-
-        /* Pipe 6: DTS */
-	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-#if 0
-        /* Full blown, four time slots, 16 bit stereo */
-	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
-#else
-        /* Single time slot, 8 bit mono */
-	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
-#endif
-	dbri->cmd[n++] = 0;
-
-        /* Pipe 20: DTS */
-	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_20);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16); 
-	dbri->cmd[n++] = 0;
+        /* Pipes 4 and 6 - Single time slot, 8 bit mono */
 
-        /* CHI: Slave mode; enable interrupts */
-	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN);
+        link_time_slot(dbri, 17, PIPEoutput, 16, 32, 32);
+        link_time_slot(dbri,  4, PIPEoutput, 17, 8, 128);
+        link_time_slot(dbri,  6, PIPEinput, 16, 8, 0);
+        link_time_slot(dbri, 20, PIPEinput, 6, 16, 40);
 
-        dbri_cmdsend(dbri, n);
+        xmit_fixed(dbri, 17, *(int *)dbri->mm.data);
 }
 
 
@@ -377,7 +923,7 @@
  */
 static void mmcodec_setctrl(struct dbri *dbri)
 {
-	int n, val;
+	int i, val;
 
 	/*
 	 * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
@@ -398,15 +944,15 @@
          * into Data mode and put the DBRI into slave mode.  Various timing
          * requirements must be observed along the way.
          *
-         * Oh, and one more thing - when the DBRI is master (and only when
-         * the DBRI is master), the addressing of the CS4215's time slots
-         * is offset by eight bits, so we add eight to all the "cycle"
-         * values in the Define Time Slot (DTS) commands.  This is done in
-         * hardware by a TI 248 that delays the DBRI->4215 frame sync signal
-         * by eight clock cycles.  Anybody know why?
+         * Oh, and one more thing, on a SPARCStation 20 (and maybe
+         * others?), the addressing of the CS4215's time slots is
+         * offset by eight bits, so we add eight to all the "cycle"
+         * values in the Define Time Slot (DTS) commands.  This is
+         * done in hardware by a TI 248 that delays the DBRI->4215
+         * frame sync signal by eight clock cycles.  Anybody know why?
          */
 
-	n = dbri_cmdlock(dbri);
+        reset_chi(dbri, CHImaster, 128);
 
 	/*
 	 * Control mode:
@@ -415,472 +961,325 @@
 	 * Pipe 19: Receive timeslot 7 (version). 
 	 */
 
-	/* Set CHI Anchor: Pipe 16. This should take care of the rest. */
-	val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
-	      D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
-	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
-
-
-	/* Setup the pipes first */
-	val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_17);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;
-
-	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_18);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;
-
-	val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_19);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = 0;
+        setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+        setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
+        setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
+
+        link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128);
+        link_time_slot(dbri, 18, PIPEinput, 16, 8, 0);
+        link_time_slot(dbri, 19, PIPEinput, 18, 8, 48);
+
+        recv_fixed(dbri, 18, & dbri->mm.status);
+        recv_fixed(dbri, 19, & dbri->mm.version);
+
+        /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
 
-	/* Fill in the data to send */
 	dbri->mm.ctrl[0] &= ~CS4215_CLB;
-	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
-	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
+        xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
 
-	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+        i = 1000000;
+        while ((! dbri->mm.status & CS4215_CLB) && i--);
+        if (i == 0) {
+                printk("CS4215 didn't respond to CLB\n");
+		return;
+        }
 
+        /* Terminate CS4215 control mode - data sheet says
+         * "Set CLB=1 and send two more frames of valid control info"
+         */
 
+	dbri->mm.ctrl[0] |= CS4215_CLB;
+        xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
 
-	/* Link the timeslots */
+        /* Two frames of control info @ 8kHz frame rate = 250 us delay */
+        udelay(250);
+}
 
-        /* Pipe 17 - CS4215 Status, Data Format, Serial Control, Test - output
-         *           time slots 1, 2, 3 and 4 - 32 bits
-         */
+static int mmcodec_init(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
+	int reg2 = dbri->regs->reg2;
 
-	val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = 0;
-	dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16);
 
-        /* Pipe 18 - CS4215 Status and Data Format - input
-         *           time slots 1 & 2 - 16 bits
-         */
+	/* Look for the cs4215 chips */
+	if(reg2 & D_PIO2) {
+		dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n"));
+		dbri->mm.onboard = 1;
+	}
+	if(reg2 & D_PIO0) {
+		dprintk(D_MM, ("DBRI: Speakerbox detected\n"));
+		dbri->mm.onboard = 0;
+	}
+	
 
-	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16);
-	dbri->cmd[n++] = 0;
+	/* Using the Speakerbox, if both are attached.  */
+	if((reg2 & D_PIO2) && (reg2 & D_PIO0)) {
+		printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n");
+		dbri->regs->reg2 = D_ENPIO2;
+		dbri->mm.onboard = 0;
+	}
+	if( !(reg2 & (D_PIO0|D_PIO2)) ) {
+		printk("DBRI: no mmcodec found.\n");
+		return -EIO;
+	}
 
-        /* Pipe 19 - CS4215 Revision - time slot 7, eight bits - input
-         */
 
-	val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(56) | D_TS_NEXT(D_P_16);
-	dbri->cmd[n++] = 0;
+	/* Now talk to our baby */
+	dbri->regs->reg0 |= D_C;	/* Enable CHI */
 
+	mmcodec_default(&dbri->mm);
 
-	/* Setup DBRI for CHI Master
-         *
-         * BPF   =  128 (128 bits per 8 kHz frame = 1.024 MHz clock rate)
-         * CHICM =  12 (12.288 MHz / 24 = 1.024 MHz clock rate)
-         * FD    =  1 - drive CHIFS on rising edge of CHICK
-         *
-         * RCE   =  0 - receive on falling edge of CHICK
-         * XCE   =  1 - transmit on rising edge of CHICK
-         */
-	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(12) | D_CHI_FD |
-					D_CHI_IR | D_CHI_EN | D_CHI_BPF(128));
-	dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
-	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+	dbri->mm.version = 0xff;
+	mmcodec_setctrl(dbri);
+	if(dbri->mm.version == 0xff) 
+		return -EIO;
 
-        /* Wait for the command to complete */
-        dbri_cmdsend(dbri, n);
+	mmcodec_init_data(dbri);
 
-        /* Switch CS4215 to data mode - data sheet says
-         * "Set CLB=1 and send two more frames of valid control info"
-         */
-	n = dbri_cmdlock(dbri);
+	return 0;
+}
 
-	dbri->mm.ctrl[0] |= CS4215_CLB;
-	dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
-	dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
 
-        dbri_cmdsend(dbri, n);
+/*
+****************************************************************************
+******************** Interface with sparcaudio midlevel ********************
+****************************************************************************
+*/
 
-        /* Two frames of control info @ 8kHz frame rate = 250 us delay */
-        udelay(250);
 
-        n = dbri_cmdlock(dbri);
+static int dbri_open(struct inode * inode, struct file * file,
+                     struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
 
-	/* Now switch back to data mode */
-	/* Reset CHI Anchor: Stop Send/Receive */
-	val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
-	      D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
-	dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
-	dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+	MOD_INC_USE_COUNT;
 
+	return 0;
+}
 
-	/* Setup DBRI for CHI Slave */
-	dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
-	/* dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN); */
-	dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0x16);
+static void dbri_release(struct inode * inode, struct file * file,
+                         struct sparcaudio_driver *drv)
+{
+	MOD_DEC_USE_COUNT;
+}
 
+static int dbri_ioctl(struct inode * inode, struct file * file,
+                      unsigned int x, unsigned long y,
+                      struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-        /* Wait for command to complete */
-        dbri_cmdsend(dbri, n);
+static void dbri_audio_output_callback(void * callback_arg, int status)
+{
+        struct sparcaudio_driver *drv = callback_arg;
 
-        /* Switch CS4215 to data mode - set PIO3 to 1 */
-	dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 |
-				(dbri->mm.onboard ? D_PIO0 : D_PIO2);
+        sparcaudio_output_done(drv, 1);
 }
 
-static int mmcodec_init(struct sparcaudio_driver *drv)
+static void dbri_start_output(struct sparcaudio_driver *drv,
+                              __u8 * buffer, unsigned long count)
 {
 	struct dbri *dbri = (struct dbri *)drv->private;
-	int reg2 = dbri->regs->reg2;
-
 
-	/* Look for the cs4215 chips */
-	if(reg2 & D_PIO2) {
-		dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n"));
-		dbri->mm.onboard = 1;
-	}
-	if(reg2 & D_PIO0) {
-		dprintk(D_MM, ("DBRI: Speakerbox detected\n"));
-		dbri->mm.onboard = 0;
-	}
-	
+        /* Pipe 4 is audio transmit */
+        xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv);
+}
 
-	/* Using the Speakerbox, if both are attached.  */
-	if((reg2 & D_PIO2) && (reg2 & D_PIO0)) {
-		printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n");
-		dbri->regs->reg2 = D_ENPIO2;
-		dbri->mm.onboard = 0;
-	}
-	if( !(reg2 & (D_PIO0|D_PIO2)) ) {
-		printk("DBRI: no mmcodec found.\n");
-		return -EIO;
-	}
+static void dbri_stop_output(struct sparcaudio_driver *drv)
+{
+	struct dbri *dbri = (struct dbri *)drv->private;
 
+        reset_pipe(dbri, 4);
+}
 
-	/* Now talk to our baby */
-	dbri->regs->reg0 |= D_C;	/* Enable CHI */
+static void dbri_start_input(struct sparcaudio_driver *drv,
+                             __u8 * buffer, unsigned long len)
+{
+}
 
-	mmcodec_default(&dbri->mm);
+static void dbri_stop_input(struct sparcaudio_driver *drv)
+{
+}
 
-	dbri->mm.version = 0xff;
-	mmcodec_setctrl(dbri);
-	if(dbri->mm.version == 0xff) 
-		return -EIO;
+static void dbri_audio_getdev(struct sparcaudio_driver *drv,
+                              audio_device_t *devptr)
+{
+}
 
-	mmcodec_init_data(dbri);
+static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume)
+{
+        return 0;
+}
 
-	return 0;
+static int dbri_get_output_volume(struct sparcaudio_driver *drv)
+{
+        return 0;
 }
 
-void dbri_isdn_init(struct dbri *dbri)
+static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume)
 {
-        int n, val;
+        return 0;
+}
 
-        /* Pipe  0: Receive D channel
-         * Pipe  8: Receive B1 channel
-         * Pipe  9: Receive B2 channel
-         * Pipe  1: Transmit D channel
-         * Pipe 10: Transmit B1 channel
-         * Pipe 11: Transmit B2 channel
-         */
+static int dbri_get_input_volume(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-        n = dbri_cmdlock(dbri);
+static int dbri_set_monitor_volume(struct sparcaudio_driver *drv, int volume)
+{
+        return 0;
+}
 
-       /* Pipe 0: SDP */
-       val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = 0;
-
-       /* Pipe 8: SDP */
-       val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = 0;
-
-       /* Pipe 9: SDP */
-       val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_9);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = 0;
-
-       /* Pipe 1: SDP */
-       val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = 0;
-
-       /* Pipe 10: SDP */
-       val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = 0;
-
-       /* Pipe 11: SDP */
-       val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_11);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = 0;
-
-
-       dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
-
-        /* Pipe 0: DTS */
-       val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_0);
-       dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-       dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_0);
-       dbri->cmd[n++] = 0;
-
-        /* Pipe 8: DTS */
-       val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_8);
-       dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-       dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_0);
-       dbri->cmd[n++] = 0;
-
-        /* Pipe 9: DTS */
-       val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_8) | D_PIPE(D_P_9);
-       dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-       dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_0);
-       dbri->cmd[n++] = 0;
-
-        /* Pipe 1: DTS */
-       val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_1);
-       dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-       dbri->cmd[n++] = 0;
-       dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_1);
-
-        /* Pipe 10: DTS */
-       val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_10);
-       dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-       dbri->cmd[n++] = 0;
-       dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_1);
-
-        /* Pipe 11: DTS */
-       val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_10) | D_PIPE(D_P_11);
-       dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
-       dbri->cmd[n++] = 0;
-       dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_1);
+static int dbri_get_monitor_volume(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
+static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance)
+{
+        return 0;
+}
 
-        /* Wait for command to complete */
-       dbri_cmdsend(dbri, n);
+static int dbri_get_output_balance(struct sparcaudio_driver *drv)
+{
+        return 0;
 }
 
-void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
+static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance)
 {
-	struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
-	struct dbri *dbri = (struct dbri *)drv->private;
-	int x, val;
-	static int numint = 0;
-	
-	/*
-	 * Read it, so the interrupt goes away.
-	 */
-	x = dbri->regs->reg1;
-#if 0
-	if(numint++ > 20) {
-	    dbri->regs->reg0 = D_R; /* Soft Reset */
-	    numint = 0;
-	    printk("Soft reset\n");
-	}
-#endif
+        return 0;
+}
 
-	if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
-		/*
-		 * What should I do here ?
-		 */
-		if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
-		if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
-		if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
-		if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
-	}
+static int dbri_get_input_balance(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-	if (!(x & D_IR))	/* Not for us */
-		return;
+static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan)
+{
+        return 0;
+}
 
-	x = dbri->intr[dbri->dbri_irqp];
-	while (x != 0) {
-		dbri->intr[dbri->dbri_irqp] = 0;
+static int dbri_get_output_channels(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-		if(D_INTR_GETCHAN(x) == D_INTR_CMD) {
-			dprintk(D_INT,("DBRI: INTR: Command: %-5s  Value:%d\n",
-				cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x)));
-		} else {
-			dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
-				D_INTR_GETCHAN(x), D_INTR_GETCODE(x),
-				D_INTR_GETRVAL(x)));
-		}
+static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan)
+{
+        return 0;
+}
 
-		val = D_INTR_GETVAL(x);
+static int dbri_get_input_channels(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-                if (D_INTR_GETCODE(x) == D_INTR_SBRI) {
-                        int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
-                        dbri->liu_state = liu_states[val & 0x7];
-                        if (dbri->liu_callback)
-                                dbri->liu_callback(dbri->liu_callback_arg);
-                }
+static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec)
+{
+        return 8;
+}
 
-		switch(D_INTR_GETCHAN(x)) {
-			case D_INTR_CMD:
-#if 0
-				if(D_INTR_GETCMD(x) == D_WAIT)
-					if(val == WAIT_INTR1) {
-						dbri_cmdlocked = 0;
-						wake_up(&dbri->wait);
-					}
-					if(val == WAIT_INTR2)
-						wake_up(&dbri->int_wait);
-#endif
-				break;
+static int dbri_get_output_precision(struct sparcaudio_driver *drv)
+{
+        return 8;
+}
 
-                        case D_P_0:
-                                /* Pipe 0 - D channel receive */
-                                if (D_INTR_GETCODE(x) == D_INTR_BRDY &&
-                                    dbri->D.input_callback) {
-                                        dbri->D.input_callback(dbri->D.input_callback_arg,
-                                                               DBRI_RD_STATUS(dbri->D.rd.flags),
-                                                               DBRI_RD_CNT(dbri->D.rd.flags)-2);
-                                }
-                                break;
-
-                        case D_P_1:
-                                /* Pipe 1 - D channel transmit */
-                                if (D_INTR_GETCODE(x) == D_INTR_XCMP &&
-                                    dbri->D.output_callback) {
-                                        dbri->D.output_callback(dbri->D.output_callback_arg,
-                                                                DBRI_TD_STATUS(dbri->D.rd.flags)&0xe);
-                                }
-                                break;
-
-			case D_P_4:
-                                /* Pipe 4 - audio transmit */
-				if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
-					sparcaudio_output_done(output_callback_arg, 1);
-				}
-				break;
-
-                        case D_P_8:
-                                /* Pipe 8 - B1 channel receive */
-                                if (D_INTR_GETCODE(x) == D_INTR_BRDY &&
-                                    dbri->B[0].input_callback) {
-                                        dbri->B[0].input_callback(dbri->B[0].input_callback_arg,
-                                                                  DBRI_RD_STATUS(dbri->B[0].rd.flags),
-                                                                  DBRI_RD_CNT(dbri->B[0].rd.flags)-2);
-                                }
-                                break;
-
-                        case D_P_9:
-                                /* Pipe 9 - B2 channel receive */
-                                if (D_INTR_GETCODE(x) == D_INTR_BRDY &&
-                                    dbri->B[1].input_callback) {
-                                        dbri->B[1].input_callback(dbri->B[1].input_callback_arg,
-                                                                DBRI_RD_STATUS(dbri->B[1].rd.flags),
-                                                                DBRI_RD_CNT(dbri->B[1].rd.flags)-2);
-                                }
-                                break;
-
-                        case D_P_10:
-                                /* Pipe 10 - B1 channel transmit */
-                                if (D_INTR_GETCODE(x) == D_INTR_XCMP &&
-                                    dbri->B[0].output_callback) {
-                                        dbri->B[0].output_callback(dbri->B[0].output_callback_arg,
-                                                                   DBRI_TD_STATUS(dbri->B[0].rd.flags)&0xfe);
-                                }
-                                break;
-
-                        case D_P_11:
-                                /* Pipe 11 - B2 channel transmit */
-                                if (D_INTR_GETCODE(x) == D_INTR_XCMP &&
-                                    dbri->B[1].output_callback) {
-                                        dbri->B[1].output_callback(dbri->B[1].output_callback_arg,
-                                                                   DBRI_TD_STATUS(dbri->B[1].rd.flags)&0xfe);
-                                }
-                                break;
-
-			case D_P_18:
-                                /* Pipe 18 - receive CS4215 status */
-				if(val != 0) {
-					x = reverse_bytes(val,2)&CS4215_12_MASK;
-                                        printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl);
-					if(x == (*(int *)dbri->mm.ctrl >> 16)) {
-                                          printk("Comp ok\n");
-                                          wake_up(&dbri->int_wait);
-                                        }
-				}
-				break;
-			case D_P_19:
-                                /* Pipe 19 - receive CS4215 version */
-				if(val != 0) {
-					dbri->mm.version = 
-						reverse_bytes(val, 1) & 0xf;
-				}
-				break;
-		}
+static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec)
+{
+        return 8;
+}
 
-		dbri->dbri_irqp++;
-		if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
-			dbri->dbri_irqp = 1;
-		else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0)
-			dbri->dbri_irqp++;
-		x = dbri->intr[dbri->dbri_irqp];
-	}
+static int dbri_get_input_precision(struct sparcaudio_driver *drv)
+{
+        return 8;
+}
+
+static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
+{
+        return 0;
 }
 
+static int dbri_get_output_port(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-/*
-****************************************************************************
-******************** Interface with sparcaudio midlevel ********************
-****************************************************************************
-*/
+static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
+{
+        return 0;
+}
 
+static int dbri_get_input_port(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-static void dummy()
+static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
 {
+        return 0;
 }
 
-static int dbri_open(struct inode * inode, struct file * file,
-                     struct sparcaudio_driver *drv)
+static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
 {
-	struct dbri *dbri = (struct dbri *)drv->private;
+        return 0;
+}
 
-	MOD_INC_USE_COUNT;
+static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
+{
+        return 0;
+}
 
-	return 0;
+static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
+{
+        return 0;
 }
 
-static void dbri_release(struct inode * inode, struct file * file,
-                         struct sparcaudio_driver *drv)
+static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
 {
-	MOD_DEC_USE_COUNT;
+        return 0;
 }
 
-static void dbri_start_output(struct sparcaudio_driver *drv,
-                              __u8 * buffer, unsigned long count)
+static int dbri_get_output_rate(struct sparcaudio_driver *drv)
 {
-	struct dbri *dbri = (struct dbri *)drv->private;
-        int val, n;
+        return 0;
+}
 
-        if (count > (1 << 14) - 1) {
-                printk("dbri_start_output called with count=%d; truncated", count);
-                count = (1 << 14) - 1;
-        }
+static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
+{
+        return 0;
+}
 
-        n = dbri_cmdlock(dbri);
+static int dbri_get_input_rate(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-        dbri->mm.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_D | DBRI_TD_CNT(count);
-        dbri->mm.td.ba = (__u32) buffer;
-        dbri->mm.td.nda = 0;
-        dbri->mm.td.status = 0;
+static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-        /* Pipe 4 is audio transmit */
-	val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4);
-	dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-	dbri->cmd[n++] = (__u32)&dbri->mm.td;
+static int dbri_get_output_ports(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-        output_callback_arg = drv;
+static int dbri_get_input_ports(struct sparcaudio_driver *drv)
+{
+        return 0;
+}
 
-        dbri_cmdsend(dbri, n);
+static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+{
+        return 0;
 }
 
-static void dbri_stop_output(struct sparcaudio_driver *drv)
+static int dbri_get_output_muted(struct sparcaudio_driver *drv)
 {
-	struct dbri *dbri = (struct dbri *)drv->private;
+        return 0;
 }
 
 
@@ -888,60 +1287,89 @@
 static struct sparcaudio_operations dbri_ops = {
 	dbri_open,
 	dbri_release,
-	dummy, /* dbri_ioctl, */
+	dbri_ioctl,
 	dbri_start_output,
 	dbri_stop_output,
-	dummy, /* dbri_start_input, */
-        dummy, /* dbri_stop_input, */
-	dummy, /* dbri_audio_getdev, */
-	dummy, /* dbri_set_output_volume, */
-	dummy, /* dbri_get_output_volume, */
-	dummy, /* dbri_set_input_volume, */
-	dummy, /* dbri_get_input_volume, */
-	dummy, /* dbri_set_monitor_volume, */
-	dummy, /* dbri_get_monitor_volume, */
-	dummy, /* dbri_set_output_balance */
-	dummy, /* dbri_get_output_balance, */
-	dummy, /* dbri_set_input_balance */
-	dummy, /* dbri_get_input_balance, */
-	dummy, /* dbri_set_output_channels */
-	dummy, /* dbri_get_output_channels, */
-	dummy, /* dbri_set_input_channels */
-	dummy, /* dbri_get_input_channels, */
-	dummy, /* dbri_set_output_precision */
-	dummy, /* dbri_get_output_precision, */
-	dummy, /* dbri_set_input_precision */
-	dummy, /* dbri_get_input_precision, */
-	dummy, /* dbri_set_output_port */
-	dummy, /* dbri_get_output_port, */
-	dummy, /* dbri_set_input_port */
-	dummy, /* dbri_get_input_port, */
-	dummy, /* dbri_set_output_encoding */
-	dummy, /* dbri_get_output_encoding, */
-	dummy, /* dbri_set_input_encoding */
-	dummy, /* dbri_get_input_encoding, */
-	dummy, /* dbri_set_output_rate */
-	dummy, /* dbri_get_output_rate, */
-	dummy, /* dbri_set_input_rate */
-	dummy, /* dbri_get_input_rate, */
-	dummy, /* dbri_sunaudio_getdev_sunos, */
-	dummy, /* dbri_get_output_ports, */
-	dummy, /* dbri_get_input_ports, */
-	dummy, /* dbri_set_output_muted */
-	dummy, /* dbri_get_output_muted, */
+	dbri_start_input,
+        dbri_stop_input,
+	dbri_audio_getdev,
+	dbri_set_output_volume,
+	dbri_get_output_volume,
+	dbri_set_input_volume,
+	dbri_get_input_volume,
+	dbri_set_monitor_volume,
+	dbri_get_monitor_volume,
+	dbri_set_output_balance,
+	dbri_get_output_balance,
+	dbri_set_input_balance,
+	dbri_get_input_balance,
+	dbri_set_output_channels,
+	dbri_get_output_channels,
+	dbri_set_input_channels,
+	dbri_get_input_channels,
+	dbri_set_output_precision,
+	dbri_get_output_precision,
+	dbri_set_input_precision,
+	dbri_get_input_precision,
+	dbri_set_output_port,
+	dbri_get_output_port,
+	dbri_set_input_port,
+	dbri_get_input_port,
+	dbri_set_output_encoding,
+	dbri_get_output_encoding,
+	dbri_set_input_encoding,
+	dbri_get_input_encoding,
+	dbri_set_output_rate,
+	dbri_get_output_rate,
+	dbri_set_input_rate,
+	dbri_get_input_rate,
+	dbri_sunaudio_getdev_sunos,
+	dbri_get_output_ports,
+	dbri_get_input_ports,
+	dbri_set_output_muted,
+	dbri_get_output_muted,
 };
 
+
 /*
 ****************************************************************************
 ************************** ISDN (Hisax) Interface **************************
 ****************************************************************************
 */
 
+
+void dbri_isdn_init(struct dbri *dbri)
+{
+        /* Pipe  0: Receive D channel
+         * Pipe  8: Receive B1 channel
+         * Pipe  9: Receive B2 channel
+         * Pipe  1: Transmit D channel
+         * Pipe 10: Transmit B1 channel
+         * Pipe 11: Transmit B2 channel
+         */
+
+        setup_pipe(dbri, 0, D_SDP_HDLC | D_SDP_FROM_SER | D_SDP_LSB);
+        setup_pipe(dbri, 8, D_SDP_HDLC | D_SDP_FROM_SER | D_SDP_LSB);
+        setup_pipe(dbri, 9, D_SDP_HDLC | D_SDP_FROM_SER | D_SDP_LSB);
+
+        setup_pipe(dbri, 1, D_SDP_HDLC_D | D_SDP_TO_SER | D_SDP_LSB);
+        setup_pipe(dbri,10, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB);
+        setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB);
+
+        link_time_slot(dbri, 0, PIPEinput, 0, 2, 17);
+        link_time_slot(dbri, 8, PIPEinput, 8, 8, 0);
+        link_time_slot(dbri, 9, PIPEinput, 9, 8, 8);
+
+        link_time_slot(dbri,  1, PIPEoutput,  1, 2, 17);
+        link_time_slot(dbri, 10, PIPEoutput,  1, 8, 0);
+        link_time_slot(dbri, 11, PIPEoutput, 10, 8, 8);
+}
+
 int dbri_get_irqnum(int dev)
 {
        struct dbri *dbri;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return(0);
        }
 
@@ -955,7 +1383,7 @@
 {
        struct dbri *dbri;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return(0);
        }
 
@@ -964,11 +1392,13 @@
        return dbri->liu_state;
 }
 
+void dbri_liu_activate(int dev, int priority);
+
 void dbri_liu_init(int dev, void (*callback)(void *), void *callback_arg)
 {
        struct dbri *dbri;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return;
        }
 
@@ -979,26 +1409,28 @@
        dbri->liu_callback_arg = callback_arg;
 
         dbri_isdn_init(dbri);
+        dbri_liu_activate(dev, 0);
 }
 
 void dbri_liu_activate(int dev, int priority)
 {
        struct dbri *dbri;
-        int n, val;
+       int val;
+       volatile int *cmd;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        n = dbri_cmdlock(dbri);
+        cmd = dbri_cmdlock(dbri);
 
         /* Turn on the ISDN TE interface and request activation */
         val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
-        dbri->cmd[n++] = DBRI_CMD(D_TE, 0, val);
+        *(cmd++) = DBRI_CMD(D_TE, 0, val);
 
-       dbri_cmdsend(dbri, n);
+       dbri_cmdsend(dbri, cmd);
 
         /* Activate the interface */
         dbri->regs->reg0 |= D_T;
@@ -1008,7 +1440,7 @@
 {
        struct dbri *dbri;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return;
        }
 
@@ -1022,37 +1454,15 @@
                        void (*callback)(void *, int), void *callback_arg)
 {
        struct dbri *dbri;
-        int n, val;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        if (count > (1 << 14) - 1) {
-                printk("dbri_dxmit called with count=%d; truncated", count);
-                count = (1 << 14) - 1;
-        }
-
-        n = dbri_cmdlock(dbri);
-
-        /* XXX - Shouldn't I check to make sure D.td isn't is use? */
-
-        dbri->D.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count) | DBRI_TD_I;
-        dbri->D.td.ba = (__u32) buffer;
-        dbri->D.td.nda = 0;
-        dbri->D.td.status = 0;
-
-        /* Pipe 1 is D channel transmit */
-       val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = (__u32)&dbri->D.td;
-
-        dbri->D.output_callback = callback;
-        dbri->D.output_callback_arg = callback_arg;
-
-       dbri_cmdsend(dbri, n);
+       /* Pipe 1 is D channel transmit */
+       xmit_on_pipe(dbri, 1, buffer, count, callback, callback_arg);
 }
 
 void dbri_drecv(int dev, __u8 *buffer, unsigned int size,
@@ -1060,106 +1470,63 @@
                        void *callback_arg)
 {
        struct dbri *dbri;
-        int n, val;
 
-       if (dev > num_drivers) {
+       if (dev >= num_drivers) {
                return;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        if (size > (1 << 14) - 1) {
-                printk("dbri_drecv called with size=%d; truncated", size);
-                size = (1 << 14) - 1;
-        }
-
-        /* Make sure size is a multiple of four */
-        size &= ~3;
-
-        n = dbri_cmdlock(dbri);
-
-        /* XXX - Shouldn't I check to make sure D.rd isn't is use? */
-
-        dbri->D.rd.flags = 0;
-        dbri->D.rd.ba = (__u32) buffer;
-        dbri->D.rd.nda = 0;
-        dbri->D.rd.status = DBRI_RD_B | DBRI_RD_BCNT(size);
-
-        /* Pipe 0 is D channel receive */
-       val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = (__u32)&dbri->D.rd;
-
-       dbri_cmdsend(dbri, n);
-
-        dbri->D.input_callback = callback;
-        dbri->D.input_callback_arg = callback_arg;
+       /* Pipe 0 is D channel receive */
+       recv_on_pipe(dbri, 0, buffer, size, callback, callback_arg);
 }
 
 int dbri_bopen(int dev, unsigned int chan,
                int hdlcmode, u_char xmit_idle_char)
 {
        struct dbri *dbri;
-        int n, val;
+       int val;
 
-       if (dev > num_drivers || chan > 1) {
+       if (dev >= num_drivers || chan > 1) {
                return -1;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        if (hdlcmode) {
-
-                return -1;
-
-                /* Pipe 8/9: receive B1/B2 channel */
-                dbri->B[chan].recvSDP = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan);
-
-                /* Pipe 10/11: transmit B1/B2 channel */
-                dbri->B[chan].xmitSDP = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan);
+       if (hdlcmode) {
 
-        } else {        /* !hdlcmode means transparent */
-
-                /* Pipe 8/9: receive B1/B2 channel */
-                dbri->B[chan].recvSDP = D_SDP_MEM|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan);
-
-                /* Pipe 10/11: transmit B1/B2 channel */
-                dbri->B[chan].xmitSDP = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan);
-
-        }
+               /* return -1; */
 
-        n = dbri_cmdlock(dbri);
+               /* Pipe 8/9: receive B1/B2 channel */
+               setup_pipe(dbri, 8+chan, D_SDP_HDLC | D_SDP_FROM_SER|D_SDP_LSB);
 
-        /* Pipe 8/9: receive B1/B2 channel */
-        val = dbri->B[chan].recvSDP | D_SDP_C;
-        dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-        dbri->cmd[n++] = 0;
+               /* Pipe 10/11: transmit B1/B2 channel */
+               setup_pipe(dbri,10+chan, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB);
 
-        /* Pipe 10/11: transmit B1/B2 channel */
-        val = dbri->B[chan].xmitSDP | D_SDP_C;
-        dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-        dbri->cmd[n++] = 0;
+       } else {        /* !hdlcmode means transparent */
 
-        dbri->B[chan].output_callback = NULL;
-        dbri->B[chan].input_callback = NULL;
+               /* Pipe 8/9: receive B1/B2 channel */
+               setup_pipe(dbri, 8+chan, D_SDP_MEM | D_SDP_FROM_SER|D_SDP_LSB);
 
-       dbri_cmdsend(dbri, n);
+               /* Pipe 10/11: transmit B1/B2 channel */
+               setup_pipe(dbri,10+chan, D_SDP_MEM | D_SDP_TO_SER | D_SDP_LSB);
 
-        return 0;
+       }
+       return 0;
 }
 
 void dbri_bclose(int dev, unsigned int chan)
 {
        struct dbri *dbri;
 
-       if (dev > num_drivers || chan > 1) {
+       if (dev >= num_drivers || chan > 1) {
                return;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        dbri->B[chan].output_callback = NULL;
-        dbri->B[chan].input_callback = NULL;
+       reset_pipe(dbri, 8+chan);
+       reset_pipe(dbri, 10+chan);
 }
 
 void dbri_bxmit(int dev, unsigned int chan,
@@ -1168,37 +1535,15 @@
                        void *callback_arg)
 {
        struct dbri *dbri;
-        int n, val;
 
-       if (dev > num_drivers || chan > 1) {
+       if (dev >= num_drivers || chan > 1) {
                return;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        if (count > (1 << 14) - 1) {
-                printk("dbri_bxmit called with count=%ld; truncated", count);
-                count = (1 << 14) - 1;
-        }
-
-        n = dbri_cmdlock(dbri);
-
-        /* XXX - Shouldn't I check to make sure td isn't is use? */
-
-        dbri->B[chan].td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count);
-        dbri->B[chan].td.ba = (__u32) buffer;
-        dbri->B[chan].td.nda = 0;
-        dbri->B[chan].td.status = 0;
-
-        /* Pipe 10/11 is B1/B2 channel transmit */
-       val = dbri->B[chan].xmitSDP;
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = (__u32)&dbri->B[chan].td;
-
-        dbri->B[chan].output_callback = callback;
-        dbri->B[chan].output_callback_arg = callback_arg;
-
-       dbri_cmdsend(dbri, n);
+       /* Pipe 10/11 is B1/B2 channel transmit */
+       xmit_on_pipe(dbri, 10+chan, buffer, count, callback, callback_arg);
 }
 
 void dbri_brecv(int dev, unsigned int chan,
@@ -1207,40 +1552,15 @@
                        void *callback_arg)
 {
        struct dbri *dbri;
-        int n, val;
 
-       if (dev > num_drivers || chan > 1) {
+       if (dev >= num_drivers || chan > 1) {
                return;
        }
 
        dbri = (struct dbri *) drivers[dev].private;
 
-        if (size > (1 << 14) - 1) {
-                printk("dbri_brecv called with size=%ld; truncated", size);
-                size = (1 << 14) - 1;
-        }
-
-        /* Make sure size is a multiple of four */
-        size &= ~3;
-
-        n = dbri_cmdlock(dbri);
-
-        /* XXX - Shouldn't I check to make sure RD isn't is use? */
-
-        dbri->B[chan].rd.flags = 0;
-        dbri->B[chan].rd.ba = (__u32) buffer;
-        dbri->B[chan].rd.nda = 0;
-        dbri->B[chan].rd.status = DBRI_RD_B | DBRI_RD_BCNT(size);
-
-        /* Pipe 8/9 is B1/B2 channel receive */
-       val = dbri->B[chan].recvSDP;
-       dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
-       dbri->cmd[n++] = (__u32)&dbri->B[chan].rd;
-
-       dbri_cmdsend(dbri, n);
-
-        dbri->B[chan].input_callback = callback;
-        dbri->B[chan].input_callback_arg = callback_arg;
+       /* Pipe 8/9 is B1/B2 channel receive */
+       recv_on_pipe(dbri, 8+chan, buffer, size, callback, callback_arg);
 }
 
 #if defined(DBRI_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
@@ -1271,6 +1591,7 @@
 {
 	struct dbri *dbri;
 	struct linux_prom_irqs irq;
+        __u32 dma_dvma;
 	int err;
 
 	if (sdev->prom_name[9] < 'e') {
@@ -1287,7 +1608,13 @@
 
         memset(dbri, 0, sizeof(*dbri));
 
+        /* sparc_dvma_malloc() will halt the kernel if the malloc fails */
+        dbri->dma = sparc_dvma_malloc (sizeof (struct dbri_dma),
+                                       "DBRI DMA Cmd Block", &dma_dvma);
+        dbri->dma_dvma = (struct dbri_dma *) dma_dvma;
+
 	dbri->dbri_version = sdev->prom_name[9];
+        dbri->sdev = sdev;
 
 	/* Map the registers into memory. */
 	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], 
@@ -1295,7 +1622,7 @@
 	dbri->regs_size = sdev->reg_addrs[0].reg_size;
 	dbri->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
 		sdev->reg_addrs[0].reg_size, 
-		drv_name, sdev->reg_addrs[0].which_io, 0);
+		"DBRI Registers", sdev->reg_addrs[0].which_io, 0);
 	if (!dbri->regs) {
 		printk(KERN_ERR "DBRI: could not allocate registers\n");
 		kfree(drv->private);
@@ -1305,7 +1632,8 @@
 	prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq));
 	dbri->irq = irq.pri;
 
-	err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ, "DBRI/audio", drv);
+	err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ,
+                          "DBRI audio/ISDN", dbri);
 	if (err) {
 		printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
 		sparc_free_io(dbri->regs, dbri->regs_size);
@@ -1313,23 +1641,20 @@
 		return err;
 	}
 
+	dbri_initialize(dbri);
+	err = mmcodec_init(drv);
+	if(err) {
+		dbri_detach(dbri);
+		return err;
+	}
+	  
 	/* Register ourselves with the midlevel audio driver. */
 	err = register_sparcaudio_driver(drv,1);
 	if (err) {
 		printk(KERN_ERR "DBRI: unable to register audio\n");
-		free_irq(dbri->irq, drv);
-		sparc_free_io(dbri->regs, dbri->regs_size);
-		kfree(drv->private);
-		return err;
-	}
-
-	dbri_initialize(drv);
-	err = mmcodec_init(drv);
-	if(err) {
-		dbri_detach(drv);
+                dbri_detach(dbri);
 		return err;
 	}
-	  
 
 	dbri->perchip_info.play.active   = dbri->perchip_info.play.pause = 0;
 	dbri->perchip_info.record.active = dbri->perchip_info.record.pause = 0;
@@ -1380,7 +1705,8 @@
         register int i;
 
         for (i = 0; i < num_drivers; i++) {
-                dbri_detach(&drivers[i]);
+                dbri_detach((struct dbri *) drivers[i].private);
+                unregister_sparcaudio_driver(& drivers[i], 1);
                 num_drivers--;
         }
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dbri.h linux/drivers/sbus/audio/dbri.h
--- v2.2.3/linux/drivers/sbus/audio/dbri.h	Fri Jan  8 22:36:09 1999
+++ linux/drivers/sbus/audio/dbri.h	Mon Mar 15 16:11:30 1999
@@ -22,53 +22,77 @@
 #define DBRI_NO_CMDS	64
 #define DBRI_NO_INTS	2
 #define DBRI_INT_BLK	64
+#define DBRI_NO_DESCS	64
 
 #define DBRI_MM_ONB	1
 #define DBRI_MM_SB	2
 
 struct dbri_mem {
-	__u32	flags;
+	__u32	word1;
 	__u32	ba;			/* Transmit/Receive Buffer Address */
 	__u32	nda;			/* Next Descriptor Address */
-	__u32	status;
+	__u32	word4;
 };
 
-struct dbri_channel {
-        struct dbri_mem td;
-        struct dbri_mem rd;
-        unsigned int recvSDP;
-        unsigned int xmitSDP;
+#include "cs4215.h"
+
+/* This structure is in a DMA region where it can accessed by both
+ * the CPU and the DBRI
+ */
+
+struct dbri_dma {
+  int cmd[DBRI_NO_CMDS];			/* Place for commands */
+  int intr[DBRI_NO_INTS * DBRI_INT_BLK];	/* Interrupt field */
+  struct dbri_mem desc[DBRI_NO_DESCS];		/* Xmit/receive descriptors */
+};
+
+struct dbri_pipe {
+        u32 sdp;				/* SDP command word */
+        int nextpipe;				/* Next pipe in linked list */
+        int cycle;				/* Offset of timeslot (bits) */
+        int length;				/* Length of timeslot (bits) */
+        int desc;				/* Index of active descriptor*/
+        __u32 *recv_fixed_ptr;			/* Ptr to receive fixed data */
+};
+
+struct dbri_desc {
+        int inuse;				/* Boolean flag */
+        int next;				/* Index of next desc, or -1 */
+        void *buffer;
+        unsigned int len;
         void (*output_callback)(void *, int);
         void *output_callback_arg;
         void (*input_callback)(void *, int, unsigned int);
         void *input_callback_arg;
 };
 
-#include "cs4215.h"
-
 /* This structure holds the information for both chips (DBRI & CS4215) */
+
 struct dbri {
   int regs_size, irq;				/* Needed for unload */
+  struct linux_sbus_device *sdev;
+
+  volatile struct dbri_dma *dma;		/* Pointer to our DMA block */
+  struct dbri_dma *dma_dvma;			/* DBRI visible DMA address */
 
   struct dbri_regs *regs;			/* dbri HW regs */
   int dbri_version;				/* 'e' and up is OK */
   int dbri_irqp;				/* intr queue pointer */
-  __volatile__ int cmd[DBRI_NO_CMDS];		/* Place for commands */
-  __volatile__ int intr[DBRI_NO_INTS * DBRI_INT_BLK];	/* Interrupt field */
-  
+
+  struct dbri_pipe pipes[32];			/* DBRI's 32 data pipes */
+  struct dbri_desc descs[DBRI_NO_DESCS];
+
   struct cs4215 mm;				/* mmcodec special info */
- 
+
+#if 0
   struct wait_queue *wait, *int_wait;		/* Where to sleep if busy */
+#endif
   struct audio_info perchip_info;
 
   /* Track ISDN LIU and notify changes */
   int liu_state;
   void (*liu_callback)(void *);
   void *liu_callback_arg;
-
-  /* Callback routines and descriptors for ISDN channels */
-  struct dbri_channel D;
-  struct dbri_channel B[2];
 };
 
 
@@ -145,6 +169,7 @@
 #define D_SDP_HDLC_D	(3<<13)	/* D Channel (prio control)*/
 #define D_SDP_SER	(4<<13)	/* Serial to serial */
 #define D_SDP_FIXED	(6<<13)	/* Short only */
+#define D_SDP_MODE(v)	((v)&(7<<13))
 
 #define D_SDP_TO_SER	(1<<12)	/* Direction */
 #define D_SDP_FROM_SER	(0<<12)	/* Direction */
@@ -163,8 +188,8 @@
 #define D_DTS_PRVOUT(v)        ((v)<<5)  /* Previous Out Pipe */
 
 /* Time Slot defines */
-#define D_TS_LEN(v)	(v<<24)	/* Number of bits in this time slot */
-#define D_TS_CYCLE(v)	(v<<14)	/* Bit Count at start of TS */
+#define D_TS_LEN(v)	((v)<<24)	/* Number of bits in this time slot */
+#define D_TS_CYCLE(v)	((v)<<14)	/* Bit Count at start of TS */
 #define D_TS_DI(v)	(1<<13)	/* Data Invert */
 #define D_TS_1CHANNEL	(0<<10)	/* Single Channel / Normal mode */
 #define D_TS_MONITOR	(2<<10)	/* Monitor pipe */
@@ -174,13 +199,13 @@
 #define D_TS_NEXT(v)   ((v)<<0)        /* Pipe Nr: 0-15 long, 16-21 short */
 
 /* Concentration Highway Interface Modes */
-#define D_CHI_CHICM(v)	(v<<16)	/* Clock mode */
+#define D_CHI_CHICM(v)	((v)<<16)	/* Clock mode */
 #define D_CHI_IR	(1<<15) /* Immediate Interrupt Report */
 #define D_CHI_EN	(1<<14) /* CHIL Interrupt enabled */
 #define D_CHI_OD	(1<<13) /* Open Drain Enable */
 #define D_CHI_FE	(1<<12) /* Sample CHIFS on Rising Frame Edge */
 #define D_CHI_FD	(1<<11) /* Frame Drive */
-#define D_CHI_BPF(v)	(v<<0)	/* Bits per Frame */
+#define D_CHI_BPF(v)	((v)<<0)	/* Bits per Frame */
 
 /* NT: These are here for completeness */
 #define D_NT_FBIT	(1<<17)	/* Frame Bit */
@@ -199,13 +224,13 @@
 #define D_NT_ABV	(1<<0)	/* Activate Bipolar Violation */
 
 /* Codec Setup */
-#define D_CDEC_CK(v)	(v<<24)	/* Clock Select */
-#define D_CDEC_FED(v)	(v<<12)	/* FSCOD Falling Edge Delay */
-#define D_CDEC_RED(v)	(v<<0)	/* FSCOD Rising Edge Delay */
+#define D_CDEC_CK(v)	((v)<<24)	/* Clock Select */
+#define D_CDEC_FED(v)	((v)<<12)	/* FSCOD Falling Edge Delay */
+#define D_CDEC_RED(v)	((v)<<0)	/* FSCOD Rising Edge Delay */
 
 /* Test */
-#define D_TEST_RAM(v)	(v<<16)	/* RAM Pointer */
-#define D_TEST_SIZE(v)	(v<<11)	/* */
+#define D_TEST_RAM(v)	((v)<<16)	/* RAM Pointer */
+#define D_TEST_SIZE(v)	((v)<<11)	/* */
 #define D_TEST_ROMONOFF	0x5	/* Toggle ROM opcode monitor on/off */
 #define D_TEST_PROC	0x6	/* MicroProcessor test */
 #define D_TEST_SER	0x7	/* Serial-Controller test */
@@ -245,11 +270,11 @@
 #define D_INTR_CHI	36
 #define D_INTR_CMD	38
 
-#define D_INTR_GETCHAN(v)	((v>>24) & 0x3f)
-#define D_INTR_GETCODE(v)	((v>>20) & 0xf)
-#define D_INTR_GETCMD(v)	((v>>16) & 0xf)
-#define D_INTR_GETVAL(v)	(v & 0xffff)
-#define D_INTR_GETRVAL(v)	(v & 0xfffff)
+#define D_INTR_GETCHAN(v)	(((v)>>24) & 0x3f)
+#define D_INTR_GETCODE(v)	(((v)>>20) & 0xf)
+#define D_INTR_GETCMD(v)	(((v)>>16) & 0xf)
+#define D_INTR_GETVAL(v)	((v) & 0xffff)
+#define D_INTR_GETRVAL(v)	((v) & 0xfffff)
 
 #define D_P_0		0	/* TE receive anchor */
 #define D_P_1		1	/* TE transmit anchor */
@@ -288,11 +313,11 @@
 /* Transmit descriptor defines */
 #define DBRI_TD_F	(1<<31)	/* End of Frame */
 #define DBRI_TD_D	(1<<30)	/* Do not append CRC */
-#define DBRI_TD_CNT(v)	(v<<16)	/* Number of valid bytes in the buffer */
+#define DBRI_TD_CNT(v)	((v)<<16)	/* Number of valid bytes in the buffer */
 #define DBRI_TD_B	(1<<15)	/* Final interrupt */
 #define DBRI_TD_M	(1<<14)	/* Marker interrupt */
 #define DBRI_TD_I	(1<<13)	/* Transmit Idle Characters */
-#define DBRI_TD_FCNT(v)	v	/* Flag Count */
+#define DBRI_TD_FCNT(v)	(v)	/* Flag Count */
 #define DBRI_TD_UNR	(1<<3)	/* Underrun: transmitter is out of data */
 #define DBRI_TD_ABT	(1<<2)	/* Abort: frame aborted */
 #define DBRI_TD_TBC	(1<<0)	/* Transmit buffer Complete */
@@ -303,12 +328,12 @@
 #define DBRI_RD_C	(1<<30)	/* Completed buffer */
 #define DBRI_RD_B	(1<<15)	/* Final interrupt */
 #define DBRI_RD_M	(1<<14)	/* Marker interrupt */
-#define DBRI_RD_BCNT(v)	v	/* Buffer size */
+#define DBRI_RD_BCNT(v)	(v)	/* Buffer size */
 #define DBRI_RD_CRC	(1<<7)	/* 0: CRC is correct */
 #define DBRI_RD_BBC	(1<<6)	/* 1: Bad Byte received */
 #define DBRI_RD_ABT	(1<<5)	/* Abort: frame aborted */
 #define DBRI_RD_OVRN	(1<<3)	/* Overrun: data lost */
 #define DBRI_RD_STATUS(v)      ((v)&0xff)      /* Receive status */
-#define DBRI_RD_CNT(v) ((v>>16)&0x1fff)        /* Number of valid bytes in the buffer */
+#define DBRI_RD_CNT(v) (((v)>>16)&0x1fff)        /* Number of valid bytes in the buffer */
 
 #endif /* _DBRI_H_ */
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dmy.c linux/drivers/sbus/audio/dmy.c
--- v2.2.3/linux/drivers/sbus/audio/dmy.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/audio/dmy.c	Mon Mar 15 16:11:30 1999
@@ -0,0 +1,737 @@
+/*
+ * drivers/sbus/audio/dummy.c
+ *
+ * Copyright 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
+ *
+ * This is a dummy lowlevel driver. Consider it a distant cousin of 
+ * /proc/audio; It pretends to be a piece of audio hardware, and writes
+ * to a file instead. (or will shortly)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/soundcard.h>
+#include <linux/delay.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/sbus.h>
+
+#include <asm/audioio.h>
+#include "dummy.h"
+
+#define MAX_DRIVERS 1
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static int num_drivers;
+
+static int dummy_play_gain(struct sparcaudio_driver *drv, int value, 
+                            unsigned char balance);
+static int dummy_record_gain(struct sparcaudio_driver *drv, int value, 
+                            unsigned char balance);
+static int dummy_output_muted(struct sparcaudio_driver *drv, int value);
+static int dummy_attach(struct sparcaudio_driver *drv);
+
+static int
+dummy_set_output_encoding(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  if (value != 0) {
+    dummy_chip->perchip_info.play.encoding = value;
+    return 0;
+  }
+  return -EINVAL;
+}
+
+static int
+dummy_set_input_encoding(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  if (value != 0) {
+    dummy_chip->perchip_info.record.encoding = value;
+    return 0;
+  }
+  return -EINVAL;
+}
+
+static int dummy_get_output_encoding(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.encoding;
+}
+
+static int dummy_get_input_encoding(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.encoding;
+}
+
+static int
+dummy_set_output_rate(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  if (value != 0) {
+    dummy_chip->perchip_info.play.sample_rate = value;
+    return 0;
+  }
+  return -EINVAL;
+}
+
+static int
+dummy_set_input_rate(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  if (value != 0) {
+    dummy_chip->perchip_info.record.sample_rate = value;
+    return 0;
+  }
+  return -EINVAL;
+}
+
+static int dummy_get_output_rate(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.sample_rate;
+}
+
+static int dummy_get_input_rate(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.sample_rate;
+}
+
+/* Generically we support 4 channels. This does 2 */
+static int
+dummy_set_output_channels(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  switch (value) {
+  case 1:
+  case 2:
+    break;
+  default:
+    return -(EINVAL);
+  }  
+  dummy_chip->perchip_info.play.channels = value;
+  return 0;
+}
+
+/* Generically we support 4 channels. This does 2 */
+static int
+dummy_set_input_channels(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  switch (value) {
+  case 1:
+  case 2:
+    break;
+  default:
+    return -(EINVAL);
+  }  
+  dummy_chip->perchip_info.record.channels = value;
+  return 0;
+}
+
+static int dummy_get_input_channels(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.channels;
+}
+
+static int dummy_get_output_channels(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.channels;
+}
+
+static int dummy_get_output_precision(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.precision;
+}
+
+static int dummy_get_input_precision(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.precision;
+}
+
+static int dummy_set_output_precision(struct sparcaudio_driver *drv, int val)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; 
+  dummy_chip->perchip_info.play.precision = val;
+  return dummy_chip->perchip_info.play.precision;
+}
+
+static int dummy_set_input_precision(struct sparcaudio_driver *drv, int val)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private; 
+  dummy_chip->perchip_info.record.precision = val;
+  return dummy_chip->perchip_info.record.precision;
+}
+
+/* Set output mute */
+static int dummy_output_muted(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  if (!value) 
+    dummy_chip->perchip_info.output_muted = 0;
+  else
+    dummy_chip->perchip_info.output_muted = 1;
+  return 0;
+}
+
+static int dummy_get_output_muted(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.output_muted;
+}
+
+static int dummy_get_formats(struct sparcaudio_driver *drv)
+{
+  return (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_IMA_ADPCM | 
+          AFMT_S16_LE | AFMT_S16_BE);
+}
+
+static int dummy_get_output_ports(struct sparcaudio_driver *drv)
+{
+  return (AUDIO_LINE_OUT | AUDIO_SPEAKER | AUDIO_HEADPHONE);
+}
+
+static int dummy_get_input_ports(struct sparcaudio_driver *drv)
+{
+  return (AUDIO_ANALOG_LOOPBACK);
+}
+
+/* Set chip "output" port */
+static int dummy_set_output_port(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.play.port = value;
+  return (value);
+}
+
+static int dummy_set_input_port(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.record.port = value;
+  return (value);
+}
+
+static int dummy_get_output_port(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.port;
+}
+
+static int dummy_get_input_port(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.port;
+}
+
+static int dummy_get_output_error(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return (int)dummy_chip->perchip_info.play.error;
+}
+
+static int dummy_get_input_error(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return (int)dummy_chip->perchip_info.record.error;
+}
+
+static int dummy_get_output_samples(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.samples;
+}
+
+static int dummy_get_output_pause(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return (int)dummy_chip->perchip_info.play.pause;
+}
+
+static int dummy_set_output_volume(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_play_gain(drv, value, dummy_chip->perchip_info.play.balance);
+  return 0;
+}
+
+static int dummy_get_output_volume(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.play.gain;
+}
+
+static int dummy_set_output_balance(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.play.balance = value;
+  dummy_play_gain(drv, dummy_chip->perchip_info.play.gain, 
+                  dummy_chip->perchip_info.play.balance);
+  return 0;
+}
+
+static int dummy_get_output_balance(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return (int)dummy_chip->perchip_info.play.balance;
+}
+
+/* Set chip play gain */
+static int dummy_play_gain(struct sparcaudio_driver *drv, int value, unsigned char balance)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  int tmp = 0, r, l, r_adj, l_adj;
+  r = l = value;
+  if (balance < AUDIO_MID_BALANCE) {
+    r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
+    if (r < 0) r = 0;
+  } else if (balance > AUDIO_MID_BALANCE) {
+    l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT));
+    if (l < 0) l = 0;
+  }
+  (l == 0) ? (l_adj = DUMMY_MAX_DEV_ATEN) : (l_adj = DUMMY_MAX_ATEN - 
+                                             (l * (DUMMY_MAX_ATEN + 1) / 
+                                              (AUDIO_MAX_GAIN + 1)));
+  (r == 0) ? (r_adj = DUMMY_MAX_DEV_ATEN) : (r_adj = DUMMY_MAX_ATEN -
+                                             (r * (DUMMY_MAX_ATEN + 1) /
+                                              (AUDIO_MAX_GAIN + 1)));
+  if ((value == 0) || (value == AUDIO_MAX_GAIN)) {
+    tmp = value;
+  } else {
+    if (value == l)
+      tmp = ((DUMMY_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) / 
+             (DUMMY_MAX_ATEN + 1));
+    else if (value == r)
+      tmp = ((DUMMY_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) / 
+             (DUMMY_MAX_ATEN + 1));
+  }
+  dummy_chip->perchip_info.play.gain = tmp;
+  return 0;
+}
+
+static int dummy_get_input_samples(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.samples;
+}
+
+static int dummy_get_input_pause(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return (int)dummy_chip->perchip_info.record.pause;
+}
+
+static int dummy_set_monitor_volume(struct sparcaudio_driver *drv, int value)
+{
+  return 0;
+}
+
+static int dummy_get_monitor_volume(struct sparcaudio_driver *drv)
+{
+  return 0;
+}
+
+static int dummy_set_input_volume(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_record_gain(drv, value, dummy_chip->perchip_info.record.balance);
+  return 0;
+}
+
+static int dummy_get_input_volume(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return dummy_chip->perchip_info.record.gain;
+}
+
+static int dummy_set_input_balance(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.record.balance = value;
+  dummy_record_gain(drv, dummy_chip->perchip_info.record.gain, 
+                  dummy_chip->perchip_info.play.balance);
+  return 0;
+}
+
+static int dummy_get_input_balance(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  return (int)dummy_chip->perchip_info.record.balance;
+}
+
+static int dummy_record_gain(struct sparcaudio_driver *drv, int value, unsigned char balance)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  int tmp = 0, r, l, r_adj, l_adj;
+  r = l = value;
+  if (balance < AUDIO_MID_BALANCE) {
+    r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
+    if (r < 0) r = 0;
+  } else if (balance > AUDIO_MID_BALANCE) {
+    l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT));
+    if (l < 0) l = 0;
+  }
+  (l == 0) ? (l_adj = DUMMY_MAX_DEV_ATEN) : (l_adj = DUMMY_MAX_ATEN - 
+                                             (l * (DUMMY_MAX_ATEN + 1) / 
+                                              (AUDIO_MAX_GAIN + 1)));
+  (r == 0) ? (r_adj = DUMMY_MAX_DEV_ATEN) : (r_adj = DUMMY_MAX_ATEN -
+                                             (r * (DUMMY_MAX_ATEN + 1) /
+                                              (AUDIO_MAX_GAIN + 1)));
+  if ((value == 0) || (value == AUDIO_MAX_GAIN)) {
+    tmp = value;
+  } else {
+    if (value == l)
+      tmp = ((DUMMY_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) / 
+             (DUMMY_MAX_ATEN + 1));
+    else if (value == r)
+      tmp = ((DUMMY_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) / 
+             (DUMMY_MAX_ATEN + 1));
+  }
+  dummy_chip->perchip_info.record.gain = tmp;
+  return 0;
+}
+
+/* Reset the audio chip to a sane state. */
+static void dummy_chip_reset(struct sparcaudio_driver *drv)
+{
+  dummy_set_output_encoding(drv, AUDIO_ENCODING_ULAW);
+  dummy_set_output_rate(drv, DUMMY_RATE);
+  dummy_set_output_channels(drv, DUMMY_CHANNELS);
+  dummy_set_output_precision(drv, DUMMY_PRECISION);
+  dummy_set_output_balance(drv, AUDIO_MID_BALANCE);
+  dummy_set_output_volume(drv, DUMMY_DEFAULT_PLAYGAIN);
+  dummy_set_output_port(drv, AUDIO_SPEAKER);
+  dummy_output_muted(drv, 0);
+  dummy_set_input_encoding(drv, AUDIO_ENCODING_ULAW);
+  dummy_set_input_rate(drv, DUMMY_RATE);
+  dummy_set_input_channels(drv, DUMMY_CHANNELS);
+  dummy_set_input_precision(drv, DUMMY_PRECISION);
+  dummy_set_input_balance(drv, AUDIO_MID_BALANCE);
+  dummy_set_input_volume(drv, DUMMY_DEFAULT_PLAYGAIN);
+  dummy_set_input_port(drv, AUDIO_SPEAKER);
+}
+
+static int dummy_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+{	
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  /* Set the default audio parameters if not already in use. */
+  if (file->f_mode & FMODE_WRITE) {
+    if (!(drv->flags & SDF_OPEN_WRITE) && 
+	(dummy_chip->perchip_info.play.active == 0)) {
+      dummy_chip->perchip_info.play.open = 1;
+      dummy_chip->perchip_info.play.samples =
+        dummy_chip->perchip_info.play.error = 0;
+    }
+  }
+  if (file->f_mode & FMODE_READ) {
+    if (!(drv->flags & SDF_OPEN_READ) && 
+	(dummy_chip->perchip_info.record.active == 0)) {
+      dummy_chip->perchip_info.record.open = 1;
+      dummy_chip->perchip_info.record.samples =
+        dummy_chip->perchip_info.record.error = 0;
+    }
+  }
+
+  MOD_INC_USE_COUNT;
+
+  return 0;
+}
+
+static void dummy_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  if (file->f_mode & FMODE_WRITE) {
+    dummy_chip->perchip_info.play.active =
+      dummy_chip->perchip_info.play.open = 0;
+  }
+  if (file->f_mode & FMODE_READ) {
+    dummy_chip->perchip_info.record.active =
+      dummy_chip->perchip_info.record.open = 0;
+  }
+  MOD_DEC_USE_COUNT;
+}
+
+static void dummy_output_done_task(void * arg)
+{
+  struct sparcaudio_driver *drv = (struct sparcaudio_driver *)arg;
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+
+  sparcaudio_output_done(drv, 1);
+  if (dummy_chip->perchip_info.record.active)
+    sparcaudio_input_done(drv, 1);
+}
+
+static void dummy_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
+                                unsigned long count)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+
+  if (dummy_chip->perchip_info.play.pause || !count) 
+    return;
+
+  dummy_chip->perchip_info.play.active = 1;
+
+  /* fake an "interrupt" to deal with this block */
+  dummy_chip->tqueue.next = NULL;
+  dummy_chip->tqueue.sync = 0;
+  dummy_chip->tqueue.routine = dummy_output_done_task;
+  dummy_chip->tqueue.data = drv;
+
+  queue_task(&dummy_chip->tqueue, &tq_immediate);
+  mark_bh(IMMEDIATE_BH);
+}
+
+static void dummy_start_input(struct sparcaudio_driver *drv, __u8 * buffer,
+                                unsigned long count)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.record.active = 1;
+}
+
+static void dummy_stop_output(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.play.active = 0;
+}
+
+static void dummy_stop_input(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.record.active = 0;
+}
+
+static int dummy_set_output_pause(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.play.pause = value;
+
+  if (!value)
+    sparcaudio_output_done(drv, 0);
+
+  return value;
+}
+
+static int dummy_set_input_pause(struct sparcaudio_driver *drv, int value)
+{
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  dummy_chip->perchip_info.record.pause = value;
+
+  /* This should probably cause play pause. */
+
+  return value;
+}
+
+static int dummy_set_input_error(struct sparcaudio_driver *drv, int value)
+{
+  return 0;
+}
+
+static int dummy_set_output_error(struct sparcaudio_driver *drv, int value)
+{
+  int i;
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  i = dummy_chip->perchip_info.play.error;
+  dummy_chip->perchip_info.play.error = value;
+  return i;
+}
+
+static int dummy_set_output_samples(struct sparcaudio_driver *drv, int value)
+{
+  int i;
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  i = dummy_chip->perchip_info.play.samples;
+  dummy_chip->perchip_info.play.samples = value;
+  return i;
+}
+
+static int dummy_set_input_samples(struct sparcaudio_driver *drv, int value)
+{
+  int i;
+  struct dummy_chip *dummy_chip = (struct dummy_chip *)drv->private;
+  i = dummy_chip->perchip_info.play.samples;
+  dummy_chip->perchip_info.record.samples = value;
+  return i;
+}
+
+/* In order to fake things which care out, play we're a 4231 */
+static void dummy_audio_getdev(struct sparcaudio_driver *drv,
+                                 audio_device_t * audinfo)
+{
+  strncpy(audinfo->name, "SUNW,cs4231", sizeof(audinfo->name) - 1);
+  strncpy(audinfo->version, "a", sizeof(audinfo->version) - 1);
+  strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1);
+}
+
+
+static int dummy_audio_getdev_sunos(struct sparcaudio_driver *drv)
+{
+  return (5); 
+}
+
+static struct sparcaudio_operations dummy_ops = {
+	dummy_open,
+	dummy_release,
+	NULL,
+	dummy_start_output,
+	dummy_stop_output,
+	dummy_start_input,
+	dummy_stop_input,
+	dummy_audio_getdev,
+        dummy_set_output_volume,
+        dummy_get_output_volume,
+        dummy_set_input_volume,
+        dummy_get_input_volume,
+        dummy_set_monitor_volume,
+        dummy_get_monitor_volume,
+	dummy_set_output_balance,
+	dummy_get_output_balance,
+	dummy_set_input_balance,
+	dummy_get_input_balance,
+        dummy_set_output_channels,
+        dummy_get_output_channels,
+        dummy_set_input_channels,
+        dummy_get_input_channels,
+        dummy_set_output_precision,
+        dummy_get_output_precision,
+        dummy_set_input_precision,
+        dummy_get_input_precision,
+        dummy_set_output_port,
+        dummy_get_output_port,
+        dummy_set_input_port,
+        dummy_get_input_port,
+        dummy_set_output_encoding,
+        dummy_get_output_encoding,
+        dummy_set_input_encoding,
+        dummy_get_input_encoding,
+        dummy_set_output_rate,
+        dummy_get_output_rate,
+        dummy_set_input_rate,
+        dummy_get_input_rate,
+	dummy_audio_getdev_sunos,
+	dummy_get_output_ports,
+	dummy_get_input_ports,
+	dummy_output_muted,
+	dummy_get_output_muted,
+	dummy_set_output_pause,
+	dummy_get_output_pause,
+        dummy_set_input_pause,
+	dummy_get_input_pause,
+	dummy_set_output_samples,
+	dummy_get_output_samples,
+        dummy_set_input_samples,
+	dummy_get_input_samples,
+	dummy_set_output_error,
+	dummy_get_output_error,
+        dummy_set_input_error,
+	dummy_get_input_error,
+        dummy_get_formats,
+};
+
+/* Probe for the dummy chip and then attach the driver. */
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int dummy_init(void))
+#endif
+{
+  num_drivers = 0;
+      
+  /* Add support here for specifying multiple dummies to attach at once. */
+  if (dummy_attach(&drivers[num_drivers]) == 0)
+    num_drivers++;
+  
+  /* Only return success if we found some dummy chips. */
+  return (num_drivers > 0) ? 0 : -EIO;
+}
+
+/* Attach to an dummy chip given its PROM node. */
+static int dummy_attach(struct sparcaudio_driver *drv)
+{
+  struct dummy_chip *dummy_chip;
+  int err;
+
+  /* Allocate our private information structure. */
+  drv->private = kmalloc(sizeof(struct dummy_chip), GFP_KERNEL);
+  if (!drv->private)
+    return -ENOMEM;
+
+  /* Point at the information structure and initialize it. */
+  drv->ops = &dummy_ops;
+  dummy_chip = (struct dummy_chip *)drv->private;
+
+  /* Reset parameters. */
+  dummy_chip_reset(drv);
+
+  /* Register ourselves with the midlevel audio driver. */
+  err = register_sparcaudio_driver(drv, 2);
+
+  if (err < 0) {
+    printk(KERN_ERR "dummy: unable to register\n");
+    kfree(drv->private);
+    return -EIO;
+  }
+
+  dummy_chip->perchip_info.play.active = 
+    dummy_chip->perchip_info.play.pause = 0;
+
+  dummy_chip->perchip_info.play.avail_ports = (AUDIO_HEADPHONE |
+						AUDIO_SPEAKER |
+						AUDIO_LINE_OUT);
+
+  /* Announce the hardware to the user. */
+  printk(KERN_INFO "audio%d: dummy at 0x0 irq 0\n", drv->index);
+  
+  /* Success! */
+  return 0;
+}
+
+#ifdef MODULE
+/* Detach from an dummy chip given the device structure. */
+static void dummy_detach(struct sparcaudio_driver *drv)
+{
+  unregister_sparcaudio_driver(drv, 2);
+  kfree(drv->private);
+}
+
+void cleanup_module(void)
+{
+  register int i;
+
+  for (i = 0; i < num_drivers; i++) {
+    dummy_detach(&drivers[i]);
+    num_drivers--;
+  }
+}
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/audio/dummy.h linux/drivers/sbus/audio/dummy.h
--- v2.2.3/linux/drivers/sbus/audio/dummy.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/audio/dummy.h	Mon Mar 15 16:11:30 1999
@@ -0,0 +1,41 @@
+/*
+ * drivers/sbus/audio/dummy.h
+ *
+ * Copyright (C) 1998 Derrick J. Brashear (shadow@dementia.org)
+ */
+
+#ifndef _DUMMY_H_
+#define _DUMMY_H_
+
+#include <linux/types.h>
+#include <linux/tqueue.h>
+
+#define DUMMY_OUTFILE "/usr/tmp/dummy.au"
+
+/* Our structure for each chip */
+
+struct dummy_chip {
+  struct audio_info perchip_info;
+  unsigned int playlen;
+  struct tq_struct tqueue;
+};
+
+#define DUMMY_MIN_ATEN     (0)
+#define DUMMY_MAX_ATEN     (31)
+#define DUMMY_MAX_DEV_ATEN (63)
+
+#define DUMMY_MON_MIN_ATEN         (0)
+#define DUMMY_MON_MAX_ATEN         (63)
+
+#define DUMMY_DEFAULT_PLAYGAIN     (132)
+#define DUMMY_DEFAULT_RECGAIN      (126)
+
+#define DUMMY_MIN_GAIN     (0)
+#define DUMMY_MAX_GAIN     (15)
+
+#define DUMMY_PRECISION    (8)             /* # of bits/sample */
+#define DUMMY_CHANNELS     (1)             /* channels/sample */
+
+#define DUMMY_RATE   (8000)                /* default sample rate */
+
+#endif
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c
--- v2.2.3/linux/drivers/sbus/char/bpp.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sbus/char/bpp.c	Mon Mar 15 16:11:30 1999
@@ -337,7 +337,7 @@
        * responds real good. The first while loop guesses an expire
        * time accounting for possible wraparound of jiffies.
        */
-      while (time_after_eq(jiffies, extime) extime = jiffies + 1;
+      while (time_after_eq(jiffies, extime)) extime = jiffies + 1;
       while ( (time_before(jiffies, extime))
               && (((pins & set) != set) || ((pins & clr) != 0)) ) {
             pins = get_pins(minor);
@@ -470,13 +470,14 @@
  * mode as this is a reasonable place to clean up from messes made by
  * ioctls, or other mayhem.
  */
-static void bpp_release(struct inode *inode, struct file *f)
+static int bpp_release(struct inode *inode, struct file *f)
 {
       unsigned minor = MINOR(inode->i_rdev);
       instances[minor].opened = 0;
 
       if (instances[minor].mode != COMPATIBILITY)
       terminate(minor);
+      return 0;
 }
 
 static long read_nibble(unsigned minor, char *c, unsigned long cnt)
@@ -624,11 +625,10 @@
       return cnt - remaining;
 }
 
-static long bpp_read(struct inode *inode, struct file *f,
-		 char *c, unsigned long cnt)
+static ssize_t bpp_read(struct file *f, char *c, size_t cnt, loff_t * ppos)
 {
       long rc;
-      const unsigned minor = MINOR(inode->i_rdev);
+      const unsigned minor = MINOR(f->f_dentry->d_inode->i_rdev);
       if (minor >= BPP_NO) return -ENODEV;
       if (!instances[minor].present) return -ENODEV;
 
@@ -694,10 +694,12 @@
 
       unsigned long remaining = cnt;
 
+
       while (remaining > 0) {
             unsigned char byte;
-            c += 1;
+
             get_user_ret(byte, c, -EFAULT);
+            c += 1;
 
             rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor);
             if (rc == -1) return -ETIMEDOUT;
@@ -774,11 +776,10 @@
  * that. Otherwise, terminate and do my writing in compat mode. This
  * is the safest course as any device can handle it.
  */
-static long bpp_write(struct inode *inode, struct file *f,
-		  const char *c, unsigned long cnt)
+static ssize_t bpp_write(struct file *f, const char *c, size_t cnt, loff_t * ppos)
 {
       long errno = 0;
-      unsigned minor = MINOR(inode->i_rdev);
+      const unsigned minor = MINOR(f->f_dentry->d_inode->i_rdev);
       if (minor >= BPP_NO) return -ENODEV;
       if (!instances[minor].present) return -ENODEV;
 
@@ -861,6 +862,11 @@
 	bpp_open,
 	NULL,		/* flush */
 	bpp_release,
+  NULL,   /* fsync */
+  NULL,   /* fasync */
+  NULL,   /* check media change */
+  NULL,   /* revalidate */
+  NULL,   /* lock */
 };
 
 #if defined(__i386__)
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/flash.c linux/drivers/sbus/char/flash.c
--- v2.2.3/linux/drivers/sbus/char/flash.c	Wed Mar 10 15:29:46 1999
+++ linux/drivers/sbus/char/flash.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: flash.c,v 1.10 1998/08/26 10:29:41 davem Exp $
+/* $Id: flash.c,v 1.11 1999/03/09 14:06:45 davem Exp $
  * flash.c: Allow mmap access to the OBP Flash, for OBP updates.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c
--- v2.2.3/linux/drivers/sbus/char/pcikbd.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/sbus/char/pcikbd.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: pcikbd.c,v 1.24 1998/11/08 11:15:24 davem Exp $
+/* $Id: pcikbd.c,v 1.25 1999/02/08 07:01:48 ecd Exp $
  * pcikbd.c: Ultra/AX PC keyboard support.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -985,9 +985,11 @@
 	}
 
 	queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+	if (!queue) {
+		printk("pcimouse_init: kmalloc(aux_queue) failed.\n");
+		return -ENOMEM;
+	}
 	memset(queue, 0, sizeof(*queue));
-	queue->head = queue->tail = 0;
-	queue->proc_list = NULL;
 
 	if (request_irq(pcimouse_irq, &pcimouse_interrupt,
 		        SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) {
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
--- v2.2.3/linux/drivers/sbus/char/sab82532.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sbus/char/sab82532.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.27 1998/11/08 11:15:25 davem Exp $
+/* $Id: sab82532.c,v 1.28 1999/01/02 16:47:35 davem Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -2136,7 +2136,7 @@
 
 __initfunc(static inline void show_serial_version(void))
 {
-	char *revision = "$Revision: 1.27 $";
+	char *revision = "$Revision: 1.28 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.2.3/linux/drivers/sbus/char/su.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sbus/char/su.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.16 1998/11/14 23:02:54 ecd Exp $
+/* $Id: su.c,v 1.18 1999/01/02 16:47:37 davem Exp $
  * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -157,7 +157,7 @@
 #define WAKEUP_CHARS 256
 
 static void autoconfig(struct su_struct *info);
-static void change_speed(struct su_struct *info);
+static void change_speed(struct su_struct *info, struct termios *old);
 static void su_wait_until_sent(struct tty_struct *tty, int timeout);
 
 /*
@@ -845,7 +845,7 @@
 	/*
 	 * and set the speed of the serial port
 	 */
-	change_speed(info);
+	change_speed(info, 0);
 
 	info->flags |= ASYNC_INITIALIZED;
 	restore_flags(flags);
@@ -948,7 +948,8 @@
  * the specified baud rate for a serial port.
  */
 static void
-change_speed(struct su_struct *info)
+change_speed(struct su_struct *info,
+	     struct termios *old_termios)
 {
 	int		quot = 0, baud;
 	unsigned int	cval, fcr = 0;
@@ -999,7 +1000,25 @@
 		else if (baud)
 			quot = info->baud_base / baud;
 	}
-	/* If the quotient is ever zero, default to 9600 bps */
+	/* If the quotient is zero refuse the change */
+	if (!quot && old_termios) {
+		info->tty->termios->c_cflag &= ~CBAUD;
+		info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+		baud = tty_get_baud_rate(info->tty);
+		if (!baud)
+			baud = 9600;
+		if (baud == 38400 &&
+		    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+			quot = info->custom_divisor;
+		else {
+			if (baud == 134)
+				/* Special case since 134 is really 134.5 */
+				quot = (2*info->baud_base / 269);
+			else if (baud)
+				quot = info->baud_base / baud;
+		}
+	}
+	/* As a last resort, if the quotient is zero, default to 9600 bps */
 	if (!quot)
 		quot = info->baud_base / 9600;
 	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / info->baud_base);
@@ -1157,7 +1176,7 @@
 			info->cflag |= 1200;
 			break;
 	}
-	change_speed(info);
+	change_speed(info, 0);
 }
 
 static void
@@ -1303,6 +1322,9 @@
 	if (serial_paranoia_check(info, tty->device, "su_send_char"))
 		return;
 
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
 	info->x_char = ch;
 	if (ch) {
 		/* Make sure transmit interrupts are on */
@@ -1618,7 +1640,7 @@
 		== RELEVANT_IFLAG(old_termios->c_iflag)))
 	  return;
 
-	change_speed(info);
+	change_speed(info, old_termios);
 
 	/* Handle transition to B0 status */
 	if ((old_termios->c_cflag & CBAUD) &&
@@ -2067,13 +2089,13 @@
 			*tty->termios = info->normal_termios;
 		else 
 			*tty->termios = info->callout_termios;
-		change_speed(info);
+		change_speed(info, 0);
 	}
 #ifdef CONFIG_SERIAL_CONSOLE
 	if (sercons.cflag && sercons.index == line) {
 		tty->termios->c_cflag = sercons.cflag;
 		sercons.cflag = 0;
-		change_speed(info);
+		change_speed(info, 0);
 	}
 #endif
 	info->session = current->session;
@@ -2193,7 +2215,7 @@
  */
 __initfunc(static __inline__ void show_su_version(void))
 {
-	char *revision = "$Revision: 1.16 $";
+	char *revision = "$Revision: 1.18 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2207,8 +2229,8 @@
  * This routine is called by su_init() to initialize a specific serial
  * port.  It determines what type of UART chip this serial port is
  * using: 8250, 16450, 16550, 16550A.  The important question is
- * whether or not this UART is a 16550A or not, since this will
- * determine whether or not we can use its FIFO features or not.
+ * whether or not this UART is a 16550A, since this will determine
+ * whether or not we can use its FIFO features.
  */
 static void
 autoconfig(struct su_struct *info)
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.2.3/linux/drivers/sbus/char/sunkbd.c	Mon Oct  5 13:13:40 1998
+++ linux/drivers/sbus/char/sunkbd.c	Mon Mar 15 16:11:30 1999
@@ -514,8 +514,13 @@
 	mark_bh(CONSOLE_BH);
 	add_keyboard_randomness(keycode);
 
+	tty = ttytab? ttytab[fg_console]: NULL;
+	if (tty && (!tty->driver_data)) {
+		/* This is to workaround ugly bug in tty_io.c, which
+                   does not do locking when it should */
+		tty = NULL;
+	}
 	kbd = kbd_table + fg_console;
-	tty = ttytab[fg_console];
 	if((raw_mode = (kbd->kbdmode == VC_RAW))) {
 		if (kbd_redirected == fg_console+1)
 			push_kbd (keycode);
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v2.2.3/linux/drivers/sbus/char/sunserial.c	Sun Nov  8 14:03:01 1998
+++ linux/drivers/sbus/char/sunserial.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: sunserial.c,v 1.67 1998/10/25 03:22:46 jj Exp $
+/* $Id: sunserial.c,v 1.68 1998/12/09 18:53:51 davem Exp $
  * serial.c: Serial port driver infrastructure for the Sparc.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -401,10 +401,10 @@
 		return memory_start;
 		
 #ifdef __sparc_v9__
-	ret = prom_finddevice("/ssp-serial");
-	if (ret && ret != -1) {
+	{	extern int this_is_starfire;
 		/* Hello, Starfire. Pleased to meet you :) */
-		return memory_start;
+		if(this_is_starfire != 0)
+			return memory_start;
 	}
 #endif
 
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
--- v2.2.3/linux/drivers/sbus/char/zs.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/sbus/char/zs.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.32 1998/11/08 11:15:29 davem Exp $
+/* $Id: zs.c,v 1.40 1999/02/23 15:14:45 jj Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -53,6 +53,22 @@
 #define KEYBOARD_LINE 0x2
 #define MOUSE_LINE    0x3
 
+/* On 32-bit sparcs we need to delay after register accesses
+ * to accomodate sun4 systems, but we do not need to flush writes.
+ * On 64-bit sparc we only need to flush single writes to ensure
+ * completion.
+ */
+#ifndef __sparc_v9__
+#define ZSDELAY()		udelay(5)
+#define ZSDELAY_LONG()		udelay(20)
+#define ZS_WSYNC(channel)	do { } while(0)
+#else
+#define ZSDELAY()
+#define ZSDELAY_LONG()
+#define ZS_WSYNC(channel) \
+	((void) *((volatile unsigned char *)(&((channel)->control))))
+#endif
+
 struct sun_zslayout **zs_chips;
 struct sun_zschannel **zs_channels;
 struct sun_zschannel *zs_mousechan;
@@ -200,9 +216,9 @@
 	unsigned char retval;
 
 	channel->control = reg;
-	udelay(5);
+	ZSDELAY();
 	retval = channel->control;
-	udelay(5);
+	ZSDELAY();
 	return retval;
 }
 
@@ -210,9 +226,9 @@
 			       unsigned char reg, unsigned char value)
 {
 	channel->control = reg;
-	udelay(5);
+	ZSDELAY();
 	channel->control = value;
-	udelay(5);
+	ZSDELAY();
 }
 
 static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
@@ -239,7 +255,7 @@
 		write_zsreg(channel, R9, CHRA);
 	else
 		write_zsreg(channel, R9, CHRB);
-	udelay(20);	/* wait for some old sun4's */
+	ZSDELAY_LONG();
 	write_zsreg(channel, R4, regs[R4]);
 	write_zsreg(channel, R3, regs[R3] & ~RxENAB);
 	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
@@ -267,10 +283,16 @@
 {
 	int loops = ZS_PUT_CHAR_MAX_DELAY;
 
+	/* Do not change this to use ZSDELAY as this is
+	 * a timed polling loop and on sparc64 ZSDELAY
+	 * is a nop.  -DaveM
+	 */
 	while((channel->control & Tx_BUF_EMP) == 0 && --loops)
 		udelay(5);
+
 	channel->data = ch;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(channel);
 }
 
 /* Sets or clears DTR/RTS on the requested line */
@@ -420,7 +442,7 @@
 
 	do {
 		ch = (info->zs_channel->data) & info->parity_mask;
-		udelay(5);
+		ZSDELAY();
 
 		/* If this is the console keyboard, we need to handle
 		 * L1-A's here.
@@ -482,7 +504,7 @@
 
 		/* Check if we have another character... */
 		stat = info->zs_channel->control;
-		udelay(5);
+		ZSDELAY();
 		if (!(stat & Rx_CH_AV))
 			break;
 
@@ -507,7 +529,8 @@
 	if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
 		/* That's peculiar... */
 		info->zs_channel->control = RES_Tx_P;
-		udelay(5);
+		ZSDELAY();
+		ZS_WSYNC(info->zs_channel);
 		return;
 	}
 
@@ -521,7 +544,8 @@
 
 	if(info->xmit_cnt <= 0) {
 		info->zs_channel->control = RES_Tx_P;
-		udelay(5);
+		ZSDELAY();
+		ZS_WSYNC(info->zs_channel);
 	}
 }
 
@@ -531,10 +555,11 @@
 
 	/* Get status from Read Register 0 */
 	status = info->zs_channel->control;
-	udelay(5);
+	ZSDELAY();
 	/* Clear status condition... */
 	info->zs_channel->control = RES_EXT_INT;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(info->zs_channel);
 
 #if 0
 	if(status & DCD) {
@@ -571,7 +596,7 @@
 	stat = read_zsreg(info->zs_channel, R1);
 	if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
 		ch = info->zs_channel->data;
-		udelay(5);
+		ZSDELAY();
 	}
 
 	if (!tty)
@@ -592,7 +617,8 @@
 	queue_task(&tty->flip.tqueue, &tq_timer);
 clear:
 	info->zs_channel->control = ERR_RES;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(info->zs_channel);
 }
 
 
@@ -772,9 +798,12 @@
 	 * Clear the interrupt registers.
 	 */
 	info->zs_channel->control = ERR_RES;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(info->zs_channel);
+
 	info->zs_channel->control = RES_H_IUS;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(info->zs_channel);
 
 	/*
 	 * Now, initialize the Zilog
@@ -798,9 +827,12 @@
 	 * And clear the interrupt registers again for luck.
 	 */
 	info->zs_channel->control = ERR_RES;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(info->zs_channel);
+
 	info->zs_channel->control = RES_H_IUS;
-	udelay(5);
+	ZSDELAY();
+	ZS_WSYNC(info->zs_channel);
 
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -1006,6 +1038,7 @@
 	while((chan->control & Tx_BUF_EMP)==0)
 		udelay(5);
 	chan->data = kgdb_char;
+	ZS_WSYNC(chan);
 }
 
 char getDebugChar(void)
@@ -1013,7 +1046,7 @@
 	struct sun_zschannel *chan = zs_kgdbchan;
 
 	while((chan->control & Rx_CH_AV)==0)
-		barrier();
+		udelay(5);
 	return chan->data;
 }
 
@@ -1254,6 +1287,9 @@
 		goto check_and_exit;
 	}
 
+	if(new_serial.baud_base < 9600)
+		return -EINVAL;
+
 	if (info->count > 1)
 		return -EBUSY;
 
@@ -1291,6 +1327,7 @@
 
 	cli();
 	status = info->zs_channel->control;
+	ZSDELAY();
 	sti();
 	put_user_ret(status,value, -EFAULT);
 	return 0;
@@ -1303,6 +1340,7 @@
 
 	cli();
 	status = info->zs_channel->control;
+	ZSDELAY();
 	sti();
 	result =  ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
 		| ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
@@ -1806,7 +1844,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.32 $";
+	char *revision = "$Revision: 1.40 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -1853,8 +1891,8 @@
 			int len = prom_getproperty(zsnode, "address",
 						   (void *) vaddr, sizeof(vaddr));
 
-			if(len == -1) {
-				struct linux_sbus *sbus;
+			if(len == -1 || central_bus != NULL) {
+				struct linux_sbus *sbus = NULL;
 				struct linux_sbus_device *sdev = NULL;
 
 				/* "address" property is not guarenteed,
@@ -1862,20 +1900,40 @@
 				 * anyways by our clever TLB miss handling
 				 * scheme, so don't fail here.  -DaveM
 				 */
-				for_each_sbus(sbus) {
-					for_each_sbusdev(sdev, sbus) {
-						if (sdev->prom_node == zsnode)
-							goto found;
+				if (central_bus == NULL) {
+					for_each_sbus(sbus) {
+						for_each_sbusdev(sdev, sbus) {
+							if (sdev->prom_node == zsnode)
+								goto found;
+						}
 					}
 				}
 			found:
-				if (sdev == NULL)
+				if (sdev == NULL && central_bus == NULL)
 					prom_halt();
-				prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev);
-				mapped_addr = (unsigned long)
-					sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
-						       PAGE_SIZE, "Zilog Registers",
-						       sdev->reg_addrs[0].which_io, 0x0);
+				if (central_bus == NULL) {
+					prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev);
+					mapped_addr = (unsigned long)
+						sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+							       PAGE_SIZE, "Zilog Registers",
+							       sdev->reg_addrs[0].which_io, 0x0);
+				} else {
+					struct linux_prom_registers zsregs[1];
+					int err;
+
+					err = prom_getproperty(zsnode, "reg",
+							       (char *)&zsregs[0],
+							       sizeof(zsregs));
+					if (err == -1) {
+						prom_printf("ZS: Cannot map Zilog regs.\n");
+						prom_halt();
+					}
+					prom_apply_fhc_ranges(central_bus->child, &zsregs[0], 1);
+					prom_apply_central_ranges(central_bus, &zsregs[0], 1);
+					mapped_addr = (unsigned long)
+						__va((((unsigned long)zsregs[0].which_io)<<32) |
+						     (((unsigned long)zsregs[0].phys_addr)));
+				}
 			} else if(len % sizeof(unsigned int)) {
 				prom_printf("WHOOPS:  proplen for %s "
 					    "was %d, need multiple of "
@@ -1954,25 +2012,27 @@
 		/* Can use the prom for other machine types */
 		zsnode = prom_getchild(prom_root_node);
 		if (sparc_cpu_model == sun4d) {
-			int board, node;
+			int node;
+			int no = 0;
 			
 			tmpnode = zsnode;
+			zsnode = 0;
+			bbnode = 0;
 			while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
-				board = prom_getintdefault (tmpnode, "board#", -1);
-				if (board == (chip >> 1)) {
-					node = prom_getchild(tmpnode);
-					if (node && (node = prom_searchsiblings(node, "bootbus"))) {
+				bbnode = prom_getchild(tmpnode);
+				if (bbnode && (bbnode = prom_searchsiblings(bbnode, "bootbus"))) {
+					if (no == (chip >> 1)) {
 						cpunode = tmpnode;
-						bbnode = node;
-						zsnode = prom_getchild(node);
+						zsnode = prom_getchild(bbnode);
 						chipid = (chip & 1);
 						break;
 					}
+					no++;
 				}
 				tmpnode = prom_getsibling(tmpnode);
 			}
 			if (!tmpnode)
-				panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
+				panic ("get_zs: couldn't find %dth bootbus\n", chip >> 1);
 		} else {
 			tmpnode = prom_searchsiblings(zsnode, "obio");
 			if(tmpnode)
@@ -2066,13 +2126,12 @@
 	
 	node = prom_getchild(prom_root_node);
 	if (sparc_cpu_model == sun4d) {
-		node = prom_searchsiblings(node, "boards");
-		if (!node)
-			panic ("Cannot find out count of boards");
-		else
-			node = prom_getchild(node);
-		while (node && (node = prom_searchsiblings(node, "bif"))) {
-			NUM_SERIAL += 2;
+		int bbnode;
+		
+		while (node && (node = prom_searchsiblings(node, "cpu-unit"))) {
+			bbnode = prom_getchild(node);
+			if (bbnode && prom_searchsiblings(bbnode, "bootbus"))
+				NUM_SERIAL += 2;
 			node = prom_getsibling(node);
 		}
 		goto no_probe;
@@ -2082,7 +2141,7 @@
 		int central_node;
 
 		/* Central bus zilogs must be checked for first,
-		 * since Enterprise boxes have SBUS as well.
+		 * since Enterprise boxes might have SBUSes as well.
 		 */
 		central_node = prom_finddevice("/central");
 		if(central_node != 0 && central_node != -1)
@@ -2290,13 +2349,21 @@
 	/* Initialize Softinfo */
 	zs_prepare();
 
+	/* Grab IRQ line before poking the chips so we do
+	 * not lose any interrupts.
+	 */
+	if (request_irq(zilog_irq, zs_interrupt,
+			(SA_INTERRUPT | SA_STATIC_ALLOC),
+			"Zilog8530", zs_chain))
+		panic("Unable to attach zs intr\n");
+
 	/* Initialize Hardware */
 	for(channel = 0; channel < NUM_CHANNELS; channel++) {
 
 		/* Hardware reset each chip */
 		if (!(channel & 1)) {
 			write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
-			udelay(20);	/* wait for some old sun4's */
+			ZSDELAY_LONG();
 			dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
 		}
 
@@ -2462,10 +2529,6 @@
 		printk(" is a Zilog8530\n");
 	}
 
-	if (request_irq(zilog_irq, zs_interrupt,
-			(SA_INTERRUPT | SA_STATIC_ALLOC),
-			"Zilog8530", zs_chain))
-		panic("Unable to attach zs intr\n");
 	restore_flags(flags);
 
 	keyboard_zsinit(kbd_put_char);
@@ -2549,7 +2612,7 @@
 
 	/* Last character is being transmitted now (hopefully). */
 	info->zs_channel->control = RES_Tx_P;
-	udelay(5);
+	ZSDELAY();
 
 	restore_flags(flags);
 	return;
diff -u --recursive --new-file v2.2.3/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.2.3/linux/drivers/sbus/sbus.c	Sun Nov  8 14:03:01 1998
+++ linux/drivers/sbus/sbus.c	Mon Mar 15 16:11:30 1999
@@ -1,4 +1,4 @@
-/* $Id: sbus.c,v 1.73 1998/10/07 11:35:50 jj Exp $
+/* $Id: sbus.c,v 1.76 1998/12/17 11:11:26 davem Exp $
  * sbus.c:  SBus support routines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -71,7 +71,7 @@
 	base = (unsigned long) sbus_dev->reg_addrs[0].phys_addr;
 	if(base>=SUN_SBUS_BVADDR ||
 	   (sparc_cpu_model != sun4c &&
-	   sparc_cpu_model != sun4)) {
+	    sparc_cpu_model != sun4)) {
 		/* Ahh, we can determine the slot and offset */
 		if(sparc_cpu_model == sun4u) {
 			/* A bit tricky on the SYSIO. */
@@ -261,6 +261,11 @@
 			if (!pcibios_present()) {	
 				prom_printf("Neither SBUS nor PCI found.\n");
 				prom_halt();
+			} else {
+#ifdef __sparc_v9__
+				extern void firetruck_init(void);
+				firetruck_init();
+#endif
 			}
 			return;
 #else
@@ -296,6 +301,7 @@
 	 */
 	sbus = SBus_chain = kmalloc(sizeof(struct linux_sbus), GFP_ATOMIC);
 	sbus->next = 0;
+	sbus->prom_node = nd;
 	this_sbus=nd;
 
 	if(iommund && sparc_cpu_model != sun4u && sparc_cpu_model != sun4d)
@@ -319,7 +325,6 @@
 
 		prom_getstring(this_sbus, "name", lbuf, sizeof(lbuf));
 		lbuf[sizeof(sbus->prom_name) - 1] = 0;
-		sbus->prom_node = this_sbus;
 		strcpy(sbus->prom_name, lbuf);
 		sbus->clock_freq = sbus_clock;
 #ifndef __sparc_v9__		
@@ -406,6 +411,7 @@
 			sbus->next = kmalloc(sizeof(struct linux_sbus), GFP_ATOMIC);
 			sbus = sbus->next;
 			sbus->next = 0;
+			sbus->prom_node = this_sbus;
 		} else {
 			break;
 		}
@@ -415,6 +421,13 @@
 		sun4d_init_sbi_irq();
 	}
 	
+#ifdef __sparc_v9__
+	if (sparc_cpu_model == sun4u) {
+		extern void firetruck_init(void);
+
+		firetruck_init();
+	}
+#endif
 #ifdef CONFIG_SUN_OPENPROMIO
 	openprom_init();
 #endif
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.2.3/linux/drivers/scsi/Makefile	Wed Mar 10 15:29:46 1999
+++ linux/drivers/scsi/Makefile	Mon Mar 15 16:11:30 1999
@@ -560,6 +560,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_FCAL),y)
+L_OBJS += fcal.o
+else
+  ifeq ($(CONFIG_SCSI_FCAL),m)
+  M_OBJS += fcal.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_EATA),y)
 L_OBJS += eata.o
 else
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/esp.c linux/drivers/scsi/esp.c
--- v2.2.3/linux/drivers/scsi/esp.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/scsi/esp.c	Mon Mar 15 16:11:31 1999
@@ -1,6 +1,6 @@
 /* esp.c:  EnhancedScsiProcessor Sun SCSI driver code.
  *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
  */
 
 /* TODO:
@@ -162,6 +162,17 @@
 	in_tgterror   = 0x84,  /* Target did something stupid           */
 };
 
+enum {
+	/* Zero has special meaning, see skipahead[12]. */
+/*0*/	do_never,
+
+/*1*/	do_phase_determine,
+/*2*/	do_reset_bus,
+/*3*/	do_reset_complete,
+/*4*/	do_work_bus,
+/*5*/	do_intr_end
+};
+
 struct proc_dir_entry proc_scsi_esp = {
 	PROC_SCSI_ESP, 3, "esp",
 	S_IFDIR | S_IRUGO | S_IXUGO, 2
@@ -354,24 +365,30 @@
 	};
 }
 
+#ifdef DEBUG_STATE_MACHINE
 static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase)
 {
-#ifdef DEBUG_STATE_MACHINE
 	ESPLOG(("<%s>", phase_string(newphase)));
-#endif
 	s->SCp.sent_command = s->SCp.phase;
 	s->SCp.phase = newphase;
 }
+#else
+#define esp_advance_phase(__s, __newphase) \
+	(__s)->SCp.sent_command = (__s)->SCp.phase; \
+	(__s)->SCp.phase = (__newphase);
+#endif
 
+#ifdef DEBUG_ESP_CMDS
 extern inline void esp_cmd(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
 			   unchar cmd)
 {
-#ifdef DEBUG_ESP_CMDS
 	esp->espcmdlog[esp->espcmdent] = cmd;
 	esp->espcmdent = (esp->espcmdent + 1) & 31;
-#endif
 	eregs->esp_cmd = cmd;
 }
+#else
+#define esp_cmd(__esp, __eregs, __cmd)	(__eregs)->esp_cmd = (__cmd)
+#endif
 
 /* How we use the various Linux SCSI data structures for operation.
  *
@@ -442,14 +459,23 @@
 }
 
 /* Resetting various pieces of the ESP scsi driver chipset/buses. */
-static inline void esp_reset_dma(struct Sparc_ESP *esp)
+static void esp_reset_dma(struct Sparc_ESP *esp)
 {
 	struct sparc_dma_registers *dregs = esp->dregs;
-	unsigned long tmp, flags;
-	int can_do_burst16, can_do_burst32;
+	unsigned long flags;
+	int can_do_burst16, can_do_burst32, can_do_burst64;
+	int can_do_sbus64;
 
 	can_do_burst16 = esp->bursts & DMA_BURST16;
 	can_do_burst32 = esp->bursts & DMA_BURST32;
+	can_do_burst64 = 0;
+	can_do_sbus64 = 0;
+#ifdef __sparc_v9__
+	/* XXX Can sun4d do these too? */
+	can_do_burst64 = esp->bursts & DMA_BURST64;
+	can_do_sbus64 = 1;
+	mmu_set_sbus64(esp->edev, esp->bursts);
+#endif
 
 	/* Punt the DVMA into a known state. */
 	if(esp->dma->revision != dvmahme) {
@@ -462,14 +488,19 @@
 
 		save_flags(flags); cli(); /* I really hate this chip. */
 
-		dregs->cond_reg = 0x08000000;   /* Reset interface to FAS */
-		dregs->cond_reg = DMA_RST_SCSI; /* Reset DVMA itself */
+		dregs->cond_reg = DMA_RESET_FAS366;   /* Reset interface to FAS */
+		dregs->cond_reg = DMA_RST_SCSI;       /* Reset DVMA itself */
 
-		tmp = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB);
-		tmp &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ);
+		esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB);
+		esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ);
 
 		if(can_do_burst32)
-			tmp |= DMA_BRST32;
+			esp->prev_hme_dmacsr |= DMA_BRST32;
+		else if(can_do_burst64)
+			esp->prev_hme_dmacsr |= DMA_BRST64;
+
+		if(can_do_sbus64)
+			esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
 
 		/* This chip is horrible. */
 		while(dregs->cond_reg & DMA_PEND_READ)
@@ -477,8 +508,14 @@
 
 		dregs->cond_reg = 0;
 
-		dregs->cond_reg = tmp;        /* bite me */
-		restore_flags(flags);         /* ugh...  */
+		dregs->cond_reg = esp->prev_hme_dmacsr;
+
+		/* This is necessary to avoid having the SCSI channel
+		 * engine lock up on us.
+		 */
+		dregs->st_addr = 0;
+
+		restore_flags(flags);
 		break;
 	case dvmarev2:
 		/* This is the gate array found in the sun4m
@@ -512,7 +549,7 @@
 }
 
 /* Reset the ESP chip, _not_ the SCSI bus. */
-static inline void esp_reset_esp(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
+static void esp_reset_esp(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
 {
 	int family_code, version, i;
 	volatile int trash;
@@ -524,8 +561,8 @@
 
 	/* Reload the configuration registers */
 	eregs->esp_cfact = esp->cfact;
-	eregs->esp_stp   = 0;
-	eregs->esp_soff  = 0;
+	eregs->esp_stp   = esp->prev_stp = 0;
+	eregs->esp_soff  = esp->prev_soff = 0;
 	eregs->esp_timeo = esp->neg_defp;
 
 	/* This is the only point at which it is reliable to read
@@ -541,11 +578,11 @@
 			esp->erev = fashme; /* Version is usually '5'. */
 		else
 			esp->erev = fas100a;
-		printk("esp%d: FAST chip is %s (family=%d, version=%d)\n",
-		       esp->esp_id,
-		       (esp->erev == fas236) ? "fas236" :
-		       ((esp->erev == fas100a) ? "fas100a" :
-		       "fasHME"), family_code, (version & 7));
+		ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n",
+			 esp->esp_id,
+			 (esp->erev == fas236) ? "fas236" :
+			 ((esp->erev == fas100a) ? "fas100a" :
+			  "fasHME"), family_code, (version & 7)));
 
 		esp->min_period = ((4 * esp->ccycle) / 1000);
 	} else {
@@ -565,7 +602,7 @@
 	case esp236:
 		/* Slow 236 */
 		eregs->esp_cfg2 = esp->config2;
-		eregs->esp_cfg3 = esp->config3[0];
+		eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0];
 		break;
 	case fashme:
 		esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
@@ -574,13 +611,18 @@
 		/* Fast 236 or HME */
 		eregs->esp_cfg2 = esp->config2;
 		for(i=0; i<8; i++) {
-			if(esp->erev == fashme)
-				esp->config3[i] |=
-					(ESP_CONFIG3_FCLOCK | ESP_CONFIG3_BIGID | ESP_CONFIG3_OBPUSH);
-			else
+			if(esp->erev == fashme) {
+				unsigned char cfg3;
+
+				cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH;
+				if (esp->scsi_id >= 8)
+					cfg3 |= ESP_CONFIG3_IDBIT3;
+				esp->config3[i] |= cfg3;
+			} else {
 				esp->config3[i] |= ESP_CONFIG3_FCLK;
+			}
 		}
-		eregs->esp_cfg3 = esp->config3[0];
+		eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0];
 		if(esp->erev == fashme) {
 			esp->radelay = 80;
 		} else {
@@ -595,7 +637,7 @@
 		eregs->esp_cfg2 = esp->config2;
 		for(i=0; i<8; i++)
 			esp->config3[i] |= ESP_CONFIG3_FCLOCK;
-		eregs->esp_cfg3 = esp->config3[0];
+		eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0];
 		esp->radelay = 32;
 		break;
 	default:
@@ -609,7 +651,7 @@
 }
 
 /* This places the ESP into a known state at boot time. */
-static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
+static void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
 {
 	volatile unchar trash;
 
@@ -654,6 +696,10 @@
 	esp->edev = esp_dev;
 	esp->esp_id = id;
 
+#ifdef __sparc_v9__
+	esp_host->unchecked_isa_dma = 1;
+#endif
+
 	/* Put into the chain of esp chips detected */
 	if(espchain) {
 		elink = espchain;
@@ -877,7 +923,7 @@
 	esp->ctick = ESP_TICK(ccf, esp->ccycle);
 	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
 	esp->sync_defp = SYNC_DEFP_SLOW;
-	printk("SCSI ID %d  Clock %d MHz CCF=%d Time-Out %d ",
+	printk("SCSI ID %d Clk %dMHz CCF=%d TOut %d ",
 	       esp->scsi_id, (fmhz / 1000000),
 	       ccf, (int) esp->neg_defp);
 
@@ -910,28 +956,28 @@
 	eregs->esp_cfg2 = esp->config2;
 	if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
 	   (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
-		printk("NCR53C90(esp100) detected\n");
+		printk("NCR53C90(esp100)\n");
 		esp->erev = esp100;
 	} else {
 		eregs->esp_cfg2 = esp->config2 = 0;
 		eregs->esp_cfg3 = 0;
-		eregs->esp_cfg3 = esp->config3[0] = 5;
+		eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[0] = 5;
 		if(eregs->esp_cfg3 != 5) {
-			printk("NCR53C90A(esp100a) detected\n");
+			printk("NCR53C90A(esp100a)\n");
 			esp->erev = esp100a;
 		} else {
 			int target;
 
 			for(target=0; target<8; target++)
 				esp->config3[target] = 0;
-			eregs->esp_cfg3 = 0;
+			eregs->esp_cfg3 = esp->prev_cfg3 = 0;
 			if(ccf > ESP_CCF_F5) {
-				printk("NCR53C9XF(espfast) detected\n");
+				printk("NCR53C9XF(espfast)\n");
 				esp->erev = fast;
 				eregs->esp_cfg2 = esp->config2 = 0;
 				esp->sync_defp = SYNC_DEFP_FAST;
 			} else {
-				printk("NCR53C9x(esp236) detected\n");
+				printk("NCR53C9x(esp236)\n");
 				esp->erev = esp236;
 				eregs->esp_cfg2 = esp->config2 = 0;
 			}
@@ -958,6 +1004,10 @@
 	esp->prevmsgout = esp->prevmsgin = 0;
 	esp->msgout_len = esp->msgin_len = 0;
 
+	/* Clear the one behind caches to hold unmatchable values. */
+	esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff;
+	esp->prev_hme_dmacsr = 0xffffffff;
+
 	/* Reset the thing before we try anything... */
 	esp_bootup_reset(esp, eregs);
 
@@ -1238,6 +1288,59 @@
 	return esp_host_info(esp, buffer, offset, length);
 }
 
+static void esp_get_dmabufs(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	if(sp->use_sg == 0) {
+		sp->SCp.this_residual = sp->request_bufflen;
+		sp->SCp.buffer = (struct scatterlist *) sp->request_buffer;
+		sp->SCp.buffers_residual = 0;
+		sp->SCp.have_data_in = mmu_get_scsi_one((char *)sp->SCp.buffer,
+							sp->SCp.this_residual,
+							esp->edev->my_bus);
+		sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in);
+	} else {
+		sp->SCp.buffer = (struct scatterlist *) sp->buffer;
+		sp->SCp.buffers_residual = sp->use_sg - 1;
+		sp->SCp.this_residual = sp->SCp.buffer->length;
+		mmu_get_scsi_sgl((struct mmu_sglist *) sp->SCp.buffer,
+				 sp->SCp.buffers_residual,
+				 esp->edev->my_bus);
+		sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.buffer->dvma_address);
+	}
+}
+
+static void esp_release_dmabufs(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	if(sp->use_sg == 0)
+		mmu_release_scsi_one(sp->SCp.have_data_in,
+				     sp->request_bufflen,
+				     esp->edev->my_bus);
+	else
+		mmu_release_scsi_sgl((struct mmu_sglist *)
+				     sp->buffer, sp->use_sg - 1,
+				     esp->edev->my_bus);
+}
+
+static void esp_restore_pointers(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	struct esp_pointers *ep = &esp->data_pointers[sp->target];
+
+	sp->SCp.ptr = ep->saved_ptr;
+	sp->SCp.buffer = ep->saved_buffer;
+	sp->SCp.this_residual = ep->saved_this_residual;
+	sp->SCp.buffers_residual = ep->saved_buffers_residual;
+}
+
+static void esp_save_pointers(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	struct esp_pointers *ep = &esp->data_pointers[sp->target];
+
+	ep->saved_ptr = sp->SCp.ptr;
+	ep->saved_buffer = sp->SCp.buffer;
+	ep->saved_this_residual = sp->SCp.this_residual;
+	ep->saved_buffers_residual = sp->SCp.buffers_residual;
+}
+
 /* Some rules:
  *
  *   1) Never ever panic while something is live on the bus.
@@ -1307,7 +1410,7 @@
 	esp->msgout_len = 4;
 }
 
-static inline void esp_exec_cmd(struct Sparc_ESP *esp)
+static void esp_exec_cmd(struct Sparc_ESP *esp)
 {
 	struct sparc_dma_registers *dregs = esp->dregs;
 	struct Sparc_ESP_regs *eregs = esp->eregs;
@@ -1318,15 +1421,18 @@
 	int lun, target;
 	int i;
 
-	/* Hold off if we've been reselected or an IRQ is showing... */
-	if(esp->disconnected_SC || DMA_IRQ_P(dregs))
+	/* Hold off if we have disconnected commands and
+	 * an IRQ is showing...
+	 */
+	if(esp->disconnected_SC && DMA_IRQ_P(dregs))
 		return;
 
 	/* Grab first member of the issue queue. */
 	SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
 
 	/* Safe to panic here because current_SC is null. */
-	if(!SCptr) panic("esp: esp_exec_cmd and issue queue is NULL");
+	if(!SCptr)
+		panic("esp: esp_exec_cmd and issue queue is NULL");
 
 	SDptr = SCptr->device;
 	lun = SCptr->lun;
@@ -1402,7 +1508,10 @@
 		SDptr->sync_max_offset = 0;
 		SDptr->sync_min_period = 0;
 	} else {
-		int toshiba_cdrom_hwbug_wkaround = 0;
+		/* Sorry, I have had way too many problems with
+		 * various CDROM devices on ESP. -DaveM
+		 */
+		int cdrom_hwbug_wkaround = 0;
 
 		/* Never allow disconnects or synchronous transfers on
 		 * SparcStation1 and SparcStation1+.  Allowing those
@@ -1429,8 +1538,7 @@
 		 */
 		if(esp->erev == fashme && !SDptr->wide) {
 			if(!SDptr->borken &&
-			   (SDptr->type != TYPE_ROM ||
-			    strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+			   SDptr->type != TYPE_ROM) {
 				build_wide_nego_msg(esp, 16);
 				SDptr->wide = 1;
 				esp->wnip = 1;
@@ -1442,12 +1550,11 @@
 		}
 
 		if(!SDptr->borken) {
-			if((SDptr->type == TYPE_ROM) &&
-			   (!strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+			if((SDptr->type == TYPE_ROM)) {
 				/* Nice try sucker... */
-				printk(KERN_INFO "esp%d: Disabling sync for buggy "
-				       "Toshiba CDROM.\n", esp->esp_id);
-				toshiba_cdrom_hwbug_wkaround = 1;
+				ESPMISC(("esp%d: Disabling sync for buggy "
+					 "CDROM.\n", esp->esp_id));
+				cdrom_hwbug_wkaround = 1;
 				build_sync_nego_msg(esp, 0, 0);
 			} else {
 				build_sync_nego_msg(esp, esp->sync_defp, 15);
@@ -1483,13 +1590,9 @@
 		 *           thank you very much. ;-)
 		 */
 		if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) ||
-#if 1 /* Until I find out why HME barfs with disconnects enabled... */
-		   toshiba_cdrom_hwbug_wkaround || SDptr->borken || esp->erev == fashme) {
-#else
-		   toshiba_cdrom_hwbug_wkaround || SDptr->borken) {
-#endif
-			printk(KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
-			       "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun);
+		   cdrom_hwbug_wkaround || SDptr->borken) {
+			ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
+				 "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun));
 			SDptr->disconnect = 0;
 			*cmdp++ = IDENTIFY(0, lun);
 		} else {
@@ -1517,16 +1620,20 @@
 			(ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
 	else
 		eregs->esp_busid = (target & 7);
-	eregs->esp_soff = SDptr->sync_max_offset;
-	eregs->esp_stp  = SDptr->sync_min_period;
-	if(esp->erev > esp100a)
-		eregs->esp_cfg3 = esp->config3[target];
-
+	if (esp->prev_soff != SDptr->sync_max_offset ||
+	    esp->prev_stp  != SDptr->sync_min_period ||
+	    (esp->erev > esp100a &&
+	     esp->prev_cfg3 != esp->config3[target])) {
+		eregs->esp_soff = esp->prev_soff = SDptr->sync_max_offset;
+		eregs->esp_stp  = esp->prev_stp  = SDptr->sync_min_period;
+		if(esp->erev > esp100a)
+			eregs->esp_cfg3 =
+				esp->prev_cfg3 =
+				esp->config3[target];
+	}
 	i = (cmdp - esp->esp_command);
 
 	if(esp->erev == fashme) {
-		unsigned long tmp;
-
 		esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Grrr! */
 
 		/* Set up the DMA and HME counters */
@@ -1537,12 +1644,12 @@
 		esp_cmd(esp, eregs, the_esp_command);
 
 		/* Talk about touchy hardware... */
-		tmp = dregs->cond_reg;
-		tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
-		tmp &= ~(DMA_ST_WRITE);
+		esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
+					 (DMA_SCSI_DISAB | DMA_ENABLE)) &
+					~(DMA_ST_WRITE));
 		dregs->cnt = 16;
 		dregs->st_addr = esp->esp_command_dvma;
-		dregs->cond_reg = tmp;
+		dregs->cond_reg = esp->prev_hme_dmacsr;
 	} else {
 		/* Set up the DMA and ESP counters */
 		eregs->esp_tclow = i;
@@ -1575,35 +1682,10 @@
 	/* We use the scratch area. */
 	ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun));
 	ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun));
-	if(!SCpnt->use_sg) {
-		ESPQUEUE(("!use_sg\n"));
-		SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
-		SCpnt->SCp.buffer           =
-			(struct scatterlist *) SCpnt->request_buffer;
-		SCpnt->SCp.buffers_residual = 0;
-		/* Sneaky. */
-		SCpnt->SCp.have_data_in = mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
-							   SCpnt->SCp.this_residual,
-							   esp->edev->my_bus);
-		/* XXX The casts are extremely gross, but with 64-bit kernel
-		 * XXX and 32-bit SBUS what am I to do? -DaveM
-		 */
-		SCpnt->SCp.ptr = (char *)((unsigned long)SCpnt->SCp.have_data_in);
-	} else {
-		ESPQUEUE(("use_sg "));
-#ifdef DEBUG_ESP_SG
-		printk("esp%d: sglist at %p with %d buffers\n",
-		       esp->esp_id, SCpnt->buffer, SCpnt->use_sg);
-#endif
-		SCpnt->SCp.buffer           = (struct scatterlist *) SCpnt->buffer;
-		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
-		SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
-		mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer,
-				 SCpnt->SCp.buffers_residual,
-				 esp->edev->my_bus);
-		/* XXX Again these casts are sick... -DaveM */
-		SCpnt->SCp.ptr=(char *)((unsigned long)SCpnt->SCp.buffer->dvma_address);
-	}
+
+	esp_get_dmabufs(esp, SCpnt);
+	esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */
+
 	SCpnt->SCp.Status           = CHECK_CONDITION;
 	SCpnt->SCp.Message          = 0xff;
 	SCpnt->SCp.sent_command     = 0;
@@ -1634,7 +1716,7 @@
 }
 
 /* Dump driver state. */
-static inline void esp_dump_cmd(Scsi_Cmnd *SCptr)
+static void esp_dump_cmd(Scsi_Cmnd *SCptr)
 {
 	ESPLOG(("[tgt<%02x> lun<%02x> "
 		"pphase<%s> cphase<%s>]",
@@ -1643,8 +1725,8 @@
 		phase_string(SCptr->SCp.phase)));
 }
 
-static inline void esp_dump_state(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-				  struct sparc_dma_registers *dregs)
+static void esp_dump_state(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			   struct sparc_dma_registers *dregs)
 {
 	Scsi_Cmnd *SCptr = esp->current_SC;
 #ifdef DEBUG_ESP_CMDS
@@ -1735,6 +1817,7 @@
 			if(this == SCptr) {
 				*prev = (Scsi_Cmnd *) this->host_scribble;
 				this->host_scribble = NULL;
+				esp_release_dmabufs(esp, this);
 				this->result = DID_ABORT << 16;
 				this->done(this);
 				if(don)
@@ -1749,8 +1832,11 @@
 	 * on the bus at this time.  So, we let the SCSI code wait
 	 * a little bit and try again later.
 	 */
-	if(esp->current_SC)
+	if(esp->current_SC) {
+		if(don)
+			DMA_INTSON(dregs);
 		return SCSI_ABORT_BUSY;
+	}
 
 	/* It's disconnected, we have to reconnect to re-establish
 	 * the nexus and tell the device to abort.  However, we really
@@ -1759,20 +1845,70 @@
 	 * happens, we are really hung so reset the bus.
 	 */
 
+	if(don)
+		DMA_INTSON(dregs);
 	return SCSI_ABORT_SNOOZE;
 }
 
+/* We've sent ESP_CMD_RS to the ESP, the interrupt had just
+ * arrived indicating the end of the SCSI bus reset.  Our job
+ * is to clean out the command queues and begin re-execution
+ * of SCSI commands once more.
+ */
+static int esp_finish_reset(struct Sparc_ESP *esp,
+			    struct Sparc_ESP_regs *eregs,
+			    struct sparc_dma_registers *dregs)
+{
+	Scsi_Cmnd *sp = esp->current_SC;
+
+	/* Clean up currently executing command, if any. */
+	if (sp != NULL) {
+		esp_release_dmabufs(esp, sp);
+		sp->result = (DID_RESET << 16);
+		sp->scsi_done(sp);
+		esp->current_SC = NULL;
+	}
+
+	/* Clean up disconnected queue, they have been invalidated
+	 * by the bus reset.
+	 */
+	if (esp->disconnected_SC) {
+		while((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) {
+			esp_release_dmabufs(esp, sp);
+			sp->result = (DID_RESET << 16);
+			sp->scsi_done(sp);
+		}
+	}
+
+	/* SCSI bus reset is complete. */
+	esp->resetting_bus = 0;
+
+	/* Ok, now it is safe to get commands going once more. */
+	if(esp->issue_SC)
+		esp_exec_cmd(esp);
+
+	return do_intr_end;
+}
+
+static int esp_do_resetbus(struct Sparc_ESP *esp,
+			   struct Sparc_ESP_regs *eregs,
+			   struct sparc_dma_registers *dregs)
+{
+	ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
+	esp->resetting_bus = 1;
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+
+	return do_intr_end;
+}
+
 /* Reset ESP chip, reset hanging bus, then kill active and
  * disconnected commands for targets without soft reset.
  */
 int esp_reset(Scsi_Cmnd *SCptr, unsigned int how)
 {
 	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCptr->host->hostdata;
-	struct Sparc_ESP_regs *eregs = esp->eregs;
 
-	ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
-	esp->resetting_bus = 1;
-	esp_cmd(esp, eregs, ESP_CMD_RS);
+	(void) esp_do_resetbus(esp, esp->eregs, esp->dregs);
 	return SCSI_RESET_PENDING;
 }
 
@@ -1784,25 +1920,7 @@
 	if(esp->current_SC) {
 		done_SC = esp->current_SC;
 		esp->current_SC = NULL;
-
-		/* Free dvma entry. */
-		if(!done_SC->use_sg) {
-			/* Sneaky. */
-			mmu_release_scsi_one(done_SC->SCp.have_data_in,
-					     done_SC->request_bufflen,
-					     esp->edev->my_bus);
-		} else {
-#ifdef DEBUG_ESP_SG
-			printk("esp%d: unmapping sg ", esp->esp_id);
-#endif
-			mmu_release_scsi_sgl((struct mmu_sglist *) done_SC->buffer,
-					     done_SC->use_sg - 1,
-					     esp->edev->my_bus);
-#ifdef DEBUG_ESP_SG
-			printk("done.\n");
-#endif
-		}
-
+		esp_release_dmabufs(esp, done_SC);
 		done_SC->result = error;
 		done_SC->scsi_done(done_SC);
 
@@ -1820,11 +1938,6 @@
 
 /* Wheee, ESP interrupt engine. */  
 
-enum {
-	do_phase_determine, do_reset_bus, do_reset_complete,
-	do_work_bus, do_intr_end,
-};
-
 /* Forward declarations. */
 static int esp_do_phase_determine(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
 				  struct sparc_dma_registers *dregs);
@@ -1843,29 +1956,29 @@
 static int esp_do_cmdbegin(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
 			   struct sparc_dma_registers *dregs);
 
-static inline int sreg_datainp(unchar sreg)
-{
-	return (sreg & ESP_STAT_PMASK) == ESP_DIP;
-}
-
-static inline int sreg_dataoutp(unchar sreg)
-{
-	return (sreg & ESP_STAT_PMASK) == ESP_DOP;
-}
+#define sreg_datainp(__sreg)  (((__sreg) & ESP_STAT_PMASK) == ESP_DIP)
+#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP)
 
-/* Did they drop these fabs on the floor or what?!?!! */
-static inline void hme_fifo_hwbug_workaround(struct Sparc_ESP *esp,
-					     struct Sparc_ESP_regs *eregs)
+/* Read any bytes found in the FAS366 fifo, storing them into
+ * the ESP driver software state structure.
+ */
+static void hme_fifo_read(struct Sparc_ESP *esp,
+			  struct Sparc_ESP_regs *eregs)
 {
+	unsigned long count = 0;
 	unchar status = esp->sreg;
 
-	/* Cannot safely frob the fifo for these following cases. */
-	if(sreg_datainp(status) || sreg_dataoutp(status) ||
-	   (esp->current_SC && esp->current_SC->SCp.phase == in_data_done)) {
+	/* Cannot safely frob the fifo for these following cases, but
+	 * we must always read the fifo when the reselect interrupt
+	 * is pending.
+	 */
+	if(((esp->ireg & ESP_INTR_RSEL) == 0)	&&
+	   (sreg_datainp(status)		||
+	    sreg_dataoutp(status)		||
+	    (esp->current_SC &&
+	     esp->current_SC->SCp.phase == in_data_done))) {
 		ESPHME(("<wkaround_skipped>"));
-		return;
 	} else {
-		unsigned long count = 0;
 		unsigned long fcnt = eregs->esp_fflags & ESP_FF_FBYTES;
 
 		/* The HME stores bytes in multiples of 2 in the fifo. */
@@ -1886,9 +1999,9 @@
 		} else {
 			ESPHME(("no_xtra_byte"));
 		}
-		esp->hme_fifo_workaround_count = count;
-		ESPHME(("wkarnd_cnt=%d]", (int)count));
 	}
+	ESPHME(("wkarnd_cnt=%d]", (int)count));
+	esp->hme_fifo_workaround_count = count;
 }
 
 static inline void hme_fifo_push(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
@@ -1913,7 +2026,10 @@
 		return 0;
 	if(DMA_IRQ_P(dregs)) {
 		/* Yes, we are able to save an interrupt. */
-		esp->sreg = eregs->esp_status;
+		if (esp->erev == fashme)
+			esp->sreg2 = eregs->esp_status2;
+		esp->sreg = (eregs->esp_status & ~(ESP_STAT_INTR));
+		esp->ireg = eregs->esp_intrpt;
 		if(esp->erev == fashme) {
 			/* This chip is really losing. */
 			ESPHME(("HME["));
@@ -1923,10 +2039,10 @@
 			 * Happy Meal indeed....
 			 */
 			ESPHME(("fifo_workaround]"));
-			hme_fifo_hwbug_workaround(esp, eregs);
+			if(!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+			   (esp->sreg2 & ESP_STAT2_F1BYTE))
+				hme_fifo_read(esp, eregs);
 		}
-		esp->ireg = eregs->esp_intrpt;
-		esp->sreg &= ~(ESP_STAT_INTR);
 		if(!(esp->ireg & ESP_INTR_SR))
 			return 0;
 		else
@@ -1947,7 +2063,10 @@
 		return 0;
 	if(DMA_IRQ_P(dregs)) {
 		/* Yes, we are able to save an interrupt. */
-		esp->sreg = eregs->esp_status;
+		if (esp->erev == fashme)
+			esp->sreg2 = eregs->esp_status2;
+		esp->sreg = (eregs->esp_status & ~(ESP_STAT_INTR));
+		esp->ireg = eregs->esp_intrpt;
 		if(esp->erev == fashme) {
 			/* This chip is really losing. */
 			ESPHME(("HME["));
@@ -1958,10 +2077,10 @@
 			 * Happy Meal indeed....
 			 */
 			ESPHME(("fifo_workaround]"));
-			hme_fifo_hwbug_workaround(esp, eregs);
+			if(!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+			   (esp->sreg2 & ESP_STAT2_F1BYTE))
+				hme_fifo_read(esp, eregs);
 		}
-		esp->ireg = eregs->esp_intrpt;
-		esp->sreg &= ~(ESP_STAT_INTR);
 		if(!(esp->ireg & ESP_INTR_SR))
 			return 0;
 		else
@@ -2012,21 +2131,27 @@
 	}
 }
 
-static inline void dma_invalidate(struct sparc_dma_registers *dregs, enum dvma_rev drev)
+static inline void dma_invalidate(struct Sparc_ESP *esp,
+				  struct sparc_dma_registers *dregs,
+				  enum dvma_rev drev)
 {
 	unsigned int tmp;
 
 	if(drev == dvmahme) {
-		/* SMCC can bite me. */
-		tmp = dregs->cond_reg;
 		dregs->cond_reg = DMA_RST_SCSI;
 
-		/* This would explain a lot. */
-		tmp |= (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB);
+		esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
+					 (DMA_PARITY_OFF | DMA_2CLKS |
+					  DMA_SCSI_DISAB | DMA_INT_ENAB)) &
+					~(DMA_ST_WRITE | DMA_ENABLE));
 
-		tmp &= ~(DMA_ENABLE|DMA_ST_WRITE);
 		dregs->cond_reg = 0;
-		dregs->cond_reg = tmp;
+		dregs->cond_reg = esp->prev_hme_dmacsr;
+
+		/* This is necessary to avoid having the SCSI channel
+		 * engine lock up on us.
+		 */
+		dregs->st_addr = 0;
 	} else {
 		while(dregs->cond_reg & DMA_PEND_READ)
 			udelay(1);
@@ -2039,10 +2164,12 @@
 	}
 }
 
-static inline void dma_flashclear(struct sparc_dma_registers *dregs, enum dvma_rev drev)
+static inline void dma_flashclear(struct Sparc_ESP *esp,
+				  struct sparc_dma_registers *dregs,
+				  enum dvma_rev drev)
 {
 	dma_drain(dregs, drev);
-	dma_invalidate(dregs, drev);
+	dma_invalidate(esp, dregs, drev);
 }
 
 static inline int dma_can_transfer(Scsi_Cmnd *sp, enum dvma_rev drev)
@@ -2064,56 +2191,38 @@
 	return sz;
 }
 
-/* Misc. esp helper routines. */
-static inline void esp_setcount(struct Sparc_ESP_regs *eregs, int cnt, int hme)
-{
-	eregs->esp_tclow = (cnt & 0xff);
-	eregs->esp_tcmed = ((cnt >> 8) & 0xff);
-	if(hme) {
-		eregs->fas_rlo = 0;
-		eregs->fas_rhi = 0;
-	}
-}
-
-static inline int esp_getcount(struct Sparc_ESP_regs *eregs)
-{
-	return (((eregs->esp_tclow)&0xff) |
-		(((eregs->esp_tcmed)&0xff) << 8));
-}
-
-static inline int fcount(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
-{
-	if(esp->erev == fashme)
-		return esp->hme_fifo_workaround_count;
-	else
-		return eregs->esp_fflags & ESP_FF_FBYTES;
-}
-
-static inline int fnzero(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
-{
-	if(esp->erev == fashme)
-		return 0;
-	else
-		return eregs->esp_fflags & ESP_FF_ONOTZERO;
-}
+/* Misc. esp helper macros. */
+#define esp_setcount(__eregs, __cnt, __hme) \
+	(__eregs)->esp_tclow = ((__cnt) & 0xff); \
+	(__eregs)->esp_tcmed = (((__cnt) >> 8) & 0xff); \
+	if(__hme) { \
+		(__eregs)->fas_rlo = 0; \
+		(__eregs)->fas_rhi = 0; \
+	}
+
+#define esp_getcount(__eregs) \
+	((((__eregs)->esp_tclow)&0xff) | \
+	 ((((__eregs)->esp_tcmed)&0xff) << 8))
+
+#define fcount(__esp, __eregs) \
+	(((__esp)->erev == fashme) ? \
+	  (__esp)->hme_fifo_workaround_count : \
+	  (__eregs)->esp_fflags & ESP_FF_FBYTES)
+
+#define fnzero(__esp, __eregs) \
+	(((__esp)->erev == fashme) ? 0 : \
+	 (__eregs)->esp_fflags & ESP_FF_ONOTZERO)
 
 /* XXX speculative nops unnecessary when continuing amidst a data phase
  * XXX even on esp100!!!  another case of flooding the bus with I/O reg
  * XXX writes...
  */
-static inline void esp_maybe_nop(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
-{
-	if(esp->erev == esp100)
-		esp_cmd(esp, eregs, ESP_CMD_NULL);
-}
+#define esp_maybe_nop(__esp, __eregs) \
+	if((__esp)->erev == esp100) \
+		esp_cmd((__esp), (__eregs), ESP_CMD_NULL)
 
-static inline int sreg_to_dataphase(unchar sreg)
-{
-	if((sreg & ESP_STAT_PMASK) == ESP_DOP)
-		return in_dataout;
-	else
-		return in_datain;
-}
+#define sreg_to_dataphase(__sreg) \
+	((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain)
 
 /* The ESP100 when in synchronous data phase, can mistake a long final
  * REQ pulse from the target as an extra byte, it places whatever is on
@@ -2205,10 +2314,19 @@
 		lun = esp->hme_fifo_workaround_buffer[1];
 	else
 		lun = eregs->esp_fdata;
+
+	/* Yes, you read this correctly.  We report lun of zero
+	 * if we see parity error.  ESP reports parity error for
+	 * the lun byte, and this is the only way to hope to recover
+	 * because the target is connected.
+	 */
 	if(esp->sreg & ESP_STAT_PERR)
 		return 0;
+
+	/* Check for illegal bits being set in the lun. */
 	if((lun & 0x40) || !(lun & 0x80))
 		return -1;
+
 	return lun & 7;
 }
 
@@ -2219,13 +2337,18 @@
 			       Scsi_Cmnd *sp)
 {
 	Scsi_Device *dp = sp->device;
-	eregs->esp_soff = dp->sync_max_offset;
-	eregs->esp_stp  = dp->sync_min_period;
-	if(esp->erev > esp100a)
-		eregs->esp_cfg3 = esp->config3[sp->target];
-	if(esp->erev == fashme)
-		eregs->esp_busid = (sp->target & 0xf) |
-			(ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
+
+	if(esp->prev_soff  != dp->sync_max_offset ||
+	   esp->prev_stp   != dp->sync_min_period ||
+	   (esp->erev > esp100a &&
+	    esp->prev_cfg3 != esp->config3[sp->target])) {
+		eregs->esp_soff = esp->prev_soff = dp->sync_max_offset;
+		eregs->esp_stp  = esp->prev_stp = dp->sync_min_period;
+		if(esp->erev > esp100a)
+			eregs->esp_cfg3 =
+				esp->prev_cfg3 =
+				esp->config3[sp->target];
+	}
 	esp->current_SC = sp;
 }
 
@@ -2235,8 +2358,8 @@
 static inline void esp_reconnect(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
 {
 	if(!esp->disconnected_SC)
-		printk("esp%d: Weird, being reselected but disconnected "
-		       "command queue is empty.\n", esp->esp_id);
+		ESPLOG(("esp%d: Weird, being reselected but disconnected "
+			"command queue is empty.\n", esp->esp_id));
 	esp->snip = 0;
 	esp->current_SC = 0;
 	sp->SCp.phase = not_issued;
@@ -2244,8 +2367,8 @@
 }
 
 /* Begin message in phase. */
-static inline int esp_do_msgin(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-			       struct sparc_dma_registers *dregs)
+static int esp_do_msgin(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			struct sparc_dma_registers *dregs)
 {
 	/* Must be very careful with the fifo on the HME */
 	if((esp->erev != fashme) || !(eregs->esp_status2 & ESP_STAT2_FEMPTY))
@@ -2298,8 +2421,8 @@
  * within a buffer or sub-buffer should not upset us at all no matter
  * how bad the target and/or ESP fucks things up.
  */
-static inline int esp_do_data(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-			      struct sparc_dma_registers *dregs)
+static int esp_do_data(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+		       struct sparc_dma_registers *dregs)
 {
 	Scsi_Cmnd *SCptr = esp->current_SC;
 	int thisphase, hmuch;
@@ -2313,19 +2436,28 @@
 	ESPDATA(("hmuch<%d> ", hmuch));
 	esp->current_transfer_size = hmuch;
 	if(esp->erev == fashme) {
-		unsigned long tmp = dregs->cond_reg;
+		unsigned long tmp = esp->prev_hme_dmacsr;
 
-		/* Touchy chip, this stupid HME scsi adapter... */
+		/* Always set the ESP count registers first. */
 		esp_setcount(eregs, hmuch, 1);
-		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
-		dregs->cnt = hmuch;
+
+		/* Get the DMA csr computed. */
 		tmp |= (DMA_SCSI_DISAB | DMA_ENABLE);
-		if(thisphase == in_datain)
+		if (thisphase == in_datain)
 			tmp |= DMA_ST_WRITE;
 		else
 			tmp &= ~(DMA_ST_WRITE);
+		esp->prev_hme_dmacsr = tmp;
+
+		if (thisphase == in_datain) {
+			dregs->cnt = hmuch;
+			esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+		} else {
+			esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+			dregs->cnt = hmuch;
+		}
 		dregs->st_addr = ((__u32)((unsigned long)SCptr->SCp.ptr));
-		dregs->cond_reg = tmp;
+		dregs->cond_reg = esp->prev_hme_dmacsr;
 	} else {
 		esp_setcount(eregs, hmuch, 0);
 		dma_setup(dregs, esp->dma->revision,
@@ -2338,9 +2470,9 @@
 }
 
 /* See how successful the data transfer was. */
-static inline int esp_do_data_finale(struct Sparc_ESP *esp,
-				     struct Sparc_ESP_regs *eregs,
-				     struct sparc_dma_registers *dregs)
+static int esp_do_data_finale(struct Sparc_ESP *esp,
+			      struct Sparc_ESP_regs *eregs,
+			      struct sparc_dma_registers *dregs)
 {
 	Scsi_Cmnd *SCptr = esp->current_SC;
 	int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;
@@ -2361,7 +2493,7 @@
 		}
 		dma_drain(dregs, esp->dma->revision);
 	}
-	dma_invalidate(dregs, esp->dma->revision);
+	dma_invalidate(esp, dregs, esp->dma->revision);
 
 	/* This could happen for the above parity error case. */
 	if(!(esp->ireg == ESP_INTR_BSERV)) {
@@ -2387,26 +2519,43 @@
 		ecount = esp_getcount(eregs);
 	bytes_sent = esp->current_transfer_size;
 
-	/* Uhhh, might not want both of these conditionals to run
-	 * at once on HME due to the fifo problems it has.  Consider
-	 * changing it to:
-	 *
-	 * 	if(!(esp->sreg & ESP_STAT_TCNT)) {
-	 * 		bytes_sent -= ecount;
-	 * 	} else if(SCptr->SCp.phase == in_dataout) {
-	 * 		bytes_sent -= fifocnt;
-	 *	}
-	 *
-	 * But only for the HME case, leave the current code alone
-	 * for all other ESP revisions as we know the existing code
-	 * works just fine for them.
-	 */
 	ESPDATA(("trans_sz=%d, ", bytes_sent));
 	if(esp->erev == fashme) {
 		if(!(esp->sreg & ESP_STAT_TCNT)) {
-			bytes_sent -= esp_getcount(eregs);
-		} else if(SCptr->SCp.phase == in_dataout) {
+			ecount = esp_getcount(eregs);
+			bytes_sent -= ecount;
+		}
+
+		/* Always subtract any cruft remaining in the FIFO. */
+		if(esp->prev_cfg3 & ESP_CONFIG3_EWIDE)
+			fifocnt <<= 1;
+		if(SCptr->SCp.phase == in_dataout)
 			bytes_sent -= fifocnt;
+
+		/* I have an IBM disk which exhibits the following
+		 * behavior during writes to it.  It disconnects in
+		 * the middle of a partial transfer, the current sglist
+		 * buffer is 1024 bytes, the disk stops data transfer
+		 * at 512 bytes.
+		 *
+		 * However the FAS366 reports that 32 more bytes were
+		 * transferred than really were.  This is precisely
+		 * the size of a fully loaded FIFO in wide scsi mode.
+		 * The FIFO state recorded indicates that it is empty.
+		 *
+		 * I have no idea if this is a bug in the FAS366 chip
+		 * or a bug in the firmware on this IBM disk.  In any
+		 * event the following seems to be a good workaround.  -DaveM
+		 */
+		if (bytes_sent != esp->current_transfer_size &&
+		    SCptr->SCp.phase == in_dataout) {
+			int mask = (64 - 1);
+
+			if((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0)
+				mask >>= 1;
+
+			if (bytes_sent & mask)
+				bytes_sent -= (bytes_sent & mask);
 		}
 	} else {
 		if(!(esp->sreg & ESP_STAT_TCNT))
@@ -2442,6 +2591,8 @@
 		 * driver.  No idea why it happened, but allowing
 		 * this value to be negative caused things to
 		 * lock up.  This allows greater chance of recovery.
+		 * In fact every time I've seen this, it has been
+		 * a driver bug without question.
 		 */
 		ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
 		ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
@@ -2462,7 +2613,7 @@
 	SCptr->SCp.this_residual -= bytes_sent;
 	if(SCptr->SCp.this_residual < 0) {
 		/* shit */
-		printk("esp%d: Data transfer overrun.\n", esp->esp_id);
+		ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id));
 		SCptr->SCp.this_residual = 0;
 	}
 
@@ -2493,11 +2644,49 @@
 	return do_intr_end;
 }
 
+/* We received a non-good status return at the end of
+ * running a SCSI command.  This is used to decide if
+ * we should clear our synchronous transfer state for
+ * such a device when that happens.
+ *
+ * The idea is that when spinning up a disk or rewinding
+ * a tape, we don't want to go into a loop re-negotiating
+ * synchronous capabilities over and over.
+ */
+static int esp_should_clear_sync(Scsi_Cmnd *sp)
+{
+	unchar cmd1 = sp->cmnd[0];
+	unchar cmd2 = sp->data_cmnd[0];
+
+	/* These cases are for spinning up a disk and
+	 * waiting for that spinup to complete.
+	 */
+	if(cmd1 == START_STOP ||
+	   cmd2 == START_STOP)
+		return 0;
+
+	if(cmd1 == TEST_UNIT_READY ||
+	   cmd2 == TEST_UNIT_READY)
+		return 0;
+
+	/* One more special case for SCSI tape drives,
+	 * this is what is used to probe the device for
+	 * completion of a rewind or tape load operation.
+	 */
+	if(sp->device->type == TYPE_TAPE) {
+		if(cmd1 == MODE_SENSE ||
+		   cmd2 == MODE_SENSE)
+			return 0;
+	}
+
+	return 1;
+}
+
 /* Either a command is completing or a target is dropping off the bus
  * to continue the command in the background so we can do other work.
  */
-static inline int esp_do_freebus(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-				 struct sparc_dma_registers *dregs)
+static int esp_do_freebus(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			  struct sparc_dma_registers *dregs)
 {
 	Scsi_Cmnd *SCptr = esp->current_SC;
 	int rval;
@@ -2516,9 +2705,11 @@
 		if(esp->disconnected_SC || (esp->erev == fashme))
 			esp_cmd(esp, eregs, ESP_CMD_ESEL);
 
-		if(SCptr->SCp.Status != GOOD && SCptr->SCp.Status != CONDITION_GOOD &&
+		if(SCptr->SCp.Status != GOOD &&
+		   SCptr->SCp.Status != CONDITION_GOOD &&
 		   ((1<<SCptr->target) & esp->targets_present) &&
-		   SCptr->device->sync && SCptr->device->sync_max_offset) {
+		   SCptr->device->sync &&
+		   SCptr->device->sync_max_offset) {
 			/* SCSI standard says that the synchronous capabilities
 			 * should be renegotiated at this point.  Most likely
 			 * we are about to request sense from this target
@@ -2535,15 +2726,7 @@
 			 * can report not ready many times right after
 			 * loading up a tape.
 			 */
-			if(SCptr->cmnd[0] != START_STOP &&
-			   SCptr->data_cmnd[0] != START_STOP &&
-			   SCptr->cmnd[0] != TEST_UNIT_READY &&
-			   SCptr->data_cmnd[0] != TEST_UNIT_READY &&
-			   !(SCptr->device->type == TYPE_TAPE &&
-			     (SCptr->cmnd[0] == TEST_UNIT_READY ||
-			      SCptr->data_cmnd[0] == TEST_UNIT_READY ||
-			      SCptr->cmnd[0] == MODE_SENSE ||
-			      SCptr->data_cmnd[0] == MODE_SENSE)))
+			if(esp_should_clear_sync(SCptr) != 0)
 				SCptr->device->sync = 0;
 		}
 		ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun));
@@ -2570,9 +2753,43 @@
 	return do_intr_end;
 }
 
+/* When a reselect occurs, and we cannot find the command to
+ * reconnect to in our queues, we do this.
+ */
+static int esp_bad_reconnect(struct Sparc_ESP *esp)
+{
+	Scsi_Cmnd *sp;
+
+	ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
+		esp->esp_id));
+	ESPLOG(("QUEUE DUMP\n"));
+	sp = esp->issue_SC;
+	ESPLOG(("esp%d: issue_SC[", esp->esp_id));
+	while(sp) {
+		ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+		sp = (Scsi_Cmnd *) sp->host_scribble;
+	}
+	ESPLOG(("]\n"));
+	sp = esp->current_SC;
+	ESPLOG(("esp%d: current_SC[", esp->esp_id));
+	while(sp) {
+		ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+		sp = (Scsi_Cmnd *) sp->host_scribble;
+	}
+	ESPLOG(("]\n"));
+	sp = esp->disconnected_SC;
+	ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
+	while(sp) {
+		ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+		sp = (Scsi_Cmnd *) sp->host_scribble;
+	}
+	ESPLOG(("]\n"));
+	return do_reset_bus;
+}
+
 /* Do the needy when a target tries to reconnect to us. */
-static inline int esp_do_reconnect(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-				   struct sparc_dma_registers *dregs)
+static int esp_do_reconnect(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			    struct sparc_dma_registers *dregs)
 {
 	int lun, target;
 	Scsi_Cmnd *SCptr;
@@ -2592,12 +2809,8 @@
 	/* Things look ok... */
 	ESPDISC(("R<%02x,%02x>", target, lun));
 
-	/* Must flush both FIFO and the DVMA on HME. */
-	if(esp->erev == fashme) {
-		/* XXX this still doesn't fix the problem... */
-		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
-		dma_invalidate(esp->dregs, dvmahme);
-	} else {
+	/* Must not flush FIFO or DVMA on HME. */
+	if(esp->erev != fashme) {
 		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
 		if(esp100_reconnect_hwbug(esp, eregs))
 			return do_reset_bus;
@@ -2605,39 +2818,19 @@
 	}
 
 	SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun);
-	if(!SCptr) {
-		Scsi_Cmnd *sp;
+	if(!SCptr)
+		return esp_bad_reconnect(esp);
 
-		ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
-			esp->esp_id));
-		ESPLOG(("QUEUE DUMP\n"));
-		sp = esp->issue_SC;
-		ESPLOG(("esp%d: issue_SC[", esp->esp_id));
-		while(sp) {
-			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
-			sp = (Scsi_Cmnd *) sp->host_scribble;
-		}
-		ESPLOG(("]\n"));
-		sp = esp->current_SC;
-		ESPLOG(("esp%d: current_SC[", esp->esp_id));
-		while(sp) {
-			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
-			sp = (Scsi_Cmnd *) sp->host_scribble;
-		}
-		ESPLOG(("]\n"));
-		sp = esp->disconnected_SC;
-		ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
-		while(sp) {
-			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
-			sp = (Scsi_Cmnd *) sp->host_scribble;
-		}
-		ESPLOG(("]\n"));
-		return do_reset_bus;
-	}
 	esp_connect(esp, eregs, SCptr);
 	esp_cmd(esp, eregs, ESP_CMD_MOK);
 
-	/* No need for explicit restore pointers operation. */
+	if(esp->erev == fashme)
+		eregs->esp_busid = (SCptr->target & 0xf) |
+			(ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
+
+	/* Reconnect implies a restore pointers operation. */
+	esp_restore_pointers(esp, SCptr);
+
 	esp->snip = 0;
 	esp_advance_phase(SCptr, in_the_dark);
 	return do_intr_end;
@@ -2665,7 +2858,7 @@
 		esp_cmd(esp, eregs, ESP_CMD_MOK);
 
 		if(esp->erev != fashme) {
-			dma_flashclear(dregs, esp->dma->revision);
+			dma_flashclear(esp, dregs, esp->dma->revision);
 
 			/* Wait till the first bits settle. */
 			while(esp->esp_command[0] == 0xff)
@@ -2767,112 +2960,135 @@
 	}
 }
 
-/* The target has control of the bus and we have to see where it has
- * taken us.
- */
-static int esp_do_phase_determine(struct Sparc_ESP *esp,
-				  struct Sparc_ESP_regs *eregs,
-				  struct sparc_dma_registers *dregs)
+static int esp_enter_status(struct Sparc_ESP *esp,
+			    struct Sparc_ESP_regs *eregs,
+			    struct sparc_dma_registers *dregs)
 {
-	Scsi_Cmnd *SCptr = esp->current_SC;
+	unchar thecmd = ESP_CMD_ICCSEQ;
 
-	ESPPHASE(("esp_do_phase_determine: "));
-	if(!(esp->ireg & ESP_INTR_DC)) {
-		switch(esp->sreg & ESP_STAT_PMASK) {
-		case ESP_DOP:
-		case ESP_DIP:
-			ESPPHASE(("to data phase\n"));
-			return esp_do_data(esp, eregs, dregs);
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	if(esp->erev != fashme) {
+		esp->esp_command[0] = esp->esp_command[1] = 0xff;
+		eregs->esp_tclow = 2;
+		eregs->esp_tcmed = 0;
+		dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE);
+		if(esp->dma->revision == dvmaesc1)
+			dregs->cnt = 0x100;
+		dregs->st_addr = esp->esp_command_dvma;
+		thecmd |= ESP_CMD_DMA;
+	}
+	esp_cmd(esp, eregs, thecmd);
+	esp_advance_phase(esp->current_SC, in_status);
 
-		case ESP_STATP:
-			/* Whee, status phase, finish up the command. */
-			ESPPHASE(("to status phase\n"));
-			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
-			if(esp->erev != fashme) {
-				esp->esp_command[0] = 0xff;
-				esp->esp_command[1] = 0xff;
-				eregs->esp_tclow = 2;
-				eregs->esp_tcmed = 0;
-				dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE);
-				if(esp->dma->revision == dvmaesc1)
-					dregs->cnt = 0x1000;
-				dregs->st_addr = esp->esp_command_dvma;
-				esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ);
-			} else {
-				/* Using DVMA for status/message bytes is
-				 * unreliable on HME, nice job QLogic.
-				 * Happy Meal indeed....
-				 */
-				esp_cmd(esp, eregs, ESP_CMD_ICCSEQ);
-			}
-			esp_advance_phase(SCptr, in_status);
-			return esp_do_status(esp, eregs, dregs);
+	return esp_do_status(esp, eregs, dregs);
+}
 
-		case ESP_MOP:
-			ESPPHASE(("to msgout phase\n"));
-			esp_advance_phase(SCptr, in_msgout);
-			return esp_do_msgout(esp, eregs, dregs);
-
-		case ESP_MIP:
-			ESPPHASE(("to msgin phase\n"));
-			esp_advance_phase(SCptr, in_msgin);
-			return esp_do_msgin(esp, eregs, dregs);
-
-		case ESP_CMDP:
-			/* Ugh, we're running a non-standard command the
-			 * ESP doesn't understand, one byte at a time.
-			 */
-			ESPPHASE(("to cmd phase\n"));
-			esp_advance_phase(SCptr, in_cmdbegin);
-			return esp_do_cmdbegin(esp, eregs, dregs);
-		};
-	} else {
-		Scsi_Device *dp = SCptr->device;
+static int esp_disconnect_amidst_phases(struct Sparc_ESP *esp,
+					struct Sparc_ESP_regs *eregs,
+					struct sparc_dma_registers *dregs)
+{
+	Scsi_Cmnd *sp = esp->current_SC;
+	Scsi_Device *dp = sp->device;
 
-		/* This means real problems if we see this
-		 * here.  Unless we were actually trying
-		 * to force the device to abort/reset.
-		 */
-		ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
-		ESPLOG(("pphase<%s> cphase<%s>, ",
-			phase_string(SCptr->SCp.phase),
-			phase_string(SCptr->SCp.sent_command)));
-		if(esp->disconnected_SC || (esp->erev == fashme))
-			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+	/* This means real problems if we see this
+	 * here.  Unless we were actually trying
+	 * to force the device to abort/reset.
+	 */
+	ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
+	ESPLOG(("pphase<%s> cphase<%s>, ",
+		phase_string(sp->SCp.phase),
+		phase_string(sp->SCp.sent_command)));
 
-		switch(esp->cur_msgout[0]) {
-		default:
-			/* We didn't expect this to happen at all. */
-			ESPLOG(("device is bolixed\n"));
-			esp_advance_phase(SCptr, in_tgterror);
-			esp_done(esp, (DID_ERROR << 16));
-			break;
+	if(esp->disconnected_SC || (esp->erev == fashme))
+		esp_cmd(esp, eregs, ESP_CMD_ESEL);
 
-		case BUS_DEVICE_RESET:
-			ESPLOG(("device reset successful\n"));
-			dp->sync_max_offset = 0;
-			dp->sync_min_period = 0;
-			dp->sync = 0;
-			esp_advance_phase(SCptr, in_resetdev);
-			esp_done(esp, (DID_RESET << 16));
-			break;
+	switch(esp->cur_msgout[0]) {
+	default:
+		/* We didn't expect this to happen at all. */
+		ESPLOG(("device is bolixed\n"));
+		esp_advance_phase(sp, in_tgterror);
+		esp_done(esp, (DID_ERROR << 16));
+		break;
 
-		case ABORT:
-			ESPLOG(("device abort successful\n"));
-			esp_advance_phase(SCptr, in_abortone);
-			esp_done(esp, (DID_ABORT << 16));
-			break;
+	case BUS_DEVICE_RESET:
+		ESPLOG(("device reset successful\n"));
+		dp->sync_max_offset = 0;
+		dp->sync_min_period = 0;
+		dp->sync = 0;
+		esp_advance_phase(sp, in_resetdev);
+		esp_done(esp, (DID_RESET << 16));
+		break;
 
-		};
-		return do_intr_end;
-	}
+	case ABORT:
+		ESPLOG(("device abort successful\n"));
+		esp_advance_phase(sp, in_abortone);
+		esp_done(esp, (DID_ABORT << 16));
+		break;
+
+	};
+	return do_intr_end;
+}
+
+static int esp_enter_msgout(struct Sparc_ESP *esp,
+			    struct Sparc_ESP_regs *eregs,
+			    struct sparc_dma_registers *dregs)
+{
+	esp_advance_phase(esp->current_SC, in_msgout);
+	return esp_do_msgout(esp, eregs, dregs);
+}
+
+static int esp_enter_msgin(struct Sparc_ESP *esp,
+			   struct Sparc_ESP_regs *eregs,
+			   struct sparc_dma_registers *dregs)
+{
+	esp_advance_phase(esp->current_SC, in_msgin);
+	return esp_do_msgin(esp, eregs, dregs);
+}
 
-	ESPLOG(("esp%d: to unknown phase\n", esp->esp_id));
-	printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
-	       esp->sreg & ESP_STAT_PMASK);
+static int esp_enter_cmd(struct Sparc_ESP *esp,
+			 struct Sparc_ESP_regs *eregs,
+			 struct sparc_dma_registers *dregs)
+{
+	esp_advance_phase(esp->current_SC, in_cmdbegin);
+	return esp_do_cmdbegin(esp, eregs, dregs);
+}
+
+static int esp_enter_badphase(struct Sparc_ESP *esp,
+			      struct Sparc_ESP_regs *eregs,
+			      struct sparc_dma_registers *dregs)
+{
+	ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
+		esp->sreg & ESP_STAT_PMASK));
 	return do_reset_bus;
 }
 
+typedef int (*espfunc_t)(struct Sparc_ESP *,
+			 struct Sparc_ESP_regs *,
+			 struct sparc_dma_registers *);
+
+static espfunc_t phase_vector[] = {
+	esp_do_data,		/* ESP_DOP */
+	esp_do_data,		/* ESP_DIP */
+	esp_enter_cmd,		/* ESP_CMDP */
+	esp_enter_status,	/* ESP_STATP */
+	esp_enter_badphase,	/* ESP_STAT_PMSG */
+	esp_enter_badphase,	/* ESP_STAT_PMSG | ESP_STAT_PIO */
+	esp_enter_msgout,	/* ESP_MOP */
+	esp_enter_msgin,	/* ESP_MIP */
+};
+
+/* The target has control of the bus and we have to see where it has
+ * taken us.
+ */
+static int esp_do_phase_determine(struct Sparc_ESP *esp,
+				  struct Sparc_ESP_regs *eregs,
+				  struct sparc_dma_registers *dregs)
+{
+	if ((esp->ireg & ESP_INTR_DC) != 0)
+		return esp_disconnect_amidst_phases(esp, eregs, dregs);
+	return phase_vector[esp->sreg & ESP_STAT_PMASK](esp, eregs, dregs);
+}
+
 /* First interrupt after exec'ing a cmd comes here. */
 static int esp_select_complete(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
 			       struct sparc_dma_registers *dregs)
@@ -2888,7 +3104,7 @@
 	else
 		fcnt = (eregs->esp_fflags & ESP_FF_FBYTES);
 	cmd_bytes_sent = esp_bytes_sent(esp, dregs, fcnt);
-	dma_invalidate(dregs, esp->dma->revision);
+	dma_invalidate(esp, dregs, esp->dma->revision);
 
 	/* Let's check to see if a reselect happened
 	 * while we we're trying to select.  This must
@@ -3061,9 +3277,8 @@
 		    SCptr->SCp.phase == in_slct_stop)) {
 			/* shit */
 			esp->snip = 0;
-			printk("esp%d: Failed synchronous negotiation for target %d "
-			       "lun %d\n",
-			       esp->esp_id, SCptr->target, SCptr->lun);
+			ESPLOG(("esp%d: Failed synchronous negotiation for target %d "
+				"lun %d\n", esp->esp_id, SCptr->target, SCptr->lun));
 			SDptr->sync_max_offset = 0;
 			SDptr->sync_min_period = 0;
 			SDptr->sync = 1; /* so we don't negotiate again */
@@ -3090,8 +3305,8 @@
 		 * But first make sure that is really what is happening.
 		 */
 		if(((1<<SCptr->target) & esp->targets_present)) {
-			printk("esp%d: Warning, live target %d not responding to "
-			       "selection.\n", esp->esp_id, SCptr->target);
+			ESPLOG(("esp%d: Warning, live target %d not responding to "
+				"selection.\n", esp->esp_id, SCptr->target));
 
 			/* This _CAN_ happen.  The SCSI standard states that
 			 * the target is to _not_ respond to selection if
@@ -3154,15 +3369,15 @@
 		 * have miscoded something..... so, try to
 		 * recover as best we can.
 		 */
-		printk("esp%d: message in mis-carriage.\n", esp->esp_id);
+		ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id));
 	}
 	esp_advance_phase(esp->current_SC, in_the_dark);
 	return do_phase_determine;
 }
 
-static inline int check_singlebyte_msg(struct Sparc_ESP *esp,
-				       struct Sparc_ESP_regs *eregs,
-				       struct sparc_dma_registers *dregs)
+static int check_singlebyte_msg(struct Sparc_ESP *esp,
+				struct Sparc_ESP_regs *eregs,
+				struct sparc_dma_registers *dregs)
 {
 	esp->prevmsgin = esp->cur_msgin[0];
 	if(esp->cur_msgin[0] & 0x80) {
@@ -3191,8 +3406,20 @@
 		return 0;
 
 	case RESTORE_POINTERS:
+		/* In this case we might also have to backup the
+		 * "slow command" pointer.  It is rare to get such
+		 * a save/restore pointer sequence so early in the
+		 * bus transition sequences, but cover it.
+		 */
+		if(esp->esp_slowcmd) {
+			esp->esp_scmdleft = esp->current_SC->cmd_len;
+			esp->esp_scmdp = &esp->current_SC->cmnd[0];
+		}
+		esp_restore_pointers(esp, esp->current_SC);
+		return 0;
+
 	case SAVE_POINTERS:
-		/* We handle this all automatically. */
+		esp_save_pointers(esp, esp->current_SC);
 		return 0;
 
 	case COMMAND_COMPLETE:
@@ -3227,9 +3454,9 @@
  * the SCSI2 standard specifically recommends against targets doing
  * this because so many initiators cannot cope with this occuring.
  */
-static inline int target_with_ants_in_pants(struct Sparc_ESP *esp,
-					    Scsi_Cmnd *SCptr,
-					    Scsi_Device *SDptr)
+static int target_with_ants_in_pants(struct Sparc_ESP *esp,
+				     Scsi_Cmnd *SCptr,
+				     Scsi_Device *SDptr)
 {
 	if(SDptr->sync || SDptr->borken) {
 		/* sorry, no can do */
@@ -3246,7 +3473,7 @@
 	return 0;
 }
 
-static inline void sync_report(struct Sparc_ESP *esp)
+static void sync_report(struct Sparc_ESP *esp)
 {
 	int msg3, msg4;
 	char *type;
@@ -3267,21 +3494,27 @@
 		} else {
 			type = "synchronous";
 		}
-		printk(KERN_INFO "esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n",
-		       esp->esp_id, esp->current_SC->target,
-		       (int) msg3 * 4,
-		       (int) msg4,
-		       integer, fraction, type,
-		       (((msg3 * 4) < 200) ? "-II" : ""));
+
+		/* Do not transform this back into one big printk
+		 * again, it triggers a bug in our sparc64-gcc272
+		 * sibling call optimization.  -DaveM
+		 */
+		ESPLOG((KERN_INFO "esp%d: target %d ",
+			esp->esp_id, esp->current_SC->target));
+		ESPLOG(("[period %dns offset %d %d.%02dMHz ",
+			(int) msg3 * 4, (int) msg4,
+			integer, fraction));
+		ESPLOG(("%s SCSI%s]\n", type,
+			(((msg3 * 4) < 200) ? "-II" : "")));
 	} else {
-		printk(KERN_INFO "esp%d: target %d asynchronous\n",
-		       esp->esp_id, esp->current_SC->target);
+		ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n",
+			esp->esp_id, esp->current_SC->target));
 	}
 }
 
-static inline int check_multibyte_msg(struct Sparc_ESP *esp,
-				       struct Sparc_ESP_regs *eregs,
-				       struct sparc_dma_registers *dregs)
+static int check_multibyte_msg(struct Sparc_ESP *esp,
+			       struct Sparc_ESP_regs *eregs,
+			       struct sparc_dma_registers *dregs)
 {
 	Scsi_Cmnd *SCptr = esp->current_SC;
 	Scsi_Device *SDptr = SCptr->device;
@@ -3358,10 +3591,10 @@
 					esp->config3[SCptr->target] |= bit;
 				else
 					esp->config3[SCptr->target] &= ~bit;
-				eregs->esp_cfg3 = esp->config3[SCptr->target];
+				eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[SCptr->target];
 			}
-			eregs->esp_soff = SDptr->sync_min_period;
-			eregs->esp_stp  = SDptr->sync_max_offset;
+			eregs->esp_soff = esp->prev_soff = SDptr->sync_min_period;
+			eregs->esp_stp  = esp->prev_stp  = SDptr->sync_max_offset;
 
 			ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
 				SDptr->sync_max_offset,
@@ -3376,15 +3609,15 @@
 			ESPSDTR(("unaccaptable sync nego, forcing async\n"));
 			SDptr->sync_max_offset = 0;
 			SDptr->sync_min_period = 0;
-			eregs->esp_soff = 0;
-			eregs->esp_stp = 0;
+			eregs->esp_soff = esp->prev_soff = 0;
+			eregs->esp_stp = esp->prev_stp = 0;
 			if((esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme)) {
 				if((esp->erev == fas100a) || (esp->erev == fashme))
 					bit = ESP_CONFIG3_FAST;
 				else
 					bit = ESP_CONFIG3_FSCSI;
 				esp->config3[SCptr->target] &= ~bit;
-				eregs->esp_cfg3 = esp->config3[SCptr->target];
+				eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[SCptr->target];
 			}
 		}
 
@@ -3409,32 +3642,30 @@
 
 		esp->wnip = 0;
 		if(esp->erev != fashme) {
-			printk("esp%d: AIEEE wide msg received and not HME.\n",
-			       esp->esp_id);
+			ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n",
+				esp->esp_id));
 			message_out = MESSAGE_REJECT;
 		} else if(size > 16) {
-			printk("esp%d: AIEEE wide transfer for %d size not supported.\n",
-			       esp->esp_id, size);
+			ESPLOG(("esp%d: AIEEE wide transfer for %d size "
+				"not supported.\n", esp->esp_id, size));
 			message_out = MESSAGE_REJECT;
 		} else {
 			/* Things look good; let's see what we got. */
 			if(size == 16) {
 				/* Set config 3 register for this target. */
-				printk("esp%d: 16 byte WIDE transfers enabled for target %d.\n",
-				       esp->esp_id, SCptr->target);
 				esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE;
 			} else {
 				/* Just make sure it was one byte sized. */
 				if(size != 8) {
-					printk("esp%d: Aieee, wide nego of %d size.\n",
-					       esp->esp_id, size);
+					ESPLOG(("esp%d: Aieee, wide nego of %d size.\n",
+						esp->esp_id, size));
 					message_out = MESSAGE_REJECT;
 					goto finish;
 				}
 				/* Pure paranoia. */
 				esp->config3[SCptr->target] &= ~(ESP_CONFIG3_EWIDE);
 			}
-			eregs->esp_cfg3 = esp->config3[SCptr->target];
+			eregs->esp_cfg3 = esp->prev_cfg3 = esp->config3[SCptr->target];
 
 			/* Regardless, next try for sync transfers. */
 			build_sync_nego_msg(esp, esp->sync_defp, 15);
@@ -3566,11 +3797,11 @@
 	return do_intr_end;
 }
 
-static inline int esp_do_cmddone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-				 struct sparc_dma_registers *dregs)
+static int esp_do_cmddone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			  struct sparc_dma_registers *dregs)
 {
 	if(esp->erev == fashme)
-		dma_invalidate(dregs, dvmahme);
+		dma_invalidate(esp, dregs, dvmahme);
 	else
 		esp_cmd(esp, eregs, ESP_CMD_NULL);
 	if(esp->ireg & ESP_INTR_BSERV) {
@@ -3583,7 +3814,7 @@
 }
 
 static int esp_do_msgout(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-				struct sparc_dma_registers *dregs)
+			 struct sparc_dma_registers *dregs)
 {
 	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
 	switch(esp->msgout_len) {
@@ -3661,8 +3892,8 @@
 	return do_intr_end;
 }
 
-static inline int esp_do_msgoutdone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-				    struct sparc_dma_registers *dregs)
+static int esp_do_msgoutdone(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			     struct sparc_dma_registers *dregs)
 {
 	if(esp->msgout_len > 1) {
 		/* XXX HME/FAS ATN deassert workaround required,
@@ -3673,7 +3904,7 @@
 			while(dregs->cond_reg & DMA_PEND_READ)
 				udelay(1);
 			dregs->cond_reg &= ~(DMA_ENABLE);
-			dma_invalidate(dregs, esp->dma->revision);
+			dma_invalidate(esp, dregs, esp->dma->revision);
 		} else {
 			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
 		}
@@ -3722,81 +3953,65 @@
 	return esp_do_phase_determine(esp, eregs, dregs);
 }
 
+static int esp_bus_unexpected(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
+			      struct sparc_dma_registers *dregs)
+{
+	ESPLOG(("esp%d: command in weird state %2x\n",
+		esp->esp_id, esp->current_SC->SCp.phase));
+	return do_reset_bus;
+}
+
+static espfunc_t bus_vector[] = {
+	esp_do_data_finale,
+	esp_do_data_finale,
+	esp_bus_unexpected,
+	esp_do_msgin,
+	esp_do_msgincont,
+	esp_do_msgindone,
+	esp_do_msgout,
+	esp_do_msgoutdone,
+	esp_do_cmdbegin,
+	esp_do_cmddone,
+	esp_do_status,
+	esp_do_freebus,
+	esp_do_phase_determine,
+	esp_bus_unexpected,
+	esp_bus_unexpected,
+	esp_bus_unexpected,
+};
+
 /* This is the second tier in our dual-level SCSI state machine. */
-static inline int esp_work_bus(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs,
-			       struct sparc_dma_registers *dregs)
+static int esp_work_bus(struct Sparc_ESP *esp,
+			struct Sparc_ESP_regs *eregs,
+			struct sparc_dma_registers *dregs)
 {
 	Scsi_Cmnd *SCptr = esp->current_SC;
+	unsigned int phase;
 
 	ESPBUS(("esp_work_bus: "));
 	if(!SCptr) {
 		ESPBUS(("reconnect\n"));
 		return esp_do_reconnect(esp, eregs, dregs);
 	}
-
-	switch(SCptr->SCp.phase) {
-	case in_the_dark:
-		ESPBUS(("in the dark\n"));
-		return esp_do_phase_determine(esp, eregs, dregs);
-
-	case in_slct_norm:
-	case in_slct_stop:
-	case in_slct_msg:
-	case in_slct_tag:
-	case in_slct_sneg:
-		ESPBUS(("finish selection\n"));
+	phase = SCptr->SCp.phase;
+	if ((phase & 0xf0) == in_phases_mask)
+		return bus_vector[(phase & 0x0f)](esp, eregs, dregs);
+	else if((phase & 0xf0) == in_slct_mask)
 		return esp_select_complete(esp, eregs, dregs);
-
-	case in_datain:
-	case in_dataout:
-		ESPBUS(("finish data\n"));
-		return esp_do_data_finale(esp, eregs, dregs);
-
-	case in_msgout:
-		ESPBUS(("message out "));
-		return esp_do_msgout(esp, eregs, dregs);
-
-	case in_msgoutdone:
-		ESPBUS(("finish message out "));
-		return esp_do_msgoutdone(esp, eregs, dregs);
-
-	case in_msgin:
-		ESPBUS(("message in "));
-		return esp_do_msgin(esp, eregs, dregs);
-
-	case in_msgincont:
-		ESPBUS(("continue message in "));
-		return esp_do_msgincont(esp, eregs, dregs);
-
-	case in_msgindone:
-		ESPBUS(("finish message in "));
-		return esp_do_msgindone(esp, eregs, dregs);
-
-	case in_status:
-		ESPBUS(("status phase "));
-		return esp_do_status(esp, eregs, dregs);
-
-	case in_freeing:
-		ESPBUS(("freeing the bus "));
-		return esp_do_freebus(esp, eregs, dregs);
-
-	case in_cmdbegin:
-		ESPBUS(("begin slow cmd "));
-		return esp_do_cmdbegin(esp, eregs, dregs);
-
-	case in_cmdend:
-		ESPBUS(("end slow cmd "));
-		return esp_do_cmddone(esp, eregs, dregs);
-
-	default:
-		printk("esp%d: command in weird state %2x\n",
-		       esp->esp_id, esp->current_SC->SCp.phase);
-		return do_reset_bus;
-	};
+	else
+		return esp_bus_unexpected(esp, eregs, dregs);
 }
 
+static espfunc_t isvc_vector[] = {
+	0,
+	esp_do_phase_determine,
+	esp_do_resetbus,
+	esp_finish_reset,
+	esp_work_bus
+};
+
 /* Main interrupt handler for an esp adapter. */
-static inline void esp_handle(struct Sparc_ESP *esp)
+static void esp_handle(struct Sparc_ESP *esp)
 {
 	struct sparc_dma_registers *dregs;
 	struct Sparc_ESP_regs *eregs;
@@ -3841,7 +4056,7 @@
 			ESPLOG(("esp%d: No current cmd during gross error, "
 				"resetting bus\n", esp->esp_id));
 			what_next = do_reset_bus;
-			goto again;
+			goto state_machine;
 		}
 	}
 
@@ -3862,9 +4077,11 @@
 		esp_reset_dma(esp);
 
 		what_next = do_reset_bus;
-		goto again;
+		goto state_machine;
 	}
 
+	esp->ireg = eregs->esp_intrpt;   /* Unlatch intr and stat regs */
+
 	if(esp->erev == fashme) {
 		/* This chip is really losing. */
 		ESPHME(("HME["));
@@ -3877,17 +4094,12 @@
 		if(!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
 		   (esp->sreg2 & ESP_STAT2_F1BYTE)) {
 			ESPHME(("fifo_workaround]"));
-			hme_fifo_hwbug_workaround(esp, eregs);
+			hme_fifo_read(esp, eregs);
 		} else {
 			ESPHME(("no_fifo_workaround]"));
 		}
 	}
 
-	esp->ireg = eregs->esp_intrpt;   /* Unlatch intr and stat regs */
-
-	/* This cannot be done until this very moment. -DaveM */
-	synchronize_irq();
-
 	/* No current cmd is only valid at this point when there are
 	 * commands off the bus or we are trying a reset.
 	 */
@@ -3926,10 +4138,7 @@
 		}
 
 		what_next = do_reset_bus;
-		goto again;
-	}
-
-	if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
+	} else if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
 		int phase;
 
 		if(SCptr) {
@@ -3941,13 +4150,12 @@
 			} else {
 				ESPLOG(("esp%d: interrupt for no good reason...\n",
 					esp->esp_id));
-				goto esp_handle_done;
+				what_next = do_intr_end;
 			}
 		} else {
 			ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
 				esp->esp_id));
 			what_next = do_reset_bus;
-			goto again;
 		}
 	} else if(esp->ireg & ESP_INTR_SR) {
 		ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
@@ -3956,7 +4164,6 @@
 		ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
 			esp->esp_id));
 		what_next = do_reset_bus;
-		goto again;
 	} else if(esp->ireg & ESP_INTR_RSEL) {
 		if(!SCptr) {
 			/* This is ok. */
@@ -3971,95 +4178,22 @@
 			ESPLOG(("esp%d: Reselected while bus is busy\n",
 				esp->esp_id));
 			what_next = do_reset_bus;
-			goto again;
 		}
 	}
 
-	/* We're trying to fight stack problems, and inline as much as
-	 * possible without making this driver a mess. hate hate hate
-	 * This is tier-one in our dual level SCSI state machine.
-	 */
-again:
-	switch(what_next) {
-	case do_intr_end:
-		goto esp_handle_done;
-
-	case do_work_bus:
-		what_next = esp_work_bus(esp, eregs, dregs);
-		break;
-
-	case do_phase_determine:
-		what_next = esp_do_phase_determine(esp, eregs, dregs);
-		break;
-
-	case do_reset_bus:
-		ESPLOG(("esp%d: resetting bus...\n", esp->esp_id));
-		esp->resetting_bus = 1;
-		esp_cmd(esp, eregs, ESP_CMD_RS);
-		goto esp_handle_done;
-
-	case do_reset_complete:
-		/* Tricky, we don't want to cause any more commands to
-		 * go out until we clear all the live cmds by hand.
-		 */
-		if(esp->current_SC) {
-			Scsi_Cmnd *SCptr = esp->current_SC;
-			if(!SCptr->use_sg)
-				mmu_release_scsi_one(SCptr->SCp.have_data_in,
-						     SCptr->request_bufflen,
-						     esp->edev->my_bus);
-			else
-				mmu_release_scsi_sgl((struct mmu_sglist *)
-						     SCptr->buffer,
-						     SCptr->use_sg - 1,
-						     esp->edev->my_bus);
-			SCptr->result = (DID_RESET << 16);
-
-			SCptr->scsi_done(SCptr);
-		}
-		esp->current_SC = NULL;
-		if(esp->disconnected_SC) {
-			Scsi_Cmnd *SCptr;
-			while((SCptr = remove_first_SC(&esp->disconnected_SC))) {
-				if(!SCptr->use_sg)
-					mmu_release_scsi_one(SCptr->SCp.have_data_in,
-							     SCptr->request_bufflen,
-							     esp->edev->my_bus);
-				else
-					mmu_release_scsi_sgl((struct mmu_sglist *)
-							     SCptr->buffer,
-							     SCptr->use_sg - 1,
-							     esp->edev->my_bus);
-				SCptr->result = (DID_RESET << 16);
-
-				SCptr->scsi_done(SCptr);
-			}
-		}
-		esp->resetting_bus = 0;
-
-		if(esp->current_SC) {
-			printk("esp%d: weird weird weird, current_SC not NULL after "
-			       "SCSI bus reset.\n", esp->esp_id);
-			goto esp_handle_done;
+	/* This is tier-one in our dual level SCSI state machine. */
+state_machine:
+	while(what_next != do_intr_end) {
+		if (what_next >= do_phase_determine &&
+		    what_next < do_intr_end)
+			what_next = isvc_vector[what_next](esp, eregs, dregs);
+		else {
+			/* state is completely lost ;-( */
+			ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
 		}
-
-		/* Now it is safe to execute more things. */
-		if(esp->issue_SC)
-			esp_exec_cmd(esp);
-		goto esp_handle_done;
-
-	default:
-		/* state is completely lost ;-( */
-		ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
-			esp->esp_id));
-		what_next = do_reset_bus;
-		break;
-
-	};
-	goto again;
-
-esp_handle_done:
-	return;
+	}
 }
 
 #ifndef __sparc_v9__
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/esp.h linux/drivers/scsi/esp.h
--- v2.2.3/linux/drivers/scsi/esp.h	Thu Aug  6 14:06:33 1998
+++ linux/drivers/scsi/esp.h	Sun Mar 21 14:19:07 1999
@@ -123,10 +123,31 @@
   unchar seqreg;                          /* The ESP sequence register */
   unchar sreg2;                           /* Copy of HME status2 register */
 
+  /* To save register writes to the ESP, which can be expensive, we
+   * keep track of the previous value that various registers had for
+   * the last target we connected to.  If they are the same for the
+   * current target, we skip the register writes as they are not needed.
+   */
+  unchar prev_soff, prev_stp, prev_cfg3, __cache_pad;
+
+  /* We also keep a cache of the previous FAS/HME DMA CSR register value.  */
+  unsigned int prev_hme_dmacsr;
+
   /* The HME is the biggest piece of shit I have ever seen. */
   unchar hme_fifo_workaround_buffer[16 * 2]; /* 16-bit/entry fifo for wide scsi */
   unchar hme_fifo_workaround_count;
 
+  /* For each target we keep track of save/restore data
+   * pointer information.  This needs to be updated majorly
+   * when we add support for tagged queueing.  -DaveM
+   */
+  struct esp_pointers {
+	  char *saved_ptr;
+	  struct scatterlist *saved_buffer;
+	  int saved_this_residual;
+	  int saved_buffers_residual;
+  } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/;
+
   /* Clock periods, frequencies, synchronization, etc. */
   unsigned int cfreq;                    /* Clock frequency in HZ */
   unsigned int cfact;                    /* Clock conversion factor */
@@ -217,7 +238,7 @@
 #define ESP_CONFIG3_IDMSG     0x10             /* ID message checking  (esp100a/hme) */
 #define ESP_CONFIG3_FSCSI     0x10             /* Enable FAST SCSI     (esp/fas236)  */
 #define ESP_CONFIG3_GTM       0x20             /* group2 SCSI2 support (esp/fas236)  */
-#define ESP_CONFIG3_BIGID     0x20             /* SCSI-ID's are 4bits  (hme)         */
+#define ESP_CONFIG3_IDBIT3    0x20             /* Bit 3 of HME SCSI-ID (hme)         */
 #define ESP_CONFIG3_TBMS      0x40             /* Three-byte msg's ok  (esp/fas236)  */
 #define ESP_CONFIG3_EWIDE     0x40             /* Enable Wide-SCSI     (hme)         */
 #define ESP_CONFIG3_IMS       0x80             /* ID msg chk'ng        (esp/fas236)  */
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/fcal.c linux/drivers/scsi/fcal.c
--- v2.2.3/linux/drivers/scsi/fcal.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/fcal.c	Mon Mar 15 16:11:31 1999
@@ -0,0 +1,307 @@
+/* fcal.c: Fibre Channel Arbitrated Loop SCSI host adapter driver.
+ *
+ * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "../fc4/fcp_impl.h"
+#include "fcal.h"
+
+#include <linux/module.h>
+
+/* #define FCAL_DEBUG */
+
+#define fcal_printk printk ("FCAL %s: ", fc->name); printk
+
+#ifdef FCAL_DEBUG
+#define FCALD(x)  fcal_printk x;
+#define FCALND(x) printk ("FCAL: "); printk x;
+#else
+#define FCALD(x)
+#define FCALND(x)
+#endif
+
+static unsigned char alpa2target[] = {
+0x7e, 0x7d, 0x7c, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x7a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79,
+0x78, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0x76, 0xff, 0xff, 0x75, 0xff, 0x74, 0x73, 0x72,
+0xff, 0xff, 0xff, 0x71, 0xff, 0x70, 0x6f, 0x6e, 0xff, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0xff,
+0xff, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0xff, 0xff, 0x61, 0x60, 0xff, 0x5f, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x5e, 0xff, 0x5d, 0x5c, 0x5b, 0xff, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0xff,
+0xff, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0xff, 0xff, 0x4e, 0x4d, 0xff, 0x4c, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x4b, 0xff, 0x4a, 0x49, 0x48, 0xff, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0xff,
+0xff, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0xff, 0xff, 0x3b, 0x3a, 0xff, 0x39, 0xff, 0xff, 0xff,
+0x38, 0x37, 0x36, 0xff, 0x35, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33,
+0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x30, 0xff, 0xff, 0x2f, 0xff, 0x2e, 0x2d, 0x2c,
+0xff, 0xff, 0xff, 0x2b, 0xff, 0x2a, 0x29, 0x28, 0xff, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0xff,
+0xff, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0xff, 0xff, 0x1b, 0x1a, 0xff, 0x19, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0x18, 0xff, 0x17, 0x16, 0x15, 0xff, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0xff,
+0xff, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0xff, 0xff, 0x08, 0x07, 0xff, 0x06, 0xff, 0xff, 0xff,
+0x05, 0x04, 0x03, 0xff, 0x02, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
+};
+
+static unsigned char target2alpa[] = {
+0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce,
+0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5, 0xb4, 0xb3,
+0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b,
+0x98, 0x97, 0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79, 0x76, 0x75, 0x74, 0x73,
+0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
+0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3c,
+0x3a, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x27, 0x26,
+0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17, 0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00
+};
+
+struct proc_dir_entry proc_scsi_fcal = {
+	PROC_SCSI_FCAL, 4, "fcal",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd);
+
+static void fcal_select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist)
+{
+	Scsi_Device *device;
+	
+	for (device = devlist; device; device = device->next) {
+		if (device->host != host) continue;
+		if (device->tagged_supported)
+			device->queue_depth = /* 254 */ 8;
+		else
+			device->queue_depth = 2;
+	}
+}
+
+/* Detect all FC Arbitrated Loops attached to the machine.
+   fc4 module has done all the work for us... */
+__initfunc(int fcal_detect(Scsi_Host_Template *tpnt))
+{
+	int nfcals = 0;
+	fc_channel *fc;
+	int fcalcount;
+	int i;
+
+	tpnt->proc_dir = &proc_scsi_fcal;
+	fcalcount = 0;
+	for_each_online_fc_channel(fc)
+		if (fc->posmap)
+			fcalcount++;
+	FCALND(("%d channels online\n", fcalcount))
+	if (!fcalcount) {
+#if defined(MODULE) && defined(CONFIG_FC4_SOCAL_MODULE) && defined(CONFIG_KMOD)
+		request_module("socal");
+		
+		for_each_online_fc_channel(fc)
+			if (fc->posmap)
+				fcalcount++;
+		if (!fcalcount)
+#endif
+			return 0;
+	}
+	for_each_online_fc_channel(fc) {
+		struct Scsi_Host *host;
+		long *ages;
+		struct fcal *fcal;
+		
+		if (!fc->posmap) continue;
+		
+		/* Strange, this is already registered to some other SCSI host, then it cannot be fcal */
+		if (fc->scsi_name[0]) continue;
+		memcpy (fc->scsi_name, "FCAL", 4);
+		
+		fc->can_queue = FCAL_CAN_QUEUE;
+		fc->rsp_size = 64;
+		fc->encode_addr = fcal_encode_addr;
+		
+		ages = kmalloc (128 * sizeof(long), GFP_KERNEL);
+		if (!ages) continue;
+				
+		host = scsi_register (tpnt, sizeof (struct fcal));
+		if (!host) panic ("Cannot register FCAL host\n");
+				
+		nfcals++;
+				
+		if (fc->module) __MOD_INC_USE_COUNT(fc->module);
+				
+		fcal = (struct fcal *)host->hostdata;
+		
+		fc->fcp_register(fc, TYPE_SCSI_FCP, 0);
+
+		for (i = 0; i < fc->posmap->len; i++) {
+			int status, target, alpa;
+
+			alpa = fc->posmap->list[i];			
+			FCALD(("Sending PLOGI to %02x\n", alpa))
+			target = alpa2target[alpa];
+			status = fc_do_plogi(fc, alpa, fcal->node_wwn + target, 
+					     fcal->nport_wwn + target);
+			FCALD(("PLOGI returned with status %d\n", status))
+			if (status != FC_STATUS_OK)
+				continue;
+			FCALD(("Sending PRLI to %02x\n", alpa))
+			status = fc_do_prli(fc, alpa);
+			FCALD(("PRLI returned with status %d\n", status))
+			if (status == FC_STATUS_OK)
+				fcal->map[target] = 1;
+		}
+		
+		host->max_id = 127;
+		host->irq = fc->irq;
+#ifdef __sparc_v9__
+		host->unchecked_isa_dma = 1;
+#endif
+		host->select_queue_depths = fcal_select_queue_depths;
+
+		fc->channels = 1;
+		fc->targets = 127;
+		fc->ages = ages;
+		memset (ages, 0, 128 * sizeof(long));
+				
+		fcal->fc = fc;
+		
+		FCALD(("Found FCAL\n"))
+	}
+	if (nfcals)
+#ifdef __sparc__
+		printk ("FCAL: Total of %d Sun Enterprise Network Array (A5000 or EX500) channels found\n", nfcals);
+#else
+		printk ("FCAL: Total of %d Fibre Channel Arbitrated Loops found\n", nfcals);
+#endif
+	return nfcals;
+}
+
+int fcal_release(struct Scsi_Host *host)
+{
+	struct fcal *fcal = (struct fcal *)host->hostdata;
+	fc_channel *fc = fcal->fc;
+
+	if (fc->module) __MOD_DEC_USE_COUNT(fc->module);
+	
+	fc->fcp_register(fc, TYPE_SCSI_FCP, 1);
+	FCALND((" releasing fcal.\n"));
+	kfree (fc->ages);
+	FCALND(("released fcal!\n"));
+	return 0;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int fcal_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+	struct Scsi_Host *host = NULL;
+	struct fcal *fcal;
+	fc_channel *fc;
+	char *pos = buffer;
+	int i, j;
+
+	for (host=scsi_hostlist; host; host=host->next)
+		if (host->host_no == hostno)
+                      break;
+
+	if (!host) return -ESRCH;
+
+	if (inout) return length;
+    
+	fcal = (struct fcal *)host->hostdata;
+	fc = fcal->fc;
+
+#ifdef __sparc__
+	SPRINTF ("Sun Enterprise Network Array (A5000 or E?500) on %s\n", fc->name);
+#else
+	SPRINTF ("Fibre Channel Arbitrated Loop on %s\n", fc->name);
+#endif
+	SPRINTF ("Initiator AL-PA: %02x\n", fc->sid);
+
+	SPRINTF ("\nAttached devices: %s\n", host->host_queue ? "" : "none");
+	
+	for (i = 0; i < fc->posmap->len; i++) {
+		unsigned char alpa = fc->posmap->list[i];
+		unsigned char target;
+		u32 *u1, *u2;
+		
+		target = alpa2target[alpa];
+		u1 = (u32 *)&fcal->nport_wwn[target];
+		u2 = (u32 *)&fcal->node_wwn[target];
+		if (!u1[0] && !u1[1]) {
+			SPRINTF ("  [AL-PA: %02x] Not responded to PLOGI\n", alpa);
+		} else if (!fcal->map[target]) {
+			SPRINTF ("  [AL-PA: %02x, Port WWN: %08x%08x, Node WWN: %08x%08x] Not responded to PRLI\n",
+				 alpa, u1[0], u1[1], u2[0], u2[1]);
+		} else {
+			Scsi_Device *scd;
+			for (scd = host->host_queue ; scd; scd = scd->next)
+				if (scd->host->host_no == hostno && scd->id == target) {
+					SPRINTF ("  [AL-PA: %02x, Id: %02d, Port WWN: %08x%08x, Node WWN: %08x%08x]  ",
+						alpa, target, u1[0], u1[1], u2[0], u2[1]);
+					SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+						scsi_device_types[(short) scd->type] : "Unknown device");
+
+					for (j = 0; (j < 8) && (scd->vendor[j] >= 0x20); j++)
+						SPRINTF ("%c", scd->vendor[j]);
+					SPRINTF (" ");
+
+					for (j = 0; (j < 16) && (scd->model[j] >= 0x20); j++)
+						SPRINTF ("%c", scd->model[j]);
+		
+					SPRINTF ("\n");
+				}
+		}
+	}
+	SPRINTF ("\n");
+
+	*start = buffer + offset;
+
+	if ((pos - buffer) < offset)
+		return 0;
+	else if (pos - buffer - offset < length)
+		return pos - buffer - offset;
+	else
+		return length;
+}
+
+/* 
+   For FC-AL, we use a simple addressing: we have just one channel 0,
+   and all AL-PAs are mapped to targets 0..0x7e
+ */
+static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd)
+{
+	struct fcal *f;
+	
+	/* We don't support LUNs yet - I'm not sure if LUN should be in SCSI fcp_cdb, or in second byte of addr[0] */
+	if (SCpnt->cmnd[1] & 0xe0) return -EINVAL;
+	/* FC-PLDA tells us... */
+	memset(addr, 0, 8);
+	f = (struct fcal *)SCpnt->host->hostdata;
+	if (!f->map[SCpnt->target]) return -EINVAL;
+	/* Now, determine DID: It will be Native Identifier, so we zero upper
+	   2 bytes of the 3 byte DID, lowest byte will be AL-PA */
+	fcmd->did = target2alpa[SCpnt->target];
+	FCALD(("trying DID %06x\n", fcmd->did))
+	return 0;
+}
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = FCAL;
+
+#include "scsi_module.c"
+
+EXPORT_NO_SYMBOLS;
+#endif /* MODULE */
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/fcal.h linux/drivers/scsi/fcal.h
--- v2.2.3/linux/drivers/scsi/fcal.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/fcal.h	Mon Mar 15 16:11:31 1999
@@ -0,0 +1,46 @@
+/* fcal.h: Generic Fibre Channel Arbitrated Loop SCSI host adapter driver definitions.
+ *
+ * Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#ifndef _FCAL_H
+#define _FCAL_H
+
+#include "../fc4/fcp_impl.h"
+
+struct fcal {
+	/* fc must be first */
+	fc_channel		*fc;
+	unsigned char		map[128];
+	fc_wwn			nport_wwn[128];
+	fc_wwn			node_wwn[128];
+};
+
+/* Arbitrary constant. Cannot be too large, as fc4 layer has limitations
+   for a particular channel */
+#define FCAL_CAN_QUEUE		512
+
+int fcal_detect(Scsi_Host_Template *);
+int fcal_release(struct Scsi_Host *);
+int fcal_proc_info (char *, char **, off_t, int, int, int);
+
+#define FCAL {							\
+	name:			"Fibre Channel Arbitrated Loop",\
+	detect:			fcal_detect,			\
+	release:		fcal_release,			\
+	proc_info:		fcal_proc_info,			\
+	queuecommand:		fcp_scsi_queuecommand,		\
+	can_queue:		FCAL_CAN_QUEUE,			\
+	this_id:		-1,				\
+	sg_tablesize:		1,				\
+	cmd_per_lun:		1,				\
+	use_clustering:		ENABLE_CLUSTERING,		\
+	use_new_eh_code:	FCP_SCSI_USE_NEW_EH_CODE,	\
+	abort:			fcp_old_abort,			\
+	eh_abort_handler:	fcp_scsi_abort,			\
+	eh_device_reset_handler:fcp_scsi_dev_reset,		\
+	eh_bus_reset_handler:	fcp_scsi_bus_reset,		\
+	eh_host_reset_handler:	fcp_scsi_host_reset,		\
+}	
+
+#endif /* !(_FCAL_H) */
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/pluto.c linux/drivers/scsi/pluto.c
--- v2.2.3/linux/drivers/scsi/pluto.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/scsi/pluto.c	Mon Mar 15 16:11:31 1999
@@ -1,6 +1,6 @@
 /* pluto.c: SparcSTORAGE Array SCSI host adapter driver.
  *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  *
  */
 
@@ -22,7 +22,7 @@
 
 #include "scsi.h"
 #include "hosts.h"
-#include "../fc4/fcp_scsi.h"
+#include "../fc4/fcp_impl.h"
 #include "pluto.h"
 
 #include <linux/module.h>
@@ -56,7 +56,7 @@
 static struct timer_list fc_timer __initdata = { 0 };
 struct semaphore fc_sem __initdata = MUTEX_LOCKED;
 
-static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr);
+static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd);
 
 __initfunc(static void pluto_detect_timeout(unsigned long data))
 {
@@ -100,15 +100,19 @@
 
 	tpnt->proc_dir = &proc_scsi_pluto;
 	fcscount = 0;
-	for_each_online_fc_channel(fc)
-		fcscount++;
+	for_each_online_fc_channel(fc) {
+		if (!fc->posmap)
+			fcscount++;
+	}
 	PLND(("%d channels online\n", fcscount))
 	if (!fcscount) {
 #if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KMOD)
 		request_module("soc");
 		
-		for_each_online_fc_channel(fc)
-			fcscount++;
+		for_each_online_fc_channel(fc) {
+			if (!fc->posmap)
+				fcscount++;
+		}
 		if (!fcscount)
 #endif
 			return 0;
@@ -129,8 +133,9 @@
 		Scsi_Cmnd *SCpnt;
 		struct Scsi_Host *host;
 		struct pluto *pluto;
-		
+
 		if (i == fcscount) break;
+		if (fc->posmap) continue;
 		
 		PLD(("trying to find SSA\n"))
 
@@ -233,9 +238,13 @@
 				host->max_id = inq->targets;
 				host->max_channel = inq->channels;
 				host->irq = fc->irq;
-				
+
+#ifdef __sparc_v9__
+				host->unchecked_isa_dma = 1;
+#endif
+
 				host->select_queue_depths = pluto_select_queue_depths;
-				
+
 				fc->channels = inq->channels + 1;
 				fc->targets = inq->targets;
 				fc->ages = ages;
@@ -304,7 +313,7 @@
    channel 0 id 0 lun 0 for CONTROLLER
    and channels 1 .. max_channel are normal single disks.
  */
-static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr)
+static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmnd *fcmd)
 {
 	PLND(("encode addr %d %d %d\n", SCpnt->channel, SCpnt->target, SCpnt->cmnd[1] & 0xe0))
 	/* We don't support LUNs - neither does SSA :) */
@@ -318,6 +327,8 @@
 		addr[2] = SCpnt->target;
 		addr[3] = 0;
 	}
+	/* We're Point-to-Point, so target it to the default DID */
+	fcmd->did = fc->did;
 	PLND(("trying %04x%04x%04x%04x\n", addr[0], addr[1], addr[2], addr[3]))
 	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/pluto.h linux/drivers/scsi/pluto.h
--- v2.2.3/linux/drivers/scsi/pluto.h	Thu Apr 23 20:21:35 1998
+++ linux/drivers/scsi/pluto.h	Mon Mar 15 16:11:31 1999
@@ -6,7 +6,7 @@
 #ifndef _PLUTO_H
 #define _PLUTO_H
 
-#include "../fc4/fcp_scsi.h"
+#include "../fc4/fcp_impl.h"
 
 struct pluto {
 	/* This must be first */
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/qlogicpti.c linux/drivers/scsi/qlogicpti.c
--- v2.2.3/linux/drivers/scsi/qlogicpti.c	Mon Oct  5 13:13:40 1998
+++ linux/drivers/scsi/qlogicpti.c	Mon Mar 15 16:11:31 1999
@@ -223,9 +223,15 @@
 	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
 	struct qlogicpti_regs *qregs = qpti->qregs;
 	u_short param[6];
+	unsigned short risc_code_addr;
 	int loop_count, i;
 	unsigned long flags;
 
+	if(qpti->is_pti != 0)
+		risc_code_addr = pti_risc_code_addr01;
+	else
+		risc_code_addr = sbus_risc_code_addr01;
+
 	save_flags(flags); cli();
 
 	qregs->hcctrl = HCCTRL_PAUSE;
@@ -264,7 +270,7 @@
 
 	/* Get RISC to start executing the firmware code. */
 	param[0] = MBOX_EXEC_FIRMWARE;
-	param[1] = risc_code_addr01;
+	param[1] = risc_code_addr;
 	if(qlogicpti_mbox_command(qpti, param, 1)) {
 		printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n",
 		       qpti->qpti_id);
@@ -352,21 +358,32 @@
 	struct qlogicpti_regs *qregs = qpti->qregs;
 	unsigned short csum = 0;
 	unsigned short param[6];
+	unsigned short *risc_code, risc_code_addr, risc_code_length;
 	unsigned long flags;
 #if !defined(MODULE) && !defined(__sparc_v9__)
 	unsigned long dvma_addr;
 #endif
 	int i, timeout;
 
+	if(qpti->is_pti != 0) {
+		risc_code = &pti_risc_code01[0];
+		risc_code_addr = pti_risc_code_addr01;
+		risc_code_length = pti_risc_code_length01;
+	} else {
+		risc_code = &sbus_risc_code01[0];
+		risc_code_addr = sbus_risc_code_addr01;
+		risc_code_length = sbus_risc_code_length01;
+	}
+
 	save_flags(flags); cli();
 
 	/* Verify the checksum twice, one before loading it, and once
 	 * afterwards via the mailbox commands.
 	 */
-	for(i = 0; i < risc_code_length01; i++)
-		csum += risc_code01[i];
+	for(i = 0; i < risc_code_length; i++)
+		csum += risc_code[i];
 	if(csum) {
-		printk(KERN_EMERG "qlogicpti%d: AIeee, firmware checksum failed!",
+		printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
 		       qpti->qpti_id);
 		return 1;
 	}		
@@ -406,11 +423,6 @@
 		qpti->differential = 0;
 	qregs->hcctrl = HCCTRL_REL;
 
-	/* XXX Talk to PTI engineer about the following, ISP always
-	 * XXX returns 0x4001 return status for stop firmware command,
-	 * XXX documentation claims this means the cmd is unsupported
-	 * XXX on this ISP.  I think something fishy is going on.
-	 */
 	param[0] = MBOX_STOP_FIRMWARE;
 	param[1] = param[2] = param[3] = param[4] = param[5] = 0;
 	if(qlogicpti_mbox_command(qpti, param, 1)) {
@@ -423,13 +435,13 @@
 	/* Load the firmware. */
 #if !defined(MODULE) && !defined(__sparc_v9__)
 	if (sparc_cpu_model != sun4d) {
-		dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code01[0],
-							 (sizeof(u_short) * risc_code_length01));
+		dvma_addr = (unsigned long) mmu_lockarea((char *)&risc_code[0],
+							 (sizeof(u_short) * risc_code_length));
 		param[0] = MBOX_LOAD_RAM;
-		param[1] = risc_code_addr01;
+		param[1] = risc_code_addr;
 		param[2] = (dvma_addr >> 16);
 		param[3] = (dvma_addr & 0xffff);
-		param[4] = (sizeof(u_short) * risc_code_length01);
+		param[4] = (sizeof(u_short) * risc_code_length);
 		if(qlogicpti_mbox_command(qpti, param, 1) ||
 		   (param[0] != MBOX_COMMAND_COMPLETE)) {
 			printk(KERN_EMERG "qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
@@ -437,14 +449,14 @@
 			restore_flags(flags);
 			return 1;
 		}
-		mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length01));
+		mmu_unlockarea((char *)dvma_addr, (sizeof(u_short) * risc_code_length));
 	} else
 #endif
 	/* We need to do it this slow way always on Ultra, SS[12]000. */
-		for(i = 0; i < risc_code_length01; i++) {
+		for(i = 0; i < risc_code_length; i++) {
 			param[0] = MBOX_WRITE_RAM_WORD;
-			param[1] = risc_code_addr01 + i;
-			param[2] = risc_code01[i];
+			param[1] = risc_code_addr + i;
+			param[2] = risc_code[i];
 			if(qlogicpti_mbox_command(qpti, param, 1) ||
 			   param[0] != MBOX_COMMAND_COMPLETE) {
 				printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
@@ -464,7 +476,7 @@
 
 	/* Ask ISP to verify the checksum of the new code. */
 	param[0] = MBOX_VERIFY_CHECKSUM;
-	param[1] = risc_code_addr01;
+	param[1] = risc_code_addr;
 	if(qlogicpti_mbox_command(qpti, param, 1) ||
 	   (param[0] != MBOX_COMMAND_COMPLETE)) {
 		printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n",
@@ -475,7 +487,7 @@
 
 	/* Start using newly downloaded firmware. */
 	param[0] = MBOX_EXEC_FIRMWARE;
-	param[1] = risc_code_addr01;
+	param[1] = risc_code_addr;
 	qlogicpti_mbox_command(qpti, param, 1);
 
 	param[0] = MBOX_ABOUT_FIRMWARE;
@@ -491,16 +503,18 @@
 	qpti->fware_majrev = param[1];
 	qpti->fware_minrev = param[2];
 
-	/* Load scsi initiator ID and interrupt level into sbus static ram. */
-	param[0] = MBOX_WRITE_RAM_WORD;
-	param[1] = 0xff80;
-	param[2] = (unsigned short) qpti->scsi_id;
-	qlogicpti_mbox_command(qpti, param, 1);
-
-	param[0] = MBOX_WRITE_RAM_WORD;
-	param[1] = 0xff00;
-	param[2] = (unsigned short) 3;
-	qlogicpti_mbox_command(qpti, param, 1);
+	if(qpti->is_pti != 0) {
+		/* Load scsi initiator ID and interrupt level into sbus static ram. */
+		param[0] = MBOX_WRITE_RAM_WORD;
+		param[1] = 0xff80;
+		param[2] = (unsigned short) qpti->scsi_id;
+		qlogicpti_mbox_command(qpti, param, 1);
+
+		param[0] = MBOX_WRITE_RAM_WORD;
+		param[1] = 0xff00;
+		param[2] = (unsigned short) 3;
+		qlogicpti_mbox_command(qpti, param, 1);
+	}
 
 	restore_flags(flags);
 	return 0;
@@ -617,6 +631,10 @@
 			/* We are wide capable, 16 targets. */
 			qpti_host->max_id = MAX_TARGETS;
 
+#ifdef __sparc_v9__
+			qpti_host->unchecked_isa_dma = 1;
+#endif
+
 			/* Setup back pointers and misc. state. */
 			qpti->qhost = qpti_host;
 			qpti->qdev = qpti_dev;
@@ -638,7 +656,8 @@
 				       sizeof(qpti->prom_name));
 			qpti->prom_node = qpti_node;
 			
-			is_pti = strcmp (qpti->prom_name, "QLGC,isp");
+			qpti->is_pti = is_pti =
+				(strcmp (qpti->prom_name, "QLGC,isp") != 0);
 
 			/* Setup the reg property for this device. */
 			prom_apply_sbus_ranges(qpti->qdev->my_bus,
@@ -728,6 +747,9 @@
 				bsizes = (DMA_BURST32 - 1);
 			qpti->bursts = bsizes;
 
+			/* Clear out Scsi_Cmnd array. */
+			memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
+
 			/* The request and response queues must each be aligned
 			 * on a page boundry.
 			 */
@@ -747,11 +769,13 @@
 			/* Set adapter and per-device default values. */
 			qlogicpti_set_hostdev_defaults(qpti);
 			
-			if (is_pti) {
-				/* Load the firmware. */
-				if(qlogicpti_load_firmware(qpti))
-					panic("PTI Qlogic/ISP firmware load failed");
+			/* Load the firmware. */
+			/* XXX Find out where is it possible to download
+			   our sbus_risc_code on non-PTI ISP1000. */
+			if(is_pti && qlogicpti_load_firmware(qpti))
+				panic("SBUS Qlogic/ISP firmware load failed");
 
+			if (is_pti) {
 				/* Check the PTI status reg. */
 				if(qlogicpti_verify_tmon(qpti))
 					panic("PTI Qlogic/ISP tmon verification failed");
@@ -768,7 +792,10 @@
 				char buffer[60];
 				
 				prom_getstring (qpti_node, "isp-fcode", buffer, 60);
-				printk("(Firmware %s)", buffer);
+				if (buffer[0])
+					printk("(Firmware %s)", buffer);
+				if (prom_getbool(qpti_node, "differential"))
+					qpti->differential = 1;
 			}
 			
 			printk (" [%s Wide, using %s interface]\n",
@@ -831,11 +858,6 @@
 	memset(cmd, 0, sizeof(struct Command_Entry));
 	cmd->hdr.entry_cnt = 1;
 	cmd->hdr.entry_type = ENTRY_COMMAND;
-#ifdef __sparc_v9__
-	cmd->handle = (u_int) (((unsigned long)Cmnd) - PAGE_OFFSET); /* magic mushroom */
-#else
-	cmd->handle = (u_int) ((unsigned long)Cmnd);                 /* magic mushroom */
-#endif
 	cmd->target_id = Cmnd->target;
 	cmd->target_lun = Cmnd->lun;
 	cmd->cdb_length = Cmnd->cmd_len;
@@ -924,9 +946,15 @@
 		cmd->dataseg[0].d_count = Cmnd->request_bufflen;
 		cmd->segment_cnt = 1;
 	}
+
+	/* Committed, record Scsi_Cmd so we can find it later. */
+	cmd->handle = in_ptr;
+	qpti->cmd_slots[in_ptr] = Cmnd;
+
 	qpti->cmd_count[Cmnd->target]++;
 	qregs->mbox4 = in_ptr;
 	qpti->req_in_ptr = in_ptr;
+
 	return in_ptr;
 }
 
@@ -1099,9 +1127,18 @@
 	/* This looks like a network driver! */
 	out_ptr = qpti->res_out_ptr;
 	while(out_ptr != in_ptr) {
+		u_int cmd_slot;
+
 		sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr];
 		out_ptr = NEXT_RES_PTR(out_ptr);
-		Cmnd = (Scsi_Cmnd *) (((unsigned long)sts->handle)+PAGE_OFFSET);
+
+		/* We store an index in the handle, not the pointer in
+		 * some form.  This avoids problems due to the fact
+		 * that the handle provided is only 32-bits. -DaveM
+		 */
+		cmd_slot = sts->handle;
+		Cmnd = qpti->cmd_slots[cmd_slot];
+		qpti->cmd_slots[cmd_slot] = NULL;
 
 		if(sts->completion_status == CS_RESET_OCCURRED ||
 		   sts->completion_status == CS_ABORTED ||
@@ -1144,8 +1181,8 @@
 	int again;
 
 	spin_lock_irqsave(&io_request_lock, flags);
-	again = 0;
 	do {
+		again = 0;
 		for_each_qlogicpti(qpti)
 			again |= qlogicpti_intr_handler(qpti);
 	} while (again);
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/qlogicpti.h linux/drivers/scsi/qlogicpti.h
--- v2.2.3/linux/drivers/scsi/qlogicpti.h	Thu Aug  6 14:06:33 1998
+++ linux/drivers/scsi/qlogicpti.h	Mon Mar 15 16:11:31 1999
@@ -455,16 +455,24 @@
 	/* These are the hot elements in the cache, so they come first. */
 	struct qlogicpti         *next;                 /* Next active adapter        */
 	struct qlogicpti_regs    *qregs;                /* Adapter registers          */
-	u_int	                  req_in_ptr;		/* index of next request slot */
-	u_int	                  res_out_ptr;		/* index of next result slot  */
 	struct pti_queue_entry   *res_cpu;              /* Ptr to RESPONSE bufs (CPU) */
-	__u32                     res_dvma;             /* Ptr to RESPONSE bufs (DVMA)*/
 	struct pti_queue_entry   *req_cpu;              /* Ptr to REQUEST bufs (CPU)  */
+
+	__u32                     res_dvma;             /* Ptr to RESPONSE bufs (DVMA)*/
 	__u32                     req_dvma;             /* Ptr to REQUEST bufs (DVMA) */
+	u_int	                  req_in_ptr;		/* index of next request slot */
+	u_int	                  res_out_ptr;		/* index of next result slot  */
+
 	int                       cmd_count[MAX_TARGETS];
 	unsigned long             tag_ages[MAX_TARGETS];
 	long	                  send_marker;		/* must we send a marker?     */
 
+	/* The cmd->handler is only 32-bits, so that things work even on monster
+	 * Ex000 sparc64 machines with >4GB of ram we just keep track of the
+	 * scsi command pointers here.  This is essentially what Matt Jacob does. -DaveM
+	 */
+	Scsi_Cmnd                *cmd_slots[QLOGICISP_REQ_QUEUE_LEN + 1];
+
 	/* The rest of the elements are unimportant for performance. */
 	u_char	                  fware_majrev, fware_minrev;
 	struct Scsi_Host         *qhost;
@@ -487,11 +495,7 @@
 #define SREG_IMASK                0x0c   /* Interrupt level            */
 #define SREG_SPMASK               0x03   /* Mask for switch pack       */
 	unsigned char             swsreg;
-
-#if 0
-	char	res[RES_QUEUE_LEN+1][QUEUE_ENTRY_LEN];
-	char	req[QLOGICISP_REQ_QUEUE_LEN+1][QUEUE_ENTRY_LEN];
-#endif
+	unsigned char             is_pti; /* Non-zero if this is a PTI board. */
 };
 
 /* How to twiddle them bits... */
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/qlogicpti_asm.c linux/drivers/scsi/qlogicpti_asm.c
--- v2.2.3/linux/drivers/scsi/qlogicpti_asm.c	Wed Apr 23 19:01:22 1997
+++ linux/drivers/scsi/qlogicpti_asm.c	Mon Mar 15 16:11:31 1999
@@ -1,8 +1,8 @@
 /* Version 1.24 Initiator Firmware (Aug 8, 1996) */
 
-unsigned short risc_code_addr01 = 0x1000;
+unsigned short pti_risc_code_addr01 = 0x1000;
 
-unsigned short risc_code01[] __initdata = {
+unsigned short pti_risc_code01[] __initdata = {
 	0x0078, 0x1030, 0x0000, 0x231f, 0x0000, 0x12ff, 0x2043, 0x4f50,
 	0x5952, 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932,
 	0x2c31, 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749,
@@ -1129,4 +1129,1170 @@
 	0x331b, 0x2019, 0x2626, 0x7b22, 0x7b26, 0x007c, 0xae5b
 };
 
-unsigned short risc_code_length01 = 0x231f;
+unsigned short pti_risc_code_length01 = 0x231f;
+
+/* Version 1.31.00 ISP1000 Initiator RISC firmware
+ * We use this for all non-PTI SBUS boards.
+ */
+unsigned short sbus_risc_code_addr01 = 0x1000;
+
+unsigned short sbus_risc_code01[] __initdata = {
+	0x0078, 0x1030, 0xa5e3, 0x241a, 0x0001, 0x12ff, 0x2043, 0x4f50,
+	0x5952, 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932,
+	0x2c31, 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749,
+	0x4320, 0x434f, 0x5250, 0x4f52, 0x4154, 0x494f, 0x4e00, 0x2049,
+	0x5350, 0x3130, 0x3030, 0x2046, 0x6972, 0x6d77, 0x6172, 0x6520,
+	0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030, 0x312e, 0x3331, 0x2020,
+	0x2071, 0x0010, 0x70c3, 0x0004, 0x20c9, 0x3fff, 0x2089, 0x10ca,
+	0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0001,
+	0x3f00, 0x70d6, 0x2031, 0x0030, 0x2079, 0x3500, 0x7863, 0x0000,
+	0x2fa0, 0x2009, 0x012b, 0x2011, 0x0000, 0x20a9, 0x0040, 0x42a4,
+	0x8109, 0x00c0, 0x104d, 0x789b, 0x0101, 0x780b, 0x0002, 0x780f,
+	0x0002, 0x784f, 0x0bb8, 0x2069, 0x3540, 0x00a8, 0x106c, 0x681b,
+	0x003c, 0x2009, 0x1313, 0xa18c, 0xff00, 0x3700, 0xa084, 0x00ff,
+	0xa105, 0x20b8, 0x0078, 0x106e, 0x681b, 0x0028, 0x6807, 0x0007,
+	0x680b, 0x00fa, 0x680f, 0x0008, 0x6813, 0x0005, 0x681f, 0x0000,
+	0x6823, 0x0006, 0x6817, 0x0008, 0x6827, 0x0000, 0x2069, 0x3600,
+	0x2011, 0x0020, 0x2009, 0x0010, 0x680b, 0x0c19, 0x680f, 0x0019,
+	0x6803, 0xdd00, 0x6807, 0x001a, 0x6a1a, 0x2d00, 0xa0e8, 0x0008,
+	0xa290, 0x0004, 0x8109, 0x00c0, 0x1084, 0x2069, 0x3680, 0x20a9,
+	0x0080, 0x6837, 0x0000, 0x680b, 0x0040, 0x6817, 0x0064, 0x681f,
+	0x0002, 0xade8, 0x0010, 0x0070, 0x10a7, 0x0078, 0x1099, 0x1078,
+	0x1a42, 0x1078, 0x2f3d, 0x1078, 0x168b, 0x1078, 0x33bb, 0x3200,
+	0xa085, 0x000d, 0x2090, 0x70c3, 0x0000, 0x0090, 0x10be, 0x70c0,
+	0xa086, 0x0002, 0x00c0, 0x10be, 0x1078, 0x11be, 0x1078, 0x10ee,
+	0x1078, 0x1821, 0x1078, 0x19b2, 0x1078, 0x3280, 0x1078, 0x1787,
+	0x0078, 0x10be, 0x10d2, 0x10d4, 0x1b9f, 0x1b9f, 0x2f9b, 0x2f9b,
+	0x1b9f, 0x1b9f, 0x0078, 0x10d2, 0x0078, 0x10d4, 0x0078, 0x10d6,
+	0x0078, 0x10d8, 0x7008, 0x800c, 0x00c8, 0x10e9, 0x7007, 0x0002,
+	0xa08c, 0x000c, 0x00c0, 0x10ea, 0x8004, 0x8004, 0x00c8, 0x10e9,
+	0x087a, 0x097a, 0x70c3, 0x4002, 0x0078, 0x11c1, 0x0068, 0x1133,
+	0x7814, 0xa005, 0x00c0, 0x10f8, 0x0010, 0x1134, 0x0078, 0x1133,
+	0x2009, 0x3568, 0x2104, 0xa005, 0x00c0, 0x1133, 0x7814, 0xa086,
+	0x0001, 0x00c0, 0x1105, 0x1078, 0x1540, 0x7817, 0x0000, 0x2009,
+	0x356f, 0x2104, 0xa065, 0x0040, 0x1121, 0x2009, 0x356a, 0x211c,
+	0x8108, 0x2114, 0x8108, 0x2104, 0xa210, 0xa399, 0x0000, 0x2009,
+	0x001c, 0x6083, 0x0103, 0x1078, 0x161b, 0x00c0, 0x112d, 0x1078,
+	0x1682, 0x2009, 0x356f, 0x200b, 0x0000, 0x2009, 0x3569, 0x2104,
+	0x200b, 0x0000, 0xa005, 0x0040, 0x1131, 0x2001, 0x4005, 0x0078,
+	0x11c0, 0x0078, 0x11be, 0x007c, 0x2061, 0x0000, 0x6018, 0xa084,
+	0x0001, 0x0040, 0x113c, 0x007c, 0x70c3, 0x0000, 0x70c7, 0x0000,
+	0x70cb, 0x0000, 0x70cf, 0x0000, 0x70c0, 0xa0bc, 0xffc0, 0x00c0,
+	0x118c, 0x2038, 0x0079, 0x114c, 0x11be, 0x1209, 0x11d7, 0x1209,
+	0x125a, 0x125a, 0x11ce, 0x159a, 0x1265, 0x11ca, 0x11db, 0x11dd,
+	0x11df, 0x11e1, 0x159f, 0x11ca, 0x126b, 0x1287, 0x154e, 0x1594,
+	0x11e3, 0x1475, 0x1497, 0x14b1, 0x14da, 0x142e, 0x143c, 0x1450,
+	0x1464, 0x12f3, 0x11ca, 0x12a3, 0x12aa, 0x12af, 0x12b4, 0x12ba,
+	0x12bf, 0x12c4, 0x12c9, 0x12ce, 0x12d2, 0x12e7, 0x11ca, 0x11ca,
+	0x11ca, 0x11ca, 0x11ca, 0x12ff, 0x1308, 0x1317, 0x133d, 0x1347,
+	0x134e, 0x137a, 0x1389, 0x1398, 0x13aa, 0x1413, 0x11ca, 0x11ca,
+	0x11ca, 0x11ca, 0x11ca, 0x1423, 0xa0bc, 0xffa0, 0x00c0, 0x11ca,
+	0x2038, 0xa084, 0x001f, 0x0079, 0x1195, 0x11ca, 0x11ca, 0x11ca,
+	0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca,
+	0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca,
+	0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x11ca, 0x15f7,
+	0x1601, 0x1605, 0x1613, 0x11ca, 0x11ca, 0x72ca, 0x71c6, 0x2001,
+	0x4006, 0x0078, 0x11c0, 0x73ce, 0x72ca, 0x71c6, 0x2001, 0x4000,
+	0x70c2, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x2091,
+	0x4080, 0x007c, 0x70c3, 0x4001, 0x0078, 0x11c1, 0x2099, 0x0041,
+	0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0078, 0x11be, 0x70c4,
+	0x70c3, 0x0004, 0x007a, 0x0078, 0x11be, 0x0078, 0x11be, 0x0078,
+	0x11be, 0x0078, 0x11be, 0x2091, 0x8000, 0x70c3, 0x0000, 0x70c7,
+	0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0001, 0x3f00,
+	0x70d6, 0x2079, 0x0000, 0x781b, 0x0001, 0x2031, 0x0030, 0x2059,
+	0x1000, 0x2029, 0x0457, 0x2051, 0x0470, 0x2061, 0x0472, 0x20b9,
+	0xffff, 0x20c1, 0x0000, 0x2091, 0x5000, 0x2091, 0x4080, 0x0078,
+	0x0455, 0x71d0, 0x72c8, 0x73cc, 0x70c4, 0x20a0, 0x2098, 0x2031,
+	0x0030, 0x81ff, 0x0040, 0x11be, 0x7007, 0x0004, 0x731a, 0x721e,
+	0x2051, 0x0012, 0x2049, 0x1238, 0x2041, 0x11be, 0x7003, 0x0002,
+	0xa786, 0x0001, 0x00c0, 0x122a, 0x2049, 0x1246, 0x2041, 0x1252,
+	0x7003, 0x0003, 0x7017, 0x0000, 0x810b, 0x7112, 0x00c8, 0x1232,
+	0x7017, 0x0001, 0x7007, 0x0001, 0xa786, 0x0001, 0x0040, 0x1246,
+	0x700c, 0xa084, 0x007f, 0x8004, 0x2009, 0x0020, 0xa102, 0x0942,
+	0x094a, 0x20a8, 0x26a0, 0x53a6, 0x0078, 0x10da, 0x700c, 0xa084,
+	0x007f, 0x0040, 0x1246, 0x80ac, 0x0048, 0x1246, 0x2698, 0x53a5,
+	0x0078, 0x10da, 0x700c, 0xa084, 0x007f, 0x80ac, 0x2698, 0x53a5,
+	0x0078, 0x11be, 0x71c4, 0x70c8, 0x2114, 0xa79e, 0x0004, 0x00c0,
+	0x1262, 0x200a, 0x72ca, 0x0078, 0x11bd, 0x70c7, 0x0001, 0x70cb,
+	0x001f, 0x0078, 0x11be, 0x70c4, 0x72c8, 0x73cc, 0x74d0, 0x70c6,
+	0x72ca, 0x73ce, 0x74d2, 0xa005, 0x0040, 0x1281, 0x8001, 0x7872,
+	0x7a7a, 0x7b7e, 0x7c76, 0x7898, 0xa084, 0xfffc, 0x789a, 0x0078,
+	0x1285, 0x7898, 0xa085, 0x0001, 0x789a, 0x0078, 0x11be, 0x70c4,
+	0x72c8, 0x73cc, 0x74d4, 0x70c6, 0x72ca, 0x73ce, 0x74d6, 0xa005,
+	0x0040, 0x129d, 0x8001, 0x7886, 0x7a8e, 0x7b92, 0x7c8a, 0x7898,
+	0xa084, 0xfcff, 0x789a, 0x0078, 0x12a1, 0x7898, 0xa085, 0x0100,
+	0x789a, 0x0078, 0x11be, 0x2009, 0x3559, 0x210c, 0x2011, 0x015c,
+	0x0078, 0x11bc, 0x2009, 0x3541, 0x210c, 0x0078, 0x11bd, 0x2009,
+	0x3542, 0x210c, 0x0078, 0x11bd, 0x2061, 0x3540, 0x610c, 0x6210,
+	0x0078, 0x11bc, 0x2009, 0x3545, 0x210c, 0x0078, 0x11bd, 0x2009,
+	0x3546, 0x210c, 0x0078, 0x11bd, 0x2009, 0x3547, 0x210c, 0x0078,
+	0x11bd, 0x2009, 0x3548, 0x210c, 0x0078, 0x11bd, 0x7908, 0x7a0c,
+	0x0078, 0x11bc, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003,
+	0x8003, 0xa0e8, 0x3600, 0x6a00, 0x6804, 0xa084, 0x0008, 0x0040,
+	0x12e4, 0x6b08, 0x0078, 0x12e5, 0x6b0c, 0x0078, 0x11bb, 0x77c4,
+	0x1078, 0x169c, 0x2091, 0x8000, 0x6b1c, 0x6a14, 0x2091, 0x8001,
+	0x2708, 0x0078, 0x11bb, 0x77c4, 0x1078, 0x169c, 0x2091, 0x8000,
+	0x6908, 0x6a18, 0x6b10, 0x2091, 0x8001, 0x0078, 0x11bb, 0x71c4,
+	0xa182, 0x0010, 0x00c8, 0x11b6, 0x1078, 0x1ac6, 0x0078, 0x11bb,
+	0x71c4, 0xa182, 0x0010, 0x00c8, 0x11b6, 0x2011, 0x3541, 0x2204,
+	0x007e, 0x2112, 0x1078, 0x1a7f, 0x017f, 0x0078, 0x11bd, 0x71c4,
+	0x2011, 0x1335, 0x20a9, 0x0008, 0x2204, 0xa106, 0x0040, 0x1327,
+	0x8210, 0x0070, 0x1325, 0x0078, 0x131c, 0x0078, 0x11b6, 0xa292,
+	0x1335, 0x027e, 0x2011, 0x3542, 0x2204, 0x2112, 0x017f, 0x007e,
+	0x1078, 0x1a8b, 0x017f, 0x0078, 0x11bd, 0x03e8, 0x00fa, 0x01f4,
+	0x02ee, 0x0064, 0x0019, 0x0032, 0x004b, 0x2061, 0x3540, 0x610c,
+	0x6210, 0x70c4, 0x600e, 0x70c8, 0x6012, 0x0078, 0x11bc, 0x2061,
+	0x3540, 0x6114, 0x70c4, 0x6016, 0x0078, 0x11bd, 0x71c4, 0x2011,
+	0x0004, 0x2019, 0x1212, 0xa186, 0x0028, 0x0040, 0x1367, 0x2011,
+	0x0005, 0x2019, 0x1212, 0xa186, 0x0032, 0x0040, 0x1367, 0x2011,
+	0x0006, 0x2019, 0x1313, 0xa186, 0x003c, 0x00c0, 0x11b6, 0x2061,
+	0x3540, 0x6018, 0x007e, 0x611a, 0xa39c, 0xff00, 0x3700, 0xa084,
+	0x00ff, 0xa305, 0x20b8, 0x1078, 0x1a9c, 0x1078, 0x33bb, 0x017f,
+	0x0078, 0x11bd, 0x71c4, 0xa184, 0xffcf, 0x00c0, 0x11b6, 0x2011,
+	0x3547, 0x2204, 0x2112, 0x007e, 0x1078, 0x1abe, 0x017f, 0x0078,
+	0x11bd, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x11b6, 0x2011, 0x3548,
+	0x2204, 0x007e, 0x2112, 0x1078, 0x1aad, 0x017f, 0x0078, 0x11bd,
+	0x71c4, 0x72c8, 0xa184, 0xfffd, 0x00c0, 0x11b5, 0xa284, 0xfffd,
+	0x00c0, 0x11b5, 0x2100, 0x7908, 0x780a, 0x2200, 0x7a0c, 0x780e,
+	0x0078, 0x11bc, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003,
+	0x8003, 0xa0e8, 0x3600, 0x2019, 0x0000, 0x72c8, 0x6800, 0x007e,
+	0xa226, 0x0040, 0x13d9, 0x6a02, 0xa484, 0x2000, 0x0040, 0x13c2,
+	0xa39d, 0x0010, 0xa484, 0x1000, 0x0040, 0x13c8, 0xa39d, 0x0008,
+	0xa484, 0x4000, 0x0040, 0x13d9, 0x810f, 0xa284, 0x4000, 0x0040,
+	0x13d5, 0x1078, 0x1ae0, 0x0078, 0x13d9, 0x1078, 0x1ad2, 0x0078,
+	0x13d9, 0x72cc, 0x82ff, 0x0040, 0x140b, 0x6808, 0xa206, 0x0040,
+	0x140b, 0xa2a4, 0x00ff, 0x2061, 0x3540, 0x6118, 0xa186, 0x0028,
+	0x0040, 0x13f2, 0xa186, 0x0032, 0x0040, 0x13f8, 0xa186, 0x003c,
+	0x0040, 0x13fe, 0xa482, 0x0064, 0x0048, 0x1408, 0x0078, 0x1402,
+	0xa482, 0x0050, 0x0048, 0x1408, 0x0078, 0x1402, 0xa482, 0x0043,
+	0x0048, 0x1408, 0x71c4, 0x71c6, 0x027f, 0x72ca, 0x0078, 0x11b7,
+	0x6a0a, 0xa39d, 0x000a, 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c,
+	0x71c4, 0x0078, 0x11bb, 0x77c4, 0x1078, 0x169c, 0x2091, 0x8000,
+	0x6a14, 0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e,
+	0x2708, 0x0078, 0x11bb, 0x71c4, 0x72c8, 0x73cc, 0xa182, 0x0010,
+	0x00c8, 0x11b6, 0x1078, 0x1aee, 0x0078, 0x11bb, 0x77c4, 0x1078,
+	0x169c, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0002, 0x6a0a, 0x2091,
+	0x8001, 0x2708, 0x0078, 0x11bc, 0x77c4, 0x1078, 0x169c, 0x2091,
+	0x8000, 0x6a08, 0xa294, 0xfff9, 0x6a0a, 0x6804, 0xa005, 0x0040,
+	0x144b, 0x1078, 0x1a23, 0x2091, 0x8001, 0x2708, 0x0078, 0x11bc,
+	0x77c4, 0x1078, 0x169c, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0004,
+	0x6a0a, 0x6804, 0xa005, 0x0040, 0x145f, 0x1078, 0x1a23, 0x2091,
+	0x8001, 0x2708, 0x0078, 0x11bc, 0x77c4, 0x2041, 0x0001, 0x2049,
+	0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078, 0x16a9, 0x2091,
+	0x8001, 0x2708, 0x6a08, 0x0078, 0x11bc, 0x77c4, 0x72c8, 0x73cc,
+	0x77c6, 0x72ca, 0x73ce, 0x1078, 0x1722, 0x00c0, 0x1493, 0x6818,
+	0xa005, 0x0040, 0x148d, 0x2708, 0x1078, 0x1afe, 0x00c0, 0x148d,
+	0x7817, 0xffff, 0x2091, 0x8001, 0x007c, 0x2091, 0x8001, 0x2001,
+	0x4005, 0x0078, 0x11c0, 0x2091, 0x8001, 0x0078, 0x11be, 0x77c4,
+	0x77c6, 0x2041, 0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091,
+	0x8000, 0x1078, 0x16a9, 0x2061, 0x3540, 0x60a3, 0x0003, 0x67b6,
+	0x60a7, 0x0000, 0x7817, 0xffff, 0x2091, 0x8001, 0x1078, 0x1a23,
+	0x007c, 0x77c8, 0x77ca, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2091,
+	0x8000, 0x2061, 0x3540, 0x60a3, 0x0002, 0x60a7, 0x0000, 0x67b6,
+	0x7817, 0xffff, 0x1078, 0x1a23, 0x2091, 0x8001, 0x2041, 0x0021,
+	0x2049, 0x0004, 0x2051, 0x0010, 0x2091, 0x8000, 0x1078, 0x16a9,
+	0x70c8, 0x6836, 0x8738, 0xa784, 0x0007, 0x00c0, 0x14ce, 0x2091,
+	0x8001, 0x007c, 0x7898, 0xa084, 0x0003, 0x00c0, 0x14fe, 0x2039,
+	0x0000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008, 0x1078,
+	0x169c, 0x2091, 0x8000, 0x6808, 0xa80d, 0x690a, 0x2091, 0x8001,
+	0x8738, 0xa784, 0x0007, 0x00c0, 0x14e7, 0xa7bc, 0xff00, 0x873f,
+	0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x14e7, 0x2091, 0x8000,
+	0x2069, 0x0100, 0x6830, 0xa084, 0x0040, 0x0040, 0x1527, 0x684b,
+	0x0004, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0004, 0x0040, 0x1514,
+	0x0070, 0x1514, 0x0078, 0x150b, 0x684b, 0x0009, 0x20a9, 0x0014,
+	0x6848, 0xa084, 0x0001, 0x0040, 0x1521, 0x0070, 0x1521, 0x0078,
+	0x1518, 0x20a9, 0x00fa, 0x0070, 0x1527, 0x0078, 0x1523, 0x2079,
+	0x3500, 0x7817, 0x0001, 0x2061, 0x3540, 0x60a3, 0x0001, 0x60a7,
+	0x0000, 0x60c3, 0x000f, 0x7898, 0xa085, 0x0002, 0x789a, 0x6808,
+	0xa084, 0xfffd, 0x680a, 0x681b, 0x0046, 0x2091, 0x8001, 0x007c,
+	0x7898, 0xa084, 0xfffd, 0x789a, 0xa084, 0x0001, 0x00c0, 0x154a,
+	0x1078, 0x176a, 0x71c4, 0x71c6, 0x794a, 0x007c, 0x74c4, 0x73c8,
+	0x72cc, 0x74c6, 0x73ca, 0x72ce, 0x2079, 0x3500, 0x2009, 0x0040,
+	0x1078, 0x1679, 0x0040, 0x1590, 0x1078, 0x1649, 0x0040, 0x1564,
+	0x1078, 0x1682, 0x0078, 0x1590, 0x6010, 0x2091, 0x8001, 0x7817,
+	0xffff, 0x2009, 0x3568, 0x200b, 0x0005, 0x8108, 0x200b, 0x0000,
+	0x8108, 0x230a, 0x8108, 0x220a, 0x8108, 0x240a, 0x8108, 0x200a,
+	0x8108, 0x200b, 0x0000, 0x8108, 0x2c0a, 0xa02e, 0x2530, 0x0e7e,
+	0x1078, 0x2f16, 0x0e7f, 0x6592, 0x65a2, 0x6696, 0x66a6, 0x60ab,
+	0x0000, 0x60af, 0x0000, 0x2091, 0x8001, 0x1078, 0x1a23, 0x007c,
+	0x70c3, 0x4005, 0x0078, 0x11c1, 0x71c4, 0x70c7, 0x0000, 0x7906,
+	0x0078, 0x11be, 0x71c4, 0x71c6, 0x2168, 0x0078, 0x15a1, 0x2069,
+	0x1000, 0x690c, 0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x00c0,
+	0x15a3, 0xa285, 0x0000, 0x00c0, 0x15b1, 0x70c3, 0x4000, 0x0078,
+	0x15b3, 0x70c3, 0x4003, 0x70ca, 0x0078, 0x11c1, 0x71c4, 0x72c8,
+	0x73cc, 0x2100, 0xa184, 0xfffc, 0x00c0, 0x11ca, 0x2100, 0x0079,
+	0x15c1, 0x15d8, 0x15ed, 0x15ef, 0x15f1, 0x70c3, 0x4003, 0x71ce,
+	0x72d2, 0x73d6, 0x0078, 0x15d4, 0x70c3, 0x4000, 0x70cf, 0x0000,
+	0x70d3, 0x0000, 0x70d7, 0x0000, 0x77c6, 0x71ca, 0x0078, 0x11be,
+	0x2031, 0x15f3, 0x2624, 0x8630, 0x2412, 0x2204, 0xa446, 0x00c0,
+	0x15c5, 0xa484, 0xffff, 0x00c0, 0x15da, 0x2031, 0x15f3, 0x8210,
+	0x8319, 0xa384, 0xffff, 0x00c0, 0x15da, 0x0078, 0x15cc, 0x0078,
+	0x15cc, 0x0078, 0x15cc, 0x5555, 0xaaaa, 0xffff, 0x0000, 0x7960,
+	0x71c6, 0x71c4, 0xa182, 0x0003, 0x00c8, 0x11b6, 0x7962, 0x0078,
+	0x11be, 0x7960, 0x71c6, 0x0078, 0x11be, 0x7954, 0x71c6, 0x71c4,
+	0x7956, 0x7958, 0x71ca, 0x71c8, 0x795a, 0x795c, 0x71ce, 0x71cc,
+	0x795e, 0x0078, 0x11be, 0x7954, 0x71c6, 0x7958, 0x71ca, 0x795c,
+	0x71ce, 0x0078, 0x11be, 0x700c, 0xa084, 0x007f, 0x0040, 0x1627,
+	0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x1622, 0x7017,
+	0x0000, 0x7112, 0x721a, 0x731e, 0x8108, 0x810c, 0x81a9, 0x8c98,
+	0x20a1, 0x0030, 0x6080, 0x20a2, 0x53a6, 0x780c, 0xa085, 0x0000,
+	0x7002, 0x7007, 0x0001, 0x7108, 0x8104, 0x00c8, 0x163b, 0x7007,
+	0x0002, 0xa184, 0x000c, 0x710c, 0xa184, 0x0300, 0x7003, 0x0000,
+	0x007c, 0x700c, 0xa084, 0x007f, 0x0040, 0x1655, 0x7007, 0x0004,
+	0x7004, 0xa084, 0x0004, 0x00c0, 0x1650, 0x7017, 0x0000, 0x7112,
+	0x721a, 0x731e, 0x2099, 0x0030, 0x8108, 0x81ac, 0x780c, 0xa085,
+	0x0001, 0x7002, 0x7007, 0x0001, 0x7008, 0x800c, 0x00c8, 0x1664,
+	0x7007, 0x0002, 0xa08c, 0x000c, 0x00c0, 0x1676, 0x710c, 0xa184,
+	0x0300, 0x00c0, 0x1676, 0x2ca0, 0x53a5, 0xa006, 0x7003, 0x0000,
+	0x007c, 0x7850, 0xa065, 0x0040, 0x1681, 0x2c04, 0x7852, 0x2063,
+	0x0000, 0x007c, 0x0f7e, 0x2079, 0x3500, 0x7850, 0x2062, 0x2c00,
+	0x7852, 0x0f7f, 0x007c, 0x2011, 0x4000, 0x7a52, 0x2019, 0x015c,
+	0x8319, 0x0040, 0x1699, 0xa280, 0x002f, 0x2012, 0x2010, 0x0078,
+	0x1690, 0x2013, 0x0000, 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784,
+	0x0007, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e8, 0x3680,
+	0x007c, 0x1078, 0x169c, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808,
+	0xa084, 0xffef, 0xa80d, 0x690a, 0x2009, 0x354f, 0x210c, 0x6804,
+	0xa005, 0x0040, 0x16c6, 0xa116, 0x00c0, 0x16c6, 0x2060, 0x6000,
+	0x6806, 0x017e, 0x200b, 0x0000, 0x0078, 0x16c9, 0x2009, 0x0000,
+	0x017e, 0x6804, 0xa065, 0x0040, 0x16d8, 0x6000, 0x6806, 0x1078,
+	0x16e9, 0x1078, 0x17d5, 0x6810, 0x8001, 0x6812, 0x00c0, 0x16c9,
+	0x017f, 0x6902, 0x6906, 0x007c, 0xa065, 0x0040, 0x16e8, 0x6098,
+	0x609b, 0x0000, 0x2008, 0x1078, 0x1682, 0x2100, 0x0078, 0x16dc,
+	0x007c, 0x6003, 0x0103, 0x20a9, 0x001c, 0xac80, 0x0004, 0x20a0,
+	0x2001, 0x0000, 0x40a4, 0x6828, 0x6016, 0x682c, 0x601e, 0x007c,
+	0x0e7e, 0x2071, 0x3540, 0x7040, 0xa08c, 0x0080, 0x00c0, 0x1706,
+	0xa088, 0x3580, 0x2d0a, 0x8000, 0x7042, 0xa006, 0x0e7f, 0x007c,
+	0x0e7e, 0x2071, 0x3540, 0x2009, 0x3580, 0x7240, 0x8221, 0x8211,
+	0x0048, 0x1720, 0x2104, 0x8108, 0xad06, 0x00c0, 0x170f, 0x8119,
+	0x211e, 0x8108, 0x8318, 0x8211, 0x00c8, 0x1718, 0x7442, 0xa006,
+	0x0e7f, 0x007c, 0x1078, 0x169c, 0x2091, 0x8000, 0x6804, 0x781e,
+	0xa065, 0x0040, 0x1769, 0x0078, 0x1733, 0x2c00, 0x781e, 0x6000,
+	0xa065, 0x0040, 0x1769, 0x600c, 0xa306, 0x00c0, 0x172d, 0x6008,
+	0xa206, 0x00c0, 0x172d, 0x2c28, 0x2001, 0x354f, 0x2004, 0xac06,
+	0x0040, 0x1769, 0x6804, 0xac06, 0x00c0, 0x1750, 0x6000, 0x2060,
+	0x6806, 0xa005, 0x00c0, 0x1750, 0x6803, 0x0000, 0x0078, 0x175a,
+	0x6400, 0x781c, 0x2060, 0x6402, 0xa486, 0x0000, 0x00c0, 0x175a,
+	0x2c00, 0x6802, 0x2560, 0x1078, 0x16e9, 0x6017, 0x0005, 0x601f,
+	0x0020, 0x1078, 0x17d5, 0x6810, 0x8001, 0x6812, 0x2001, 0xffff,
+	0xa005, 0x007c, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049, 0x0004,
+	0x2051, 0x0008, 0x2091, 0x8000, 0x1078, 0x16a9, 0x8738, 0xa784,
+	0x0007, 0x00c0, 0x1774, 0xa7bc, 0xff00, 0x873f, 0x8738, 0x873f,
+	0xa784, 0x0f00, 0x00c0, 0x1774, 0x2091, 0x8001, 0x007c, 0x2061,
+	0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1794, 0x78ac, 0x78af,
+	0x0000, 0xa005, 0x00c0, 0x1795, 0x007c, 0xa08c, 0xfff0, 0x0040,
+	0x179b, 0x1078, 0x1b81, 0x0079, 0x179d, 0x17ad, 0x17af, 0x17b5,
+	0x17b9, 0x17ad, 0x17bd, 0x17ad, 0x17ad, 0x17ad, 0x17ad, 0x17c3,
+	0x17c7, 0x17ad, 0x17ad, 0x17ad, 0x17ad, 0x1078, 0x1b81, 0x1078,
+	0x176a, 0x2001, 0x8001, 0x0078, 0x17cd, 0x2001, 0x8003, 0x0078,
+	0x17cd, 0x2001, 0x8004, 0x0078, 0x17cd, 0x1078, 0x176a, 0x2001,
+	0x8006, 0x0078, 0x17cd, 0x2001, 0x800c, 0x0078, 0x17cd, 0x1078,
+	0x176a, 0x2001, 0x800d, 0x0078, 0x17cd, 0x70c2, 0x2061, 0x0000,
+	0x601b, 0x0001, 0x2091, 0x4080, 0x007c, 0x2c04, 0x6082, 0x2c08,
+	0x2063, 0x0000, 0x7864, 0x8000, 0x7866, 0x7868, 0xa005, 0x796a,
+	0x0040, 0x17e5, 0x2c02, 0x0078, 0x17e6, 0x796e, 0x007c, 0x0c7e,
+	0x2061, 0x3500, 0x6883, 0x0103, 0x2d08, 0x206b, 0x0000, 0x6064,
+	0x8000, 0x6066, 0x6068, 0xa005, 0x616a, 0x0040, 0x17fa, 0x2d02,
+	0x0078, 0x17fb, 0x616e, 0x0c7f, 0x007c, 0x1078, 0x180e, 0x0040,
+	0x180d, 0x0c7e, 0x6098, 0xa065, 0x0040, 0x1808, 0x1078, 0x16dc,
+	0x0c7f, 0x609b, 0x0000, 0x1078, 0x1682, 0x007c, 0x786c, 0xa065,
+	0x0040, 0x1820, 0x2091, 0x8000, 0x7864, 0x8001, 0x7866, 0x2c04,
+	0x786e, 0xa005, 0x00c0, 0x181e, 0x786a, 0x8000, 0x2091, 0x8001,
+	0x007c, 0x7898, 0xa005, 0x00c0, 0x186f, 0x7974, 0x70d0, 0x0005,
+	0x0005, 0x72d0, 0xa206, 0x00c0, 0x1826, 0x2200, 0xa106, 0x00c0,
+	0x183d, 0x7804, 0xa005, 0x0040, 0x186f, 0x7807, 0x0000, 0x0068,
+	0x186f, 0x2091, 0x4080, 0x0078, 0x186f, 0x1078, 0x1679, 0x0040,
+	0x186f, 0x7a7c, 0x7b78, 0x8107, 0x8004, 0x8004, 0xa210, 0xa399,
+	0x0000, 0x2009, 0x0040, 0x1078, 0x1649, 0x0040, 0x1866, 0x1078,
+	0x1682, 0x7880, 0x8000, 0x7882, 0xa086, 0x0002, 0x00c0, 0x186f,
+	0x2091, 0x8000, 0x78af, 0x0002, 0x7883, 0x0000, 0x7898, 0xa085,
+	0x0003, 0x789a, 0x2091, 0x8001, 0x0078, 0x186f, 0x7883, 0x0000,
+	0x1078, 0x199c, 0x6000, 0xa084, 0x0007, 0x0079, 0x1870, 0x007c,
+	0x1878, 0x1887, 0x18a7, 0x1878, 0x18b9, 0x1878, 0x1878, 0x1878,
+	0x2039, 0x0400, 0x78a8, 0xa705, 0x78aa, 0x6004, 0xa705, 0x6006,
+	0x1078, 0x18f7, 0x6018, 0x78a6, 0x1078, 0x1984, 0x007c, 0x78a8,
+	0xa084, 0x0100, 0x0040, 0x188e, 0x0078, 0x1878, 0x78ab, 0x0000,
+	0x6000, 0x8007, 0xa084, 0x00ff, 0x789e, 0x8001, 0x609b, 0x0000,
+	0x0040, 0x18a4, 0x1078, 0x18f7, 0x0040, 0x18a4, 0x78a8, 0xa085,
+	0x0100, 0x78aa, 0x0078, 0x18a6, 0x1078, 0x191b, 0x007c, 0x78a8,
+	0xa08c, 0x0e00, 0x00c0, 0x18b0, 0xa084, 0x0100, 0x00c0, 0x18b2,
+	0x0078, 0x1878, 0x1078, 0x18f7, 0x00c0, 0x18b8, 0x1078, 0x191b,
+	0x007c, 0x78a8, 0xa084, 0x0100, 0x0040, 0x18c0, 0x0078, 0x1878,
+	0x78ab, 0x0000, 0x6710, 0x20a9, 0x0001, 0x6014, 0xa084, 0x00ff,
+	0xa005, 0x0040, 0x18dd, 0xa7bc, 0xff00, 0x20a9, 0x0008, 0xa08e,
+	0x0001, 0x0040, 0x18dd, 0x2039, 0x0000, 0x20a9, 0x0080, 0xa08e,
+	0x0002, 0x0040, 0x18dd, 0x0078, 0x18f4, 0x1078, 0x169c, 0x2d00,
+	0x2091, 0x8000, 0x682b, 0x0000, 0x682f, 0x0000, 0x6808, 0xa084,
+	0xffde, 0x680a, 0x2d00, 0xa080, 0x0010, 0x2068, 0x2091, 0x8001,
+	0x0070, 0x18f4, 0x0078, 0x18e0, 0x1078, 0x1682, 0x007c, 0x78a0,
+	0xa06d, 0x00c0, 0x1902, 0x2c00, 0x78a2, 0x78a6, 0x609b, 0x0000,
+	0x0078, 0x190e, 0x2c00, 0x689a, 0x609b, 0x0000, 0x78a2, 0x2d00,
+	0x6002, 0x78a4, 0xad06, 0x00c0, 0x190e, 0x6002, 0x789c, 0x8001,
+	0x789e, 0x00c0, 0x191a, 0x78a8, 0xa084, 0x0000, 0x78aa, 0x78a4,
+	0x2060, 0xa006, 0x007c, 0xa02e, 0x2530, 0x6118, 0xa184, 0x0060,
+	0x619e, 0x0040, 0x1927, 0x0e7e, 0x1078, 0x2f16, 0x0e7f, 0x6592,
+	0x65a2, 0x6696, 0x66a6, 0x60ab, 0x0000, 0x60af, 0x0000, 0x6710,
+	0x1078, 0x169c, 0x2091, 0x8000, 0x6808, 0xa084, 0x0001, 0x0040,
+	0x1949, 0x2091, 0x8001, 0x1078, 0x16e9, 0x2091, 0x8000, 0x1078,
+	0x17d5, 0x2091, 0x8001, 0x78a3, 0x0000, 0x78a7, 0x0000, 0x0078,
+	0x1983, 0x6020, 0xa096, 0x0001, 0x00c0, 0x1950, 0x8000, 0x6022,
+	0x6a10, 0x6814, 0x2091, 0x8001, 0xa202, 0x0048, 0x195f, 0x0040,
+	0x195f, 0x2039, 0x0200, 0x1078, 0x1984, 0x0078, 0x1983, 0x2c08,
+	0x2091, 0x8000, 0x6800, 0xa065, 0x0040, 0x1967, 0x6102, 0x6902,
+	0x00c0, 0x196b, 0x6906, 0x2160, 0x6003, 0x0000, 0x6810, 0x8000,
+	0x6812, 0x2091, 0x8001, 0x6808, 0xa08c, 0x0040, 0x0040, 0x197d,
+	0xa086, 0x0040, 0x680a, 0x1078, 0x16f8, 0x1078, 0x1a23, 0x78a7,
+	0x0000, 0x78a3, 0x0000, 0x007c, 0x6004, 0xa705, 0x6006, 0x2091,
+	0x8000, 0x1078, 0x17d5, 0x2091, 0x8001, 0x78a4, 0xa065, 0x0040,
+	0x1997, 0x6098, 0x78a6, 0x609b, 0x0000, 0x0078, 0x1987, 0x78a3,
+	0x0000, 0x78a7, 0x0000, 0x007c, 0x7970, 0x7874, 0x8000, 0xa10a,
+	0x00c8, 0x19a3, 0xa006, 0x7876, 0x70d2, 0x7804, 0xa005, 0x0040,
+	0x19b1, 0x8001, 0x7806, 0x00c0, 0x19b1, 0x0068, 0x19b1, 0x2091,
+	0x4080, 0x007c, 0x0068, 0x19cc, 0x2029, 0x0000, 0x786c, 0xa065,
+	0x0040, 0x19c7, 0x1078, 0x19cd, 0x0040, 0x19c7, 0x057e, 0x1078,
+	0x19e3, 0x057f, 0x00c0, 0x19c7, 0x8528, 0x0078, 0x19b6, 0x85ff,
+	0x0040, 0x19cc, 0x2091, 0x4080, 0x007c, 0x7b84, 0x7988, 0x72d4,
+	0x0005, 0x0005, 0x70d4, 0xa206, 0x00c0, 0x19cf, 0x2200, 0xa102,
+	0x00c0, 0x19dd, 0x2300, 0xa005, 0x007c, 0x0048, 0x19e1, 0xa302,
+	0x007c, 0x8002, 0x007c, 0x1078, 0x1a15, 0x2009, 0x001c, 0x6024,
+	0xa005, 0x0040, 0x19ed, 0x2009, 0x0040, 0x1078, 0x161b, 0x0040,
+	0x1a06, 0x7894, 0x8000, 0x7896, 0xa086, 0x0002, 0x00c0, 0x1a14,
+	0x2091, 0x8000, 0x78af, 0x0003, 0x7897, 0x0000, 0x7898, 0xa085,
+	0x0300, 0x789a, 0x2091, 0x8001, 0x0078, 0x1a14, 0x7897, 0x0000,
+	0x1078, 0x17fd, 0x7984, 0x7888, 0x8000, 0xa10a, 0x00c8, 0x1a11,
+	0xa006, 0x788a, 0x70d6, 0xa006, 0x007c, 0x8107, 0x8004, 0x8004,
+	0x7a90, 0x7b8c, 0xa210, 0xa399, 0x0000, 0x007c, 0x2009, 0x3568,
+	0x2091, 0x8000, 0x200a, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x3540,
+	0x2091, 0x8000, 0x2104, 0xa086, 0x0000, 0x00c0, 0x1a3e, 0x2009,
+	0x3512, 0x2104, 0xa005, 0x00c0, 0x1a3e, 0x7830, 0xa084, 0x00c0,
+	0x00c0, 0x1a3e, 0x0018, 0x1a3e, 0x781b, 0x0044, 0x2091, 0x8001,
+	0x0f7f, 0x007c, 0x127e, 0x2091, 0x2300, 0x2071, 0x3540, 0x2079,
+	0x0100, 0x2019, 0x2ddb, 0x20a1, 0x012b, 0x2304, 0xa005, 0x0040,
+	0x1a5a, 0x789a, 0x8318, 0x23ac, 0x8318, 0x2398, 0x53a6, 0x3318,
+	0x0078, 0x1a4d, 0x789b, 0x0020, 0x20a9, 0x0010, 0x78af, 0x0000,
+	0x78af, 0x0220, 0x0070, 0x1a66, 0x0078, 0x1a5e, 0x7003, 0x0000,
+	0x1078, 0x1b65, 0x7004, 0xa084, 0x000f, 0xa085, 0x6280, 0x7806,
+	0x780f, 0x9200, 0x7843, 0x00d8, 0x7853, 0x0080, 0x780b, 0x0008,
+	0x7047, 0x357f, 0x7043, 0x0000, 0x127f, 0x2000, 0x007c, 0xa18c,
+	0x000f, 0x2011, 0x0101, 0x2204, 0xa084, 0xfff0, 0xa105, 0x2012,
+	0x1078, 0x1b65, 0x007c, 0x2011, 0x0101, 0x20a9, 0x0009, 0x810b,
+	0x0070, 0x1a94, 0x0078, 0x1a8f, 0xa18c, 0x0e00, 0x2204, 0xa084,
+	0xf1ff, 0xa105, 0x2012, 0x007c, 0x2009, 0x0101, 0x20a9, 0x0005,
+	0x8213, 0x0070, 0x1aa5, 0x0078, 0x1aa0, 0xa294, 0x00e0, 0x2104,
+	0xa084, 0xff1f, 0xa205, 0x200a, 0x007c, 0x2011, 0x0101, 0x20a9,
+	0x000c, 0x810b, 0x0070, 0x1ab6, 0x0078, 0x1ab1, 0xa18c, 0xf000,
+	0x2204, 0xa084, 0x0fff, 0xa105, 0x2012, 0x007c, 0x2011, 0x0102,
+	0x2204, 0xa084, 0xffcf, 0xa105, 0x2012, 0x007c, 0x8103, 0x8003,
+	0xa080, 0x0020, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x62ac, 0x63ac,
+	0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0022, 0x0c7e, 0x2061,
+	0x0100, 0x609a, 0x60a4, 0xa084, 0xffdf, 0x60ae, 0x0c7f, 0x007c,
+	0x8103, 0x8003, 0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a,
+	0x60a4, 0xa085, 0x0020, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003,
+	0xa080, 0x0020, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0x62ae,
+	0x2010, 0x60a4, 0x63ae, 0x2018, 0x0c7f, 0x007c, 0x2091, 0x8000,
+	0x0c7e, 0x0e7e, 0x6818, 0xa005, 0x0040, 0x1b43, 0x2061, 0x3f80,
+	0x1078, 0x1b4b, 0x0040, 0x1b31, 0x20a9, 0x0000, 0x2061, 0x3e80,
+	0x0c7e, 0x1078, 0x1b4b, 0x0040, 0x1b1d, 0x0c7f, 0x8c60, 0x0070,
+	0x1b1b, 0x0078, 0x1b10, 0x0078, 0x1b43, 0x007f, 0xa082, 0x3e80,
+	0x2071, 0x3540, 0x70ba, 0x601c, 0xa085, 0x0800, 0x601e, 0x71b6,
+	0x60a7, 0x0000, 0x2001, 0x0004, 0x70a2, 0x1078, 0x1a1e, 0x0078,
+	0x1b3f, 0x2071, 0x3540, 0x601c, 0xa085, 0x0800, 0x601e, 0x71b6,
+	0x60a7, 0x0000, 0x2001, 0x0006, 0x70a2, 0x1078, 0x1a1e, 0x2001,
+	0x0000, 0x0078, 0x1b45, 0x2001, 0x0001, 0x2091, 0x8001, 0xa005,
+	0x0e7f, 0x0c7f, 0x007c, 0x2c04, 0xa005, 0x0040, 0x1b62, 0x2060,
+	0x600c, 0xa306, 0x00c0, 0x1b5f, 0x6008, 0xa206, 0x00c0, 0x1b5f,
+	0x6010, 0xa106, 0x00c0, 0x1b5f, 0xa006, 0x0078, 0x1b64, 0x6000,
+	0x0078, 0x1b4c, 0xa085, 0x0001, 0x007c, 0x2011, 0x3541, 0x220c,
+	0xa18c, 0x000f, 0x2011, 0x013b, 0x2204, 0xa084, 0x0100, 0x0040,
+	0x1b80, 0x2019, 0x0112, 0x201b, 0x1000, 0x2021, 0x013a, 0x2122,
+	0x2013, 0x0080, 0x2013, 0x008c, 0x2013, 0x0000, 0x201b, 0x0000,
+	0x007c, 0x0068, 0x1b81, 0x007e, 0x2071, 0x0000, 0x7018, 0xa084,
+	0x0001, 0x00c0, 0x1b86, 0x007f, 0x2e08, 0x2071, 0x0010, 0x70ca,
+	0x007f, 0x70c6, 0x70c3, 0x8002, 0x2071, 0x0000, 0x701b, 0x0001,
+	0x2091, 0x4080, 0x007f, 0x2070, 0x007f, 0x0078, 0x1b9d, 0x107e,
+	0x007e, 0x127e, 0x2091, 0x2300, 0x7f3c, 0x7e58, 0x7c30, 0x7d38,
+	0xa594, 0x003f, 0xa484, 0x4000, 0x0040, 0x1bb4, 0xa784, 0x007c,
+	0x00c0, 0x2da7, 0x1078, 0x1b81, 0xa49c, 0x000f, 0xa382, 0x0004,
+	0x0050, 0x1bbc, 0x1078, 0x1b81, 0x8507, 0xa084, 0x000f, 0x0079,
+	0x1bc1, 0x1fed, 0x209d, 0x20c3, 0x22e9, 0x2576, 0x25be, 0x25f5,
+	0x2670, 0x26ca, 0x274f, 0x1be7, 0x1bd1, 0x1e56, 0x1f20, 0x2555,
+	0x1bd1, 0x1078, 0x1b81, 0x0018, 0x1ba4, 0x127f, 0x2091, 0x8001,
+	0x007f, 0x107f, 0x007c, 0x7003, 0x0000, 0x703f, 0x0000, 0x7030,
+	0xa005, 0x0040, 0x1be5, 0x7033, 0x0000, 0x0018, 0x1ba4, 0x705c,
+	0xa005, 0x00c0, 0x1ca5, 0x70a0, 0xa084, 0x0007, 0x0079, 0x1bf0,
+	0x1cd1, 0x1bf8, 0x1c06, 0x1c27, 0x1c4d, 0x1c79, 0x1c77, 0x1bf8,
+	0x7808, 0xa084, 0xfffd, 0x780a, 0x2009, 0x0046, 0x1078, 0x241b,
+	0x00c0, 0x1c04, 0x7003, 0x0004, 0x0078, 0x1bd3, 0x1078, 0x2d69,
+	0x00c0, 0x1c25, 0x70b4, 0x8007, 0x789b, 0x007e, 0x78aa, 0x789b,
+	0x0010, 0x78ab, 0x000c, 0x789b, 0x0060, 0x78ab, 0x0001, 0x785b,
+	0x0004, 0x2009, 0x00f7, 0x1078, 0x2419, 0x00c0, 0x1c25, 0x7003,
+	0x0004, 0x70c3, 0x000f, 0x7033, 0x3570, 0x0078, 0x1bd3, 0x1078,
+	0x2d69, 0x00c0, 0x1c4b, 0x71b4, 0x8107, 0x789b, 0x007e, 0x78aa,
+	0x789b, 0x0010, 0xa18c, 0x0007, 0xa18d, 0x00c0, 0x79aa, 0x78ab,
+	0x0006, 0x789b, 0x0060, 0x78ab, 0x0002, 0x785b, 0x0004, 0x2009,
+	0x00f7, 0x1078, 0x2419, 0x00c0, 0x1c4b, 0x7003, 0x0004, 0x70c3,
+	0x000f, 0x7033, 0x3570, 0x0078, 0x1bd3, 0x1078, 0x2d69, 0x00c0,
+	0x1c75, 0x71b4, 0x8107, 0x789b, 0x007e, 0x78aa, 0x789b, 0x0010,
+	0xa18c, 0x0007, 0xa18d, 0x00c0, 0x79aa, 0x78ab, 0x0020, 0x71b8,
+	0x79aa, 0x78ab, 0x000d, 0x789b, 0x0060, 0x78ab, 0x0004, 0x785b,
+	0x0004, 0x2009, 0x00f7, 0x1078, 0x2419, 0x00c0, 0x1c75, 0x7003,
+	0x0004, 0x70c3, 0x000f, 0x7033, 0x3570, 0x0078, 0x1bd3, 0x0078,
+	0x1c27, 0x1078, 0x2d69, 0x00c0, 0x1bd3, 0x70bc, 0x2068, 0x789b,
+	0x0010, 0x6f10, 0x1078, 0x2cac, 0x2c50, 0x6810, 0x007e, 0x8007,
+	0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600, 0x2048,
+	0x0c7e, 0x2060, 0x704a, 0x6000, 0x704e, 0x6004, 0x7052, 0x0c7f,
+	0x007f, 0xa084, 0x0007, 0xa085, 0x0080, 0x78aa, 0x6e18, 0x2041,
+	0x0001, 0x2001, 0x0004, 0x0078, 0x1dcc, 0x1078, 0x2d69, 0x00c0,
+	0x1bd3, 0x789b, 0x0010, 0x705c, 0x2068, 0x6f10, 0x1078, 0x2cac,
+	0x2c50, 0x6008, 0xa085, 0x0010, 0x600a, 0x6820, 0xa005, 0x0040,
+	0x1cc1, 0xa082, 0x0006, 0x0048, 0x1cbf, 0x0078, 0x1cc1, 0x6823,
+	0x0005, 0x6810, 0xa084, 0x0007, 0xa085, 0x0080, 0x78aa, 0x2031,
+	0x0020, 0x2041, 0x0001, 0x1078, 0x2dc8, 0x2001, 0x0003, 0x0078,
+	0x1dcc, 0x0018, 0x1ba4, 0x7440, 0xa485, 0x0000, 0x0040, 0x1ceb,
+	0xa080, 0x3580, 0x2030, 0x7144, 0x8108, 0xa12a, 0x0048, 0x1ce2,
+	0x2009, 0x3580, 0x2164, 0x6504, 0x85ff, 0x00c0, 0x1cf8, 0x8421,
+	0x00c0, 0x1cdc, 0x7146, 0x7003, 0x0000, 0x703f, 0x0000, 0x0078,
+	0x1bd3, 0x7640, 0xa6b0, 0x3580, 0x7144, 0x2600, 0x0078, 0x1ce7,
+	0x7146, 0x2568, 0x2558, 0x753e, 0x2c50, 0x6034, 0xa085, 0x0000,
+	0x00c0, 0x1cf5, 0x6708, 0x7736, 0xa784, 0x013f, 0x0040, 0x1d2a,
+	0xa784, 0x0021, 0x00c0, 0x1cf5, 0xa784, 0x0002, 0x0040, 0x1d17,
+	0xa784, 0x0004, 0x0040, 0x1cf5, 0xa7bc, 0xfffb, 0x670a, 0xa784,
+	0x0008, 0x00c0, 0x1cf5, 0xa784, 0x0010, 0x00c0, 0x1cf5, 0xa784,
+	0x0100, 0x0040, 0x1d2a, 0x6018, 0xa005, 0x00c0, 0x1cf5, 0xa7bc,
+	0xfeff, 0x670a, 0x681f, 0x0000, 0x6e18, 0xa684, 0x000e, 0x6118,
+	0x0040, 0x1d3a, 0x601c, 0xa102, 0x0048, 0x1d3d, 0x0040, 0x1d3d,
+	0x0078, 0x1cf1, 0x81ff, 0x00c0, 0x1cf1, 0xa784, 0x0080, 0x00c0,
+	0x1d43, 0x700c, 0x6022, 0xa7bc, 0xff7f, 0x670a, 0x6b10, 0x8307,
+	0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3600, 0x2060,
+	0x2048, 0x704a, 0x6000, 0x704e, 0x6004, 0x7052, 0x2a60, 0x0018,
+	0x1ba4, 0x789b, 0x0010, 0xa046, 0x1078, 0x2d69, 0x00c0, 0x1bd3,
+	0x6b10, 0xa39c, 0x0007, 0xa39d, 0x00c0, 0x704c, 0xa084, 0x8000,
+	0x0040, 0x1d6e, 0xa684, 0x0001, 0x0040, 0x1d74, 0xa39c, 0xffbf,
+	0xa684, 0x000e, 0x00c0, 0x2901, 0xa684, 0x0010, 0x0040, 0x1d7e,
+	0xa39d, 0x0020, 0xa684, 0x000e, 0x00c0, 0x2907, 0x7baa, 0x8840,
+	0xa684, 0x000e, 0x00c0, 0x1d89, 0xa7bd, 0x0010, 0x670a, 0x0078,
+	0x1dca, 0x714c, 0xa18c, 0x0800, 0x0040, 0x290d, 0x2011, 0x0021,
+	0x8004, 0x8004, 0x0048, 0x1da0, 0x2011, 0x0022, 0x8004, 0x0048,
+	0x1da0, 0x2011, 0x0020, 0x8004, 0x0048, 0x1da0, 0x0040, 0x1dca,
+	0x7aaa, 0x8840, 0x1078, 0x2d82, 0x6a10, 0x610c, 0x8108, 0xa18c,
+	0x00ff, 0xa1e0, 0x3e80, 0x2c64, 0x8cff, 0x0040, 0x1dc1, 0x6010,
+	0xa206, 0x00c0, 0x1dab, 0x60b4, 0x8001, 0x60b6, 0x00c0, 0x1da6,
+	0x0c7e, 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078,
+	0x1cd1, 0x1078, 0x2d69, 0x00c0, 0x1bd3, 0x2a60, 0x610e, 0x79aa,
+	0x8840, 0x712e, 0x2001, 0x0001, 0x007e, 0x7150, 0xa184, 0x0018,
+	0x0040, 0x1de0, 0xa184, 0x0010, 0x0040, 0x1dda, 0x1078, 0x2ad7,
+	0x00c0, 0x1de0, 0xa184, 0x0008, 0x0040, 0x1de0, 0x1078, 0x29f1,
+	0x007f, 0x7002, 0xa68c, 0x0060, 0x88ff, 0x0040, 0x1de9, 0xa18d,
+	0x0004, 0x795a, 0x69b2, 0x789b, 0x0060, 0x2800, 0x78aa, 0x789b,
+	0x0061, 0x6814, 0xa085, 0x8000, 0x6816, 0x78aa, 0x157e, 0x137e,
+	0x147e, 0x20a1, 0x012c, 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80,
+	0x000a, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x6810, 0x8007,
+	0x789b, 0x007e, 0x78aa, 0x6d90, 0x7dd6, 0x7dde, 0x6e94, 0x7ed2,
+	0x7eda, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x1e18, 0x0098, 0x1e20,
+	0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x2d82, 0x0078, 0x1bdb,
+	0x7200, 0xa284, 0x0007, 0xa086, 0x0001, 0x00c0, 0x1e2d, 0x781b,
+	0x0049, 0x1078, 0x2d82, 0x0078, 0x1e3e, 0x6ab0, 0xa295, 0x2000,
+	0x7a5a, 0x781b, 0x0049, 0x1078, 0x2d82, 0x7200, 0x2500, 0xa605,
+	0x0040, 0x1e3e, 0xa284, 0x0007, 0x1079, 0x1e4c, 0xad80, 0x0008,
+	0x7032, 0xa284, 0x0007, 0xa086, 0x0001, 0x00c0, 0x1e4a, 0x6018,
+	0x8000, 0x601a, 0x0078, 0x1bd3, 0x1e54, 0x30f3, 0x30f3, 0x30e2,
+	0x30f3, 0x1e54, 0x1e54, 0x1e54, 0x1078, 0x1b81, 0x7808, 0xa084,
+	0xfffd, 0x780a, 0x0f7e, 0x2079, 0x3500, 0x7898, 0x0f7f, 0xa084,
+	0x0001, 0x0040, 0x1e7c, 0x70a0, 0xa086, 0x0001, 0x00c0, 0x1e6b,
+	0x70a2, 0x0078, 0x1f04, 0x70a0, 0xa086, 0x0005, 0x00c0, 0x1e7a,
+	0x70bc, 0x2068, 0x6817, 0x0004, 0x6813, 0x0000, 0x681c, 0xa085,
+	0x0008, 0x681e, 0x70a3, 0x0000, 0x157e, 0x2011, 0x0004, 0x71a0,
+	0xa186, 0x0001, 0x0040, 0x1e9e, 0xa186, 0x0007, 0x00c0, 0x1e8e,
+	0x2009, 0x352b, 0x200b, 0x0005, 0x0078, 0x1e9e, 0x2009, 0x3513,
+	0x2104, 0x2009, 0x3512, 0x200a, 0x2009, 0x352b, 0x200b, 0x0001,
+	0x70a3, 0x0000, 0x70a7, 0x0001, 0x0078, 0x1ea0, 0x70a3, 0x0000,
+	0x1078, 0x2eca, 0x20a9, 0x0010, 0x2039, 0x0000, 0x1078, 0x2bb1,
+	0xa7b8, 0x0100, 0x0070, 0x1eae, 0x0078, 0x1ea6, 0x7000, 0x2020,
+	0x0079, 0x1eb2, 0x1ee0, 0x1ec9, 0x1ec9, 0x1ebc, 0x1ee0, 0x1ee0,
+	0x1eba, 0x1eba, 0x1078, 0x1b81, 0x2021, 0x3557, 0x2404, 0xa005,
+	0x0040, 0x1ec9, 0xad06, 0x00c0, 0x1ec9, 0x6800, 0x2022, 0x0078,
+	0x1ed9, 0x681c, 0xa084, 0x0001, 0x00c0, 0x1ed5, 0x6f10, 0x1078,
+	0x2cac, 0x1078, 0x28e4, 0x0078, 0x1ed9, 0x7054, 0x2060, 0x6800,
+	0x6002, 0x6a16, 0x681c, 0xa085, 0x0008, 0x681e, 0x1078, 0x17e7,
+	0x2021, 0x3f80, 0x1078, 0x1f0a, 0x2021, 0x3557, 0x1078, 0x1f0a,
+	0x20a9, 0x0000, 0x2021, 0x3e80, 0x1078, 0x1f0a, 0x8420, 0x0070,
+	0x1ef3, 0x0078, 0x1eec, 0x20a9, 0x0080, 0x2061, 0x3680, 0x6018,
+	0x6110, 0xa102, 0x6012, 0x601b, 0x0000, 0xace0, 0x0010, 0x0070,
+	0x1f03, 0x0078, 0x1ef7, 0x157f, 0x7003, 0x0000, 0x703f, 0x0000,
+	0x0078, 0x1bd3, 0x047e, 0x2404, 0xa005, 0x0040, 0x1f1c, 0x2068,
+	0x6800, 0x007e, 0x6a16, 0x681c, 0xa085, 0x0008, 0x681e, 0x1078,
+	0x17e7, 0x007f, 0x0078, 0x1f0c, 0x047f, 0x2023, 0x0000, 0x007c,
+	0xa282, 0x0003, 0x0050, 0x1f26, 0x1078, 0x1b81, 0x2300, 0x0079,
+	0x1f29, 0x1f2c, 0x1f9f, 0x1fad, 0xa282, 0x0002, 0x0040, 0x1f32,
+	0x1078, 0x1b81, 0x70a0, 0x70a3, 0x0000, 0x70c3, 0x0000, 0x0079,
+	0x1f39, 0x1f41, 0x1f41, 0x1f43, 0x1f77, 0x2913, 0x1f41, 0x1f77,
+	0x1f41, 0x1078, 0x1b81, 0x77b4, 0x1078, 0x2bb1, 0x77b4, 0xa7bc,
+	0x0f00, 0x1078, 0x2cac, 0x6018, 0xa005, 0x0040, 0x1f6e, 0x2021,
+	0x3f80, 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x1fc8, 0x0040,
+	0x1f6e, 0x157e, 0x20a9, 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009,
+	0x0004, 0x2011, 0x0010, 0x1078, 0x1fc8, 0x047f, 0x0040, 0x1f6d,
+	0x8420, 0x0070, 0x1f6d, 0x0078, 0x1f5e, 0x157f, 0x8738, 0xa784,
+	0x0007, 0x00c0, 0x1f49, 0x0078, 0x1bdb, 0x0078, 0x1bdb, 0x77b4,
+	0x1078, 0x2cac, 0x6018, 0xa005, 0x0040, 0x1f9d, 0x2021, 0x3f80,
+	0x2009, 0x0005, 0x2011, 0x0020, 0x1078, 0x1fc8, 0x0040, 0x1f9d,
+	0x157e, 0x20a9, 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009, 0x0005,
+	0x2011, 0x0020, 0x1078, 0x1fc8, 0x047f, 0x0040, 0x1f9c, 0x8420,
+	0x0070, 0x1f9c, 0x0078, 0x1f8d, 0x157f, 0x0078, 0x1bdb, 0x2200,
+	0x0079, 0x1fa2, 0x1fa5, 0x1fa7, 0x1fa7, 0x1078, 0x1b81, 0x70a3,
+	0x0000, 0x70a7, 0x0001, 0x0078, 0x1bd3, 0x2200, 0x0079, 0x1fb0,
+	0x1fb5, 0x1fa7, 0x1fb3, 0x1078, 0x1b81, 0x1078, 0x2428, 0x7000,
+	0xa086, 0x0001, 0x00c0, 0x28ba, 0x1078, 0x28fa, 0x6008, 0xa084,
+	0xffef, 0x600a, 0x1078, 0x28ad, 0x0040, 0x28ba, 0x0078, 0x1cd1,
+	0x2404, 0xa005, 0x0040, 0x1fe9, 0x2068, 0x2d04, 0x007e, 0x6810,
+	0xa706, 0x0040, 0x1fd7, 0x2d20, 0x007f, 0x0078, 0x1fc9, 0x007f,
+	0x2022, 0x6916, 0x681c, 0xa205, 0x681e, 0x1078, 0x17e7, 0x6010,
+	0x8001, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x28fa,
+	0x007c, 0xa085, 0x0001, 0x0078, 0x1fe8, 0x2300, 0x0079, 0x1ff0,
+	0x1ff5, 0x1ff3, 0x2038, 0x1078, 0x1b81, 0x78e4, 0xa005, 0x00d0,
+	0x2018, 0x0018, 0x2018, 0x2008, 0xa084, 0x0030, 0x00c0, 0x2004,
+	0x781b, 0x0049, 0x0078, 0x1bd3, 0x78ec, 0xa084, 0x0003, 0x0040,
+	0x2000, 0x2100, 0xa084, 0x0007, 0x0079, 0x200e, 0x2026, 0x202c,
+	0x2020, 0x2016, 0x2d63, 0x2d63, 0x2016, 0x2032, 0x1078, 0x1b81,
+	0x7000, 0xa005, 0x0040, 0x1bdb, 0x2001, 0x0003, 0x0078, 0x22fd,
+	0x1078, 0x2b94, 0x781b, 0x0055, 0x0078, 0x1bd3, 0x1078, 0x2b94,
+	0x781b, 0x00dc, 0x0078, 0x1bd3, 0x1078, 0x2b94, 0x781b, 0x00e3,
+	0x0078, 0x1bd3, 0x1078, 0x2b94, 0x781b, 0x009d, 0x0078, 0x1bd3,
+	0xa584, 0x000f, 0x00c0, 0x2062, 0x1078, 0x2428, 0x7000, 0x0079,
+	0x2041, 0x2049, 0x2056, 0x2049, 0x28ba, 0x204b, 0x28ba, 0x2049,
+	0x2049, 0x1078, 0x1b81, 0x71a0, 0x70a3, 0x0000, 0xa186, 0x0004,
+	0x00c0, 0x2054, 0x0078, 0x2913, 0x0078, 0x28ba, 0x1078, 0x28fa,
+	0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x28ad, 0x0040, 0x28ba,
+	0x0078, 0x1cd1, 0x78e4, 0xa005, 0x00d0, 0x2018, 0x0018, 0x2018,
+	0x2008, 0xa084, 0x0030, 0x00c0, 0x2071, 0x781b, 0x0049, 0x0078,
+	0x1bd3, 0x78ec, 0xa084, 0x0003, 0x0040, 0x206d, 0x2100, 0xa184,
+	0x0007, 0x0079, 0x207b, 0x208b, 0x2091, 0x2085, 0x2083, 0x2d63,
+	0x2d63, 0x2083, 0x2d5b, 0x1078, 0x1b81, 0x1078, 0x2b9c, 0x781b,
+	0x0055, 0x0078, 0x1bd3, 0x1078, 0x2b9c, 0x781b, 0x00dc, 0x0078,
+	0x1bd3, 0x1078, 0x2b9c, 0x781b, 0x00e3, 0x0078, 0x1bd3, 0x1078,
+	0x2b9c, 0x781b, 0x009d, 0x0078, 0x1bd3, 0x2300, 0x0079, 0x20a0,
+	0x20a5, 0x20a3, 0x20a7, 0x1078, 0x1b81, 0x0078, 0x2670, 0x6817,
+	0x0008, 0x78a3, 0x0000, 0x79e4, 0xa184, 0x0030, 0x0040, 0x2670,
+	0x78ec, 0xa084, 0x0003, 0x0040, 0x2670, 0xa184, 0x0007, 0x0079,
+	0x20b9, 0x2026, 0x202c, 0x2020, 0x2d3b, 0x2d63, 0x2d63, 0x20c1,
+	0x2d5b, 0x1078, 0x1b81, 0xa282, 0x0005, 0x0050, 0x20c9, 0x1078,
+	0x1b81, 0x2300, 0x0079, 0x20cc, 0x20cf, 0x22d1, 0x22dd, 0x2200,
+	0x0079, 0x20d2, 0x20d7, 0x20d9, 0x20ec, 0x20d7, 0x22b6, 0x1078,
+	0x1b81, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa082, 0x0020,
+	0x0048, 0x2b75, 0xa08a, 0x0004, 0x00c8, 0x2b75, 0x0079, 0x20e8,
+	0x2b75, 0x2b75, 0x2b75, 0x2b17, 0x789b, 0x0018, 0x79a8, 0xa184,
+	0x0080, 0x0040, 0x2101, 0xa184, 0x0018, 0x0040, 0x20fd, 0x0078,
+	0x2b75, 0x7000, 0xa005, 0x00c0, 0x20f7, 0x2011, 0x0003, 0x0078,
+	0x275d, 0xa184, 0x00ff, 0xa08a, 0x0010, 0x00c8, 0x2b75, 0x0079,
+	0x2109, 0x211b, 0x2119, 0x2131, 0x2133, 0x21c5, 0x2b75, 0x2b75,
+	0x21c7, 0x2b75, 0x2b75, 0x22b2, 0x22b2, 0x2b75, 0x2b75, 0x2b75,
+	0x22b4, 0x1078, 0x1b81, 0xa684, 0x1000, 0x0040, 0x2128, 0x2001,
+	0x0300, 0x8000, 0x8000, 0x783a, 0x781b, 0x009a, 0x0078, 0x1bd3,
+	0x6814, 0xa084, 0x8000, 0x0040, 0x212f, 0x6817, 0x0003, 0x0078,
+	0x2d3b, 0x1078, 0x1b81, 0x691c, 0x691e, 0xa684, 0x1800, 0x00c0,
+	0x214d, 0x681c, 0xa084, 0x0001, 0x00c0, 0x2155, 0x6814, 0xa086,
+	0x0008, 0x00c0, 0x2145, 0x6817, 0x0000, 0xa684, 0x0400, 0x0040,
+	0x21c1, 0x781b, 0x0058, 0x0078, 0x1bd3, 0xa684, 0x1000, 0x0040,
+	0x2155, 0x781b, 0x0058, 0x0078, 0x1bd3, 0xa684, 0x0060, 0x0040,
+	0x21bd, 0xa684, 0x0800, 0x0040, 0x21bd, 0xa684, 0x8000, 0x00c0,
+	0x2163, 0x0078, 0x217d, 0xa6b4, 0x7fff, 0x7e5a, 0x6eb2, 0x789b,
+	0x0074, 0x7aac, 0x79ac, 0x78ac, 0x801b, 0x00c8, 0x2170, 0x8000,
+	0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x6b94, 0x2100, 0xa302,
+	0x68ae, 0x6b90, 0x2200, 0xa303, 0x68aa, 0xa684, 0x4000, 0x0040,
+	0x2185, 0xa6b4, 0xbfff, 0x7e5a, 0x6eb2, 0x7000, 0xa086, 0x0003,
+	0x00c0, 0x2192, 0x1078, 0x2f3d, 0x1078, 0x30e2, 0x781b, 0x0067,
+	0x0078, 0x1bd3, 0xa006, 0x1078, 0x3197, 0x6aac, 0x69a8, 0x6c94,
+	0x6b90, 0x2200, 0xa105, 0x0040, 0x21a1, 0x2200, 0xa422, 0x2100,
+	0xa31b, 0x7cd2, 0x7bd6, 0x2300, 0xa405, 0x00c0, 0x21af, 0xa6b5,
+	0x4000, 0x7e5a, 0x6eb2, 0x781b, 0x0067, 0x0078, 0x1bd3, 0x781b,
+	0x0067, 0x2200, 0xa115, 0x00c0, 0x21b9, 0x1078, 0x30f3, 0x0078,
+	0x1bd3, 0x1078, 0x3120, 0x0078, 0x1bd3, 0x781b, 0x006a, 0x0078,
+	0x1bd3, 0x781b, 0x0058, 0x0078, 0x1bd3, 0x1078, 0x1b81, 0x0078,
+	0x2224, 0x691c, 0xa184, 0x0100, 0x0040, 0x21df, 0xa18c, 0xfeff,
+	0x691e, 0x0c7e, 0x7048, 0x2060, 0x6000, 0xa084, 0xefff, 0x6002,
+	0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x0078, 0x2213, 0xa184,
+	0x0200, 0x0040, 0x2213, 0xa18c, 0xfdff, 0x691e, 0x0c7e, 0x7048,
+	0x2060, 0x6000, 0xa084, 0xdfff, 0x6002, 0x6004, 0xa084, 0xffef,
+	0x6006, 0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2213,
+	0x1078, 0x2ca8, 0x1078, 0x29f1, 0x88ff, 0x0040, 0x2213, 0x789b,
+	0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684,
+	0x0400, 0x00c0, 0x220f, 0x781b, 0x0055, 0x0078, 0x1bd3, 0x781b,
+	0x0069, 0x0078, 0x1bd3, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x221c,
+	0x781b, 0x0058, 0x0078, 0x1bd3, 0x781b, 0x006a, 0x0078, 0x1bd3,
+	0x0078, 0x2b7b, 0x0078, 0x2b7b, 0x2019, 0x0000, 0x7990, 0xa18c,
+	0x0007, 0x0040, 0x2222, 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff,
+	0xa286, 0x0001, 0x00c0, 0x2247, 0x2300, 0x7ca8, 0xa400, 0x2018,
+	0xa102, 0x0040, 0x223f, 0x0048, 0x223f, 0x0078, 0x2241, 0x0078,
+	0x21c9, 0x24a8, 0x7aa8, 0x00f0, 0x2241, 0x0078, 0x222d, 0xa284,
+	0x00f0, 0xa086, 0x0020, 0x00c0, 0x22a3, 0x8318, 0x8318, 0x2300,
+	0xa102, 0x0040, 0x2257, 0x0048, 0x2257, 0x0078, 0x22a0, 0xa286,
+	0x0023, 0x0040, 0x2222, 0x6818, 0xa084, 0xfff1, 0x681a, 0x7e58,
+	0xa684, 0xfff1, 0xa085, 0x0010, 0x2030, 0x7e5a, 0x6008, 0xa085,
+	0x0010, 0x600a, 0x0c7e, 0x7048, 0x2060, 0x6004, 0x2008, 0x2c48,
+	0x0c7f, 0xa184, 0x0010, 0x0040, 0x227b, 0x1078, 0x2ca8, 0x1078,
+	0x2ad7, 0x0078, 0x228a, 0x0c7e, 0x7048, 0x2060, 0x6004, 0x2008,
+	0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2213, 0x1078, 0x2ca8,
+	0x1078, 0x29f1, 0x88ff, 0x0040, 0x2213, 0x789b, 0x0060, 0x2800,
+	0x78aa, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x229c,
+	0x781b, 0x0055, 0x0078, 0x1bd3, 0x781b, 0x0069, 0x0078, 0x1bd3,
+	0x7aa8, 0x0078, 0x222d, 0x8318, 0x2300, 0xa102, 0x0040, 0x22ac,
+	0x0048, 0x22ac, 0x0078, 0x222d, 0xa284, 0x0080, 0x00c0, 0x2b81,
+	0x0078, 0x2b7b, 0x0078, 0x2b81, 0x0078, 0x2b75, 0x789b, 0x0018,
+	0x78a8, 0xa084, 0x00ff, 0xa08e, 0x0001, 0x0040, 0x22c1, 0x1078,
+	0x1b81, 0x7aa8, 0xa294, 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a,
+	0x0004, 0x00c8, 0x2b75, 0x0079, 0x22cd, 0x2b75, 0x2944, 0x2b75,
+	0x2a72, 0xa282, 0x0000, 0x00c0, 0x22d7, 0x1078, 0x1b81, 0x1078,
+	0x2b94, 0x781b, 0x0069, 0x0078, 0x1bd3, 0xa282, 0x0003, 0x00c0,
+	0x22e3, 0x1078, 0x1b81, 0x1078, 0x2ba4, 0x781b, 0x0069, 0x0078,
+	0x1bd3, 0xa282, 0x0004, 0x0050, 0x22ef, 0x1078, 0x1b81, 0x2300,
+	0x0079, 0x22f2, 0x22f5, 0x23d2, 0x2403, 0xa286, 0x0003, 0x0040,
+	0x22fb, 0x1078, 0x1b81, 0x2001, 0x0000, 0x703a, 0x7000, 0xa084,
+	0x0007, 0x0079, 0x2303, 0x230b, 0x230d, 0x230d, 0x2513, 0x253b,
+	0x24dd, 0x230b, 0x230b, 0x1078, 0x1b81, 0xa684, 0x1000, 0x00c0,
+	0x2315, 0x1078, 0x2eca, 0x0040, 0x23ac, 0x7868, 0xa08c, 0x00ff,
+	0x0040, 0x235d, 0xa186, 0x0008, 0x00c0, 0x232c, 0x1078, 0x28fa,
+	0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x28ad, 0x0040, 0x235d,
+	0x1078, 0x2eca, 0x0078, 0x2344, 0xa186, 0x0028, 0x00c0, 0x235d,
+	0x1078, 0x2eca, 0x6008, 0xa084, 0xffef, 0x600a, 0x6018, 0xa005,
+	0x0040, 0x2344, 0x8001, 0x601a, 0xa005, 0x0040, 0x2344, 0x8001,
+	0xa005, 0x0040, 0x2344, 0x601e, 0x681c, 0xa084, 0x0001, 0x0040,
+	0x1bdb, 0x681c, 0xa084, 0xfffe, 0x681e, 0x7054, 0x0c7e, 0x2060,
+	0x6800, 0x6002, 0x0c7f, 0x6004, 0x6802, 0xa005, 0x2d00, 0x00c0,
+	0x235a, 0x6002, 0x6006, 0x0078, 0x1bdb, 0x017e, 0x1078, 0x2428,
+	0x017f, 0xa684, 0xdf00, 0x681a, 0x6827, 0x0000, 0x6f10, 0x81ff,
+	0x0040, 0x23ac, 0xa186, 0x0002, 0x00c0, 0x23a4, 0xa684, 0x0800,
+	0x00c0, 0x237a, 0xa684, 0x0060, 0x0040, 0x237a, 0x78d8, 0x7adc,
+	0x682e, 0x6a2a, 0x8717, 0xa294, 0x000f, 0x8213, 0x8213, 0x8213,
+	0xa290, 0x3600, 0xa290, 0x0000, 0x221c, 0xa384, 0x0100, 0x00c0,
+	0x238b, 0x0078, 0x2391, 0x8210, 0x2204, 0xa085, 0x0018, 0x2012,
+	0x8211, 0xa384, 0x0400, 0x0040, 0x239e, 0x689c, 0xa084, 0x0100,
+	0x00c0, 0x239e, 0x1078, 0x249c, 0x0078, 0x1bdb, 0x6008, 0xa085,
+	0x0002, 0x600a, 0x0078, 0x23ac, 0xa186, 0x0018, 0x0040, 0x23ac,
+	0xa186, 0x0014, 0x0040, 0x1bdb, 0x6912, 0x6814, 0xa084, 0x8000,
+	0x0040, 0x23b4, 0x7038, 0x6816, 0xa68c, 0xdf00, 0x691a, 0x1078,
+	0x28eb, 0x1078, 0x28fa, 0x00c0, 0x23c1, 0x6008, 0xa084, 0xffef,
+	0x600a, 0x681c, 0xa084, 0x0001, 0x00c0, 0x23ca, 0x1078, 0x28e4,
+	0x0078, 0x23ce, 0x7054, 0x2060, 0x6800, 0x6002, 0x1078, 0x17e7,
+	0x0078, 0x1bdb, 0xa282, 0x0004, 0x0048, 0x23d8, 0x1078, 0x1b81,
+	0x2200, 0x0079, 0x23db, 0x23df, 0x23e1, 0x23ee, 0x23e1, 0x1078,
+	0x1b81, 0x7000, 0xa086, 0x0005, 0x0040, 0x23ea, 0x1078, 0x2b94,
+	0x781b, 0x0069, 0x781b, 0x006a, 0x0078, 0x1bd3, 0x7890, 0x8007,
+	0x8001, 0xa084, 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c,
+	0x00ff, 0xa186, 0x0003, 0x0040, 0x23ff, 0x0078, 0x2b75, 0x781b,
+	0x006a, 0x0078, 0x1bd3, 0x681c, 0xa085, 0x0004, 0x681e, 0x82ff,
+	0x00c0, 0x240e, 0x1078, 0x2b94, 0x0078, 0x2415, 0x8211, 0x0040,
+	0x2413, 0x1078, 0x1b81, 0x1078, 0x2ba4, 0x781b, 0x0069, 0x0078,
+	0x1bd3, 0x1078, 0x2d82, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2425,
+	0x0018, 0x2425, 0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c,
+	0xa684, 0x0060, 0x00c0, 0x2432, 0x682f, 0x0000, 0x682b, 0x0000,
+	0x0078, 0x249b, 0xa684, 0x0800, 0x00c0, 0x2441, 0x68b0, 0xa084,
+	0x4800, 0xa635, 0xa684, 0x0800, 0x00c0, 0x2441, 0x1078, 0x2eca,
+	0x007c, 0xa684, 0x0020, 0x0040, 0x246d, 0x78d0, 0x8003, 0x00c8,
+	0x244f, 0xa006, 0x1078, 0x3197, 0x78d4, 0x1078, 0x31fc, 0xa684,
+	0x4000, 0x0040, 0x2459, 0x682f, 0x0000, 0x682b, 0x0000, 0x0078,
+	0x243e, 0x68b0, 0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0,
+	0x2453, 0x7038, 0xa005, 0x00c0, 0x2467, 0x703b, 0x0007, 0x79d8,
+	0x7adc, 0x692e, 0x6a2a, 0x0078, 0x243e, 0xa684, 0x4000, 0x0040,
+	0x2477, 0x682f, 0x0000, 0x682b, 0x0000, 0x0078, 0x243e, 0x68b0,
+	0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2471, 0x7038,
+	0xa005, 0x00c0, 0x2485, 0x703b, 0x0007, 0x79d8, 0x7adc, 0x78d0,
+	0x80f3, 0x00c8, 0x248c, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291,
+	0x0000, 0x692e, 0x6a2a, 0x2100, 0xa205, 0x00c0, 0x2499, 0x0078,
+	0x243e, 0x1078, 0x3197, 0x007c, 0xa384, 0x0200, 0x0040, 0x24a4,
+	0x6008, 0xa085, 0x0002, 0x600a, 0x6817, 0x0006, 0x6a28, 0x692c,
+	0x6a3a, 0x693e, 0x682b, 0x0300, 0x682f, 0x0000, 0x6833, 0x2000,
+	0x6893, 0x0000, 0x6897, 0x0020, 0x7000, 0x0079, 0x24b7, 0x24bf,
+	0x24c1, 0x24ca, 0x24bf, 0x24bf, 0x24bf, 0x24bf, 0x24bf, 0x1078,
+	0x1b81, 0x681c, 0xa084, 0x0001, 0x00c0, 0x24ca, 0x1078, 0x28e4,
+	0x0078, 0x24d0, 0x7054, 0x2c50, 0x2060, 0x6800, 0x6002, 0x2a60,
+	0x2021, 0x3557, 0x2404, 0xa005, 0x0040, 0x24d9, 0x2020, 0x0078,
+	0x24d2, 0x2d22, 0x206b, 0x0000, 0x007c, 0x77b4, 0x1078, 0x2bb1,
+	0xa7bc, 0x0f00, 0x1078, 0x2cac, 0x6018, 0xa005, 0x0040, 0x250c,
+	0x0d7e, 0x2001, 0x3f90, 0x2068, 0x0d7f, 0x2021, 0x3f80, 0x2009,
+	0x0004, 0x2011, 0x0010, 0x1078, 0x1fc8, 0x0040, 0x250c, 0x157e,
+	0x20a9, 0x0000, 0x2021, 0x3e80, 0x047e, 0x2009, 0x0004, 0x2011,
+	0x0010, 0x1078, 0x1fc8, 0x047f, 0x0040, 0x250b, 0x8420, 0x0070,
+	0x250b, 0x0078, 0x24fc, 0x157f, 0x8738, 0xa784, 0x0007, 0x00c0,
+	0x24e2, 0x0078, 0x1bdb, 0x1078, 0x28eb, 0x1078, 0x28fa, 0x6827,
+	0x0000, 0x789b, 0x000e, 0x6f10, 0x6813, 0x0002, 0x1078, 0x31cd,
+	0xa684, 0x0800, 0x0040, 0x2528, 0x6918, 0xa18d, 0x2000, 0x691a,
+	0x6814, 0xa084, 0x8000, 0x0040, 0x252f, 0x6817, 0x0000, 0x2021,
+	0x3557, 0x6800, 0x2022, 0x6a38, 0x693c, 0x6a2a, 0x692e, 0x1078,
+	0x17e7, 0x0078, 0x1bdb, 0x1078, 0x2428, 0x6827, 0x0000, 0x789b,
+	0x000e, 0x6f10, 0x1078, 0x2d87, 0xa08c, 0x00ff, 0x6912, 0x6814,
+	0xa084, 0x8000, 0x0040, 0x254e, 0x7038, 0x6816, 0xa68c, 0xdf00,
+	0x691a, 0x70a3, 0x0000, 0x0078, 0x1bdb, 0xa006, 0x1078, 0x2eca,
+	0x6813, 0x0000, 0x6817, 0x0001, 0xa68c, 0xdf00, 0x691a, 0x6827,
+	0x0000, 0x7000, 0x0079, 0x2564, 0x256c, 0x256e, 0x256e, 0x2570,
+	0x2570, 0x2570, 0x256c, 0x256c, 0x1078, 0x1b81, 0x1078, 0x28fa,
+	0x6008, 0xa084, 0xffef, 0x600a, 0x0078, 0x28c5, 0x2300, 0x0079,
+	0x2579, 0x257c, 0x257e, 0x25bc, 0x1078, 0x1b81, 0x7000, 0x0079,
+	0x2581, 0x2589, 0x258b, 0x258b, 0x2596, 0x258b, 0x259d, 0x2589,
+	0x2589, 0x1078, 0x1b81, 0xa684, 0x2000, 0x00c0, 0x2596, 0xa6b5,
+	0x2000, 0x7e5a, 0x1078, 0x30f3, 0x0078, 0x2d3b, 0x6814, 0xa084,
+	0x8000, 0x0040, 0x259d, 0x6817, 0x0007, 0x2009, 0x3518, 0x210c,
+	0xa186, 0x0000, 0x0040, 0x25b2, 0xa186, 0x0001, 0x0040, 0x25b6,
+	0x2009, 0x352b, 0x200b, 0x000b, 0x70a3, 0x0001, 0x781b, 0x0046,
+	0x0078, 0x1bd3, 0x781b, 0x00dd, 0x0078, 0x1bd3, 0x2009, 0x352b,
+	0x200b, 0x000a, 0x0078, 0x1bd3, 0x1078, 0x1b81, 0x2300, 0x0079,
+	0x25c1, 0x25c4, 0x25c6, 0x25e9, 0x1078, 0x1b81, 0x7000, 0x0079,
+	0x25c9, 0x25d1, 0x25d3, 0x25d3, 0x25de, 0x25d3, 0x25e5, 0x25d1,
+	0x25d1, 0x1078, 0x1b81, 0xa684, 0x2000, 0x00c0, 0x25de, 0xa6b5,
+	0x2000, 0x7e5a, 0x1078, 0x30f3, 0x0078, 0x2d3b, 0x6814, 0xa084,
+	0x8000, 0x0040, 0x25e5, 0x6817, 0x0007, 0x781b, 0x00e4, 0x0078,
+	0x1bd3, 0x681c, 0xa085, 0x0004, 0x681e, 0xa6b5, 0x0800, 0x1078,
+	0x2b94, 0x781b, 0x0069, 0x0078, 0x1bd3, 0x2300, 0x0079, 0x25f8,
+	0x25fb, 0x25fd, 0x25ff, 0x1078, 0x1b81, 0x1078, 0x1b81, 0xa684,
+	0x0400, 0x00c0, 0x261e, 0x782b, 0x3009, 0x789b, 0x0060, 0x78ab,
+	0x0000, 0xa684, 0xfffb, 0x785a, 0x79e4, 0xa184, 0x0020, 0x0040,
+	0x2616, 0x78ec, 0xa084, 0x0003, 0x00c0, 0x261a, 0x2001, 0x0014,
+	0x0078, 0x22fd, 0xa184, 0x0007, 0x0079, 0x2656, 0x7a90, 0xa294,
+	0x0007, 0x789b, 0x0060, 0x79a8, 0x81ff, 0x0040, 0x2654, 0x789b,
+	0x0010, 0x7ba8, 0xa384, 0x0001, 0x00c0, 0x2645, 0x7ba8, 0x7ba8,
+	0xa386, 0x0001, 0x00c0, 0x2638, 0x2009, 0xfff7, 0x0078, 0x263e,
+	0xa386, 0x0003, 0x00c0, 0x2645, 0x2009, 0xffef, 0x0c7e, 0x7048,
+	0x2060, 0x6004, 0xa104, 0x6006, 0x0c7f, 0x789b, 0x0060, 0x78ab,
+	0x0000, 0xa684, 0xfffb, 0x785a, 0x782b, 0x3009, 0x691c, 0xa18c,
+	0xfdff, 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d3b, 0x2026, 0x202c,
+	0x2660, 0x2668, 0x265e, 0x265e, 0x265e, 0x2d3b, 0x1078, 0x1b81,
+	0x691c, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d43,
+	0x691c, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x691e, 0x0078, 0x2d3b,
+	0x79e4, 0xa184, 0x0030, 0x0040, 0x267a, 0x78ec, 0xa084, 0x0003,
+	0x00c0, 0x2682, 0x6814, 0xa085, 0x8000, 0x6816, 0x2001, 0x0014,
+	0x0078, 0x22fd, 0xa184, 0x0007, 0x0079, 0x2686, 0x2d3b, 0x2d3b,
+	0x268e, 0x2d3b, 0x2d63, 0x2d63, 0x2d3b, 0x2d3b, 0xa684, 0x0400,
+	0x00c0, 0x26bf, 0x681c, 0xa084, 0x0001, 0x0040, 0x2d43, 0xa68c,
+	0x2060, 0xa18c, 0xfffb, 0x795a, 0x69b2, 0x789b, 0x0060, 0x78ab,
+	0x0000, 0x789b, 0x0061, 0x6814, 0xa085, 0x8000, 0x6816, 0x78aa,
+	0x157e, 0x137e, 0x147e, 0x20a1, 0x012c, 0x789b, 0x0000, 0x8000,
+	0x80ac, 0xad80, 0x000a, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f,
+	0x6810, 0x8007, 0x789b, 0x007e, 0x78aa, 0x0078, 0x2d43, 0x6814,
+	0xa084, 0x8000, 0x0040, 0x26c6, 0x6817, 0x0008, 0x781b, 0x00d8,
+	0x0078, 0x1bd3, 0x2300, 0x0079, 0x26cd, 0x26d2, 0x274d, 0x26d0,
+	0x1078, 0x1b81, 0x7000, 0xa084, 0x0007, 0x0079, 0x26d7, 0x26df,
+	0x26e1, 0x26fd, 0x26df, 0x26df, 0x24dd, 0x26df, 0x26df, 0x1078,
+	0x1b81, 0x691c, 0xa18d, 0x0001, 0x691e, 0x6800, 0x6006, 0xa005,
+	0x00c0, 0x26eb, 0x6002, 0x6818, 0xa084, 0x000e, 0x0040, 0x26f7,
+	0x7014, 0x68b6, 0x712c, 0xa188, 0x3e80, 0x0078, 0x26f9, 0x2009,
+	0x3f80, 0x2104, 0x6802, 0x2d0a, 0x7156, 0x6eb2, 0xa684, 0x0060,
+	0x0040, 0x274b, 0xa684, 0x0800, 0x00c0, 0x270f, 0xa684, 0x7fff,
+	0x68b2, 0x6890, 0x6894, 0x1078, 0x2eca, 0x0078, 0x274b, 0xa684,
+	0x0020, 0x0040, 0x2721, 0xa006, 0x1078, 0x3197, 0x78d0, 0x8003,
+	0x00c8, 0x271d, 0x78d4, 0x1078, 0x31fc, 0x79d8, 0x7adc, 0x0078,
+	0x2725, 0x1078, 0x2cb9, 0x1078, 0x3197, 0xa684, 0x8000, 0x0040,
+	0x274b, 0xa684, 0x7fff, 0x68b2, 0x789b, 0x0074, 0x1078, 0x2d87,
+	0x2010, 0x1078, 0x2d87, 0x2008, 0xa684, 0x0020, 0x00c0, 0x2743,
+	0x1078, 0x2d87, 0x801b, 0x00c8, 0x273e, 0x8000, 0xa084, 0x003f,
+	0xa108, 0xa291, 0x0000, 0x6b94, 0x2100, 0xa302, 0x68ae, 0x6b90,
+	0x2200, 0xa303, 0x68aa, 0x0078, 0x1bdb, 0x0078, 0x2b81, 0x7033,
+	0x0000, 0xa282, 0x0005, 0x0050, 0x2757, 0x1078, 0x1b81, 0x2300,
+	0x0079, 0x275a, 0x275d, 0x2767, 0x278a, 0x2200, 0x0079, 0x2760,
+	0x2765, 0x2b81, 0x2765, 0x27b3, 0x2804, 0x1078, 0x1b81, 0x7000,
+	0xa086, 0x0001, 0x00c0, 0x2774, 0x1078, 0x28fa, 0x1078, 0x2eca,
+	0x7034, 0x600a, 0x0078, 0x2779, 0x7000, 0xa086, 0x0003, 0x0040,
+	0x276e, 0x7003, 0x0005, 0x2001, 0x3f90, 0x2068, 0x703e, 0x7032,
+	0x2200, 0x0079, 0x2783, 0x2b81, 0x2788, 0x27b3, 0x2788, 0x2b81,
+	0x1078, 0x1b81, 0x7000, 0xa086, 0x0001, 0x00c0, 0x2797, 0x1078,
+	0x28fa, 0x1078, 0x2eca, 0x7034, 0x600a, 0x0078, 0x279c, 0x7000,
+	0xa086, 0x0003, 0x0040, 0x2791, 0x7003, 0x0005, 0x2001, 0x3f90,
+	0x2068, 0x703e, 0x7032, 0x2200, 0x0079, 0x27a6, 0x27ad, 0x27ab,
+	0x27ad, 0x27ab, 0x27ad, 0x1078, 0x1b81, 0x1078, 0x2ba4, 0x781b,
+	0x0069, 0x0078, 0x1bd3, 0x7000, 0xa086, 0x0001, 0x00c0, 0x27c0,
+	0x1078, 0x28fa, 0x1078, 0x2eca, 0x7034, 0x600a, 0x0078, 0x27c5,
+	0x7000, 0xa086, 0x0003, 0x0040, 0x27ba, 0x7003, 0x0002, 0x7a80,
+	0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x0007, 0xa215,
+	0x2069, 0x3f80, 0x2d04, 0x2d08, 0x7156, 0x2068, 0xa005, 0x0040,
+	0x27e0, 0x6810, 0xa206, 0x0040, 0x27f9, 0x6800, 0x0078, 0x27d3,
+	0x7003, 0x0005, 0x2001, 0x3f90, 0x2068, 0x703e, 0x7032, 0x157e,
+	0x20a9, 0x002f, 0x2003, 0x0000, 0x8000, 0x0070, 0x27f1, 0x0078,
+	0x27ea, 0x157f, 0x6a12, 0x68b3, 0x0700, 0x681f, 0x0800, 0x6823,
+	0x0003, 0x6eb0, 0x7e5a, 0x681c, 0xa084, 0x0c00, 0x0040, 0x285a,
+	0x1078, 0x2b9c, 0x0078, 0x285a, 0x7000, 0xa086, 0x0001, 0x00c0,
+	0x2811, 0x1078, 0x28fa, 0x1078, 0x2eca, 0x7034, 0x600a, 0x0078,
+	0x2816, 0x7000, 0xa086, 0x0003, 0x0040, 0x280b, 0x7003, 0x0002,
+	0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x0007,
+	0xa215, 0x79a8, 0x79a8, 0xa18c, 0x00ff, 0xa1e8, 0x3e80, 0x2d04,
+	0x2d08, 0x7156, 0x2068, 0xa005, 0x0040, 0x2835, 0x6810, 0xa206,
+	0x0040, 0x284e, 0x6800, 0x0078, 0x2828, 0x7003, 0x0005, 0x2001,
+	0x3f90, 0x2068, 0x703e, 0x7032, 0x157e, 0x20a9, 0x002f, 0x2003,
+	0x0000, 0x8000, 0x0070, 0x2846, 0x0078, 0x283f, 0x157f, 0x6a12,
+	0x68b3, 0x0700, 0x681f, 0x0800, 0x6823, 0x0003, 0x6eb0, 0x7e5a,
+	0x681c, 0xa084, 0x0c00, 0x0040, 0x285a, 0x1078, 0x2b98, 0x7e58,
+	0x0078, 0x285a, 0x027e, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003,
+	0x8003, 0xa080, 0x3600, 0x2060, 0x704a, 0x6000, 0x704e, 0x6004,
+	0x7052, 0xa684, 0x0060, 0x0040, 0x2891, 0x6b94, 0x6c90, 0x69a8,
+	0x68ac, 0xa105, 0x00c0, 0x287f, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde,
+	0xa6b4, 0xb7ff, 0x7e5a, 0x1078, 0x30f3, 0x0078, 0x2891, 0x68ac,
+	0xa31a, 0x2100, 0xa423, 0x2400, 0xa305, 0x0040, 0x2891, 0x7bd2,
+	0x7bda, 0x7cd6, 0x7cde, 0x68ac, 0xa6b4, 0xbfff, 0x7e5a, 0x1078,
+	0x3120, 0x077f, 0x1078, 0x2cac, 0x2009, 0x006a, 0xa684, 0x0008,
+	0x0040, 0x289c, 0x2009, 0x0069, 0xa6b5, 0x2000, 0x7e5a, 0x791a,
+	0x2d00, 0x703e, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+	0xa080, 0x3600, 0x2048, 0x0078, 0x1bd3, 0x6020, 0xa005, 0x0040,
+	0x28b9, 0x8001, 0x6022, 0x6008, 0xa085, 0x0008, 0x600a, 0x7010,
+	0x6026, 0x007c, 0xa006, 0x1078, 0x2eca, 0x6813, 0x0000, 0x6817,
+	0x0001, 0x681f, 0x0040, 0x681b, 0x0100, 0x7000, 0xa084, 0x0007,
+	0x0079, 0x28ca, 0x28d2, 0x28d4, 0x28d4, 0x28e0, 0x28dc, 0x28d2,
+	0x28d2, 0x28d2, 0x1078, 0x1b81, 0x1078, 0x28eb, 0x1078, 0x28e4,
+	0x1078, 0x17e7, 0x0078, 0x1bdb, 0x70a3, 0x0000, 0x0078, 0x1bdb,
+	0x6817, 0x0000, 0x0078, 0x2513, 0x6800, 0xa005, 0x00c0, 0x28e9,
+	0x6002, 0x6006, 0x007c, 0x6010, 0xa005, 0x0040, 0x28f4, 0x8001,
+	0x00d0, 0x28f4, 0x1078, 0x1b81, 0x6012, 0x6008, 0xa084, 0xffef,
+	0x600a, 0x007c, 0x6018, 0xa005, 0x0040, 0x2900, 0x8001, 0x601a,
+	0x007c, 0x1078, 0x2d82, 0x6817, 0x0018, 0x0078, 0x2931, 0x1078,
+	0x2d82, 0x6817, 0x0019, 0x0078, 0x2931, 0x1078, 0x2d82, 0x6817,
+	0x001a, 0x0078, 0x2931, 0x77b4, 0x1078, 0x2cac, 0x71b8, 0xa18c,
+	0x00ff, 0xa1e8, 0x3e80, 0x2d04, 0x2d08, 0x2068, 0xa005, 0x00c0,
+	0x2923, 0x0078, 0x1bdb, 0x6810, 0x72b4, 0xa206, 0x0040, 0x292b,
+	0x6800, 0x0078, 0x291c, 0x6800, 0x200a, 0x6817, 0x0005, 0x70bf,
+	0x0000, 0x1078, 0x28eb, 0x681c, 0xa084, 0x0001, 0x00c0, 0x293a,
+	0x1078, 0x28e4, 0x1078, 0x28fa, 0x681b, 0x0000, 0x681f, 0x0020,
+	0x1078, 0x17e7, 0x0078, 0x1bdb, 0xa282, 0x0003, 0x00c0, 0x2b75,
+	0x7da8, 0xa5ac, 0x00ff, 0x7ea8, 0xa6b4, 0x00ff, 0x691c, 0xa18d,
+	0x0080, 0x691e, 0xa184, 0x0100, 0x0040, 0x29a4, 0xa18c, 0xfeff,
+	0x691e, 0xa6b4, 0x00ff, 0x0040, 0x298e, 0xa682, 0x000f, 0x0048,
+	0x2965, 0x0040, 0x2965, 0x2031, 0x000f, 0x852b, 0x852b, 0x1078,
+	0x2c2f, 0x0040, 0x296f, 0x1078, 0x2a3e, 0x0078, 0x2997, 0x1078,
+	0x2bea, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078,
+	0x2a62, 0x0c7f, 0x691c, 0xa18d, 0x0100, 0x691e, 0x7e58, 0xa6b5,
+	0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x298a, 0x781b, 0x0055,
+	0x0078, 0x1bd3, 0x781b, 0x0069, 0x0078, 0x1bd3, 0x0c7e, 0x2960,
+	0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, 0x2a62, 0x0c7f, 0x7e58,
+	0xa684, 0x0400, 0x00c0, 0x29a0, 0x781b, 0x0058, 0x0078, 0x1bd3,
+	0x781b, 0x006a, 0x0078, 0x1bd3, 0x0c7e, 0x7048, 0x2060, 0x6100,
+	0xa18c, 0x1000, 0x0040, 0x29e4, 0x6208, 0x8217, 0xa294, 0x00ff,
+	0xa282, 0x000f, 0x0048, 0x29b8, 0x0040, 0x29b8, 0x2011, 0x000f,
+	0x2600, 0xa202, 0x00c8, 0x29bd, 0x2230, 0x6208, 0xa294, 0x00ff,
+	0x7018, 0xa086, 0x0028, 0x00c0, 0x29cd, 0xa282, 0x0019, 0x00c8,
+	0x29d3, 0x2011, 0x0019, 0x0078, 0x29d3, 0xa282, 0x000c, 0x00c8,
+	0x29d3, 0x2011, 0x000c, 0x2200, 0xa502, 0x00c8, 0x29d8, 0x2228,
+	0x1078, 0x2bee, 0x852b, 0x852b, 0x1078, 0x2c2f, 0x0040, 0x29e4,
+	0x1078, 0x2a3e, 0x0078, 0x29e8, 0x1078, 0x2bea, 0x1078, 0x2a62,
+	0x7858, 0xa085, 0x0004, 0x785a, 0x0c7f, 0x781b, 0x0069, 0x0078,
+	0x1bd3, 0x0c7e, 0x2960, 0x6000, 0xa084, 0x1000, 0x00c0, 0x2a0c,
+	0x6010, 0xa084, 0x000f, 0x00c0, 0x2a06, 0xa18c, 0x0002, 0x00c0,
+	0x2a06, 0xa18c, 0xfff5, 0x6106, 0x0c7f, 0x007c, 0x2011, 0x0032,
+	0x2019, 0x0000, 0x0078, 0x2a2e, 0x6208, 0xa294, 0x00ff, 0x7018,
+	0xa086, 0x0028, 0x00c0, 0x2a1c, 0xa282, 0x0019, 0x00c8, 0x2a22,
+	0x2011, 0x0019, 0x0078, 0x2a22, 0xa282, 0x000c, 0x00c8, 0x2a22,
+	0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff, 0xa382, 0x000f,
+	0x0048, 0x2a2e, 0x0040, 0x2a2e, 0x2019, 0x000f, 0x78ab, 0x0001,
+	0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005,
+	0x681c, 0xa085, 0x0100, 0x681e, 0x0c7f, 0x007c, 0x0c7e, 0x7148,
+	0x2160, 0x2008, 0xa084, 0xfff0, 0xa635, 0x7e86, 0x6018, 0x789a,
+	0x7eae, 0x6612, 0x78a4, 0xa084, 0xfff8, 0xa18c, 0x0007, 0xa105,
+	0x78a6, 0x6016, 0x788a, 0xa6b4, 0x000f, 0x8637, 0x8204, 0x8004,
+	0xa084, 0x00ff, 0xa605, 0x600e, 0x6004, 0xa084, 0xfff5, 0x6006,
+	0x0c7f, 0x007c, 0x0c7e, 0x7048, 0x2060, 0x6018, 0x789a, 0x78a4,
+	0xa084, 0xfff0, 0x78a6, 0x6012, 0x7884, 0xa084, 0xfff0, 0x7886,
+	0x0c7f, 0x007c, 0xa282, 0x0002, 0x00c0, 0x2b75, 0x7aa8, 0x691c,
+	0xa18d, 0x0080, 0x691e, 0xa184, 0x0200, 0x0040, 0x2ab7, 0xa18c,
+	0xfdff, 0x691e, 0xa294, 0x00ff, 0xa282, 0x0002, 0x00c8, 0x2b75,
+	0x1078, 0x2afe, 0x1078, 0x2a62, 0xa980, 0x0001, 0x200c, 0x1078,
+	0x2ca8, 0x1078, 0x29f1, 0x88ff, 0x0040, 0x2aaa, 0x789b, 0x0060,
+	0x2800, 0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400,
+	0x00c0, 0x2aa6, 0x781b, 0x0055, 0x0078, 0x1bd3, 0x781b, 0x0069,
+	0x0078, 0x1bd3, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x2ab3, 0x781b,
+	0x0058, 0x0078, 0x1bd3, 0x781b, 0x006a, 0x0078, 0x1bd3, 0xa282,
+	0x0002, 0x00c8, 0x2abf, 0xa284, 0x0001, 0x0040, 0x2ac9, 0x7148,
+	0xa188, 0x0000, 0x210c, 0xa18c, 0x2000, 0x00c0, 0x2ac9, 0x2011,
+	0x0000, 0x1078, 0x2bdc, 0x1078, 0x2afe, 0x1078, 0x2a62, 0x7858,
+	0xa085, 0x0004, 0x785a, 0x781b, 0x0069, 0x0078, 0x1bd3, 0x0c7e,
+	0x027e, 0x2960, 0x6000, 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0,
+	0x2aee, 0x6014, 0xa084, 0x0040, 0x00c0, 0x2aec, 0xa18c, 0xffef,
+	0x6106, 0xa006, 0x0078, 0x2afb, 0x2011, 0x0000, 0x78ab, 0x0001,
+	0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x681c,
+	0xa085, 0x0200, 0x681e, 0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7048,
+	0x2060, 0x82ff, 0x0040, 0x2b06, 0x2011, 0x0040, 0x6018, 0xa080,
+	0x0002, 0x789a, 0x78a4, 0xa084, 0xffbf, 0xa205, 0x78a6, 0x6016,
+	0x788a, 0x6004, 0xa084, 0xffef, 0x6006, 0x0c7f, 0x007c, 0x007e,
+	0x7000, 0xa086, 0x0003, 0x0040, 0x2b20, 0x007f, 0x0078, 0x2b23,
+	0x007f, 0x0078, 0x2b71, 0xa684, 0x0020, 0x0040, 0x2b71, 0x7888,
+	0xa084, 0x0040, 0x0040, 0x2b71, 0x78a8, 0x8001, 0x0040, 0x2b30,
+	0x7bb8, 0xa384, 0x003f, 0x831b, 0x00c8, 0x2b37, 0x8000, 0xa005,
+	0x0040, 0x2b58, 0x831b, 0x00c8, 0x2b40, 0x8001, 0x0040, 0x2b6d,
+	0xa006, 0x1078, 0x3197, 0x78b4, 0x1078, 0x31fc, 0x0078, 0x2b71,
+	0xa684, 0x4000, 0x0040, 0x2b58, 0x78b8, 0x801b, 0x00c8, 0x2b51,
+	0x8000, 0xa084, 0x003f, 0x00c0, 0x2b6d, 0xa6b4, 0xbfff, 0x7e5a,
+	0x79d8, 0x7adc, 0x2001, 0x0001, 0xa108, 0x00c8, 0x2b61, 0xa291,
+	0x0000, 0x79d2, 0x79da, 0x7ad6, 0x7ade, 0x1078, 0x3197, 0x781b,
+	0x0067, 0x1078, 0x3061, 0x0078, 0x1bd3, 0x781b, 0x0067, 0x0078,
+	0x1bd3, 0x781b, 0x006a, 0x0078, 0x1bd3, 0x1078, 0x2ba8, 0x781b,
+	0x0069, 0x0078, 0x1bd3, 0x1078, 0x2b94, 0x781b, 0x0069, 0x0078,
+	0x1bd3, 0x6823, 0x0002, 0x1078, 0x2b9c, 0x691c, 0xa18d, 0x0020,
+	0x691e, 0x6814, 0xa084, 0x8000, 0x0040, 0x2b90, 0x6817, 0x0005,
+	0x781b, 0x0069, 0x0078, 0x1bd3, 0x2001, 0x0005, 0x0078, 0x2baa,
+	0x2001, 0x000c, 0x0078, 0x2baa, 0x2001, 0x0006, 0x0078, 0x2baa,
+	0x2001, 0x000d, 0x0078, 0x2baa, 0x2001, 0x0009, 0x0078, 0x2baa,
+	0x2001, 0x0007, 0x789b, 0x007f, 0x78aa, 0xa6b5, 0x0008, 0x7e5a,
+	0x007c, 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b, 0x8703,
+	0xa0e0, 0x3600, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184, 0x000f,
+	0x0040, 0x2bca, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004, 0xa085,
+	0x0008, 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184, 0x0040,
+	0x0040, 0x2bda, 0xa184, 0xffbf, 0x78a6, 0x6016, 0x6004, 0xa085,
+	0x0010, 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab, 0x0001,
+	0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060, 0x78ab,
+	0x0004, 0x007c, 0x2031, 0x0000, 0x2029, 0x0032, 0x789b, 0x0010,
+	0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa, 0x7eaa,
+	0x789b, 0x0060, 0x78ab, 0x0005, 0x007c, 0x157e, 0x8007, 0xa084,
+	0x00ff, 0x8003, 0x8003, 0xa080, 0x0020, 0x789a, 0x79a4, 0xa18c,
+	0xfff0, 0x2001, 0x3546, 0x2004, 0xa082, 0x0028, 0x0040, 0x2c18,
+	0x2021, 0x2c8f, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078, 0x2c1e,
+	0x2021, 0x2c9b, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064,
+	0x2404, 0xa084, 0xfff0, 0xa106, 0x0040, 0x2c2d, 0x8420, 0x2300,
+	0xa210, 0x0070, 0x2c2d, 0x0078, 0x2c20, 0x157f, 0x007c, 0x157e,
+	0x2011, 0x3546, 0x2214, 0xa282, 0x0032, 0x0048, 0x2c43, 0x0040,
+	0x2c47, 0x2021, 0x2c81, 0x2019, 0x0011, 0x20a9, 0x000e, 0x2011,
+	0x0032, 0x0078, 0x2c57, 0xa282, 0x0028, 0x0040, 0x2c4f, 0x2021,
+	0x2c8f, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078, 0x2c55, 0x2021,
+	0x2c9b, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011, 0x0064, 0x2200,
+	0xa502, 0x0040, 0x2c67, 0x0048, 0x2c67, 0x8420, 0x2300, 0xa210,
+	0x0070, 0x2c64, 0x0078, 0x2c57, 0x157f, 0xa006, 0x007c, 0x157f,
+	0xa582, 0x0064, 0x00c8, 0x2c70, 0x7808, 0xa085, 0x0070, 0x780a,
+	0x78ec, 0xa084, 0x0300, 0x0040, 0x2c7e, 0x2404, 0xa09e, 0x1201,
+	0x00c0, 0x2c7e, 0x2001, 0x2101, 0x0078, 0x2c7f, 0x2404, 0xa005,
+	0x007c, 0x1201, 0x3002, 0x3202, 0x4203, 0x4403, 0x5404, 0x5604,
+	0x6605, 0x6805, 0x7806, 0x7a06, 0x0a07, 0x0c07, 0x0e07, 0x3202,
+	0x4202, 0x5202, 0x6202, 0x7202, 0x6605, 0x7605, 0x7805, 0x7a05,
+	0x7c05, 0x7e05, 0x7f05, 0x2202, 0x3202, 0x4202, 0x5202, 0x5404,
+	0x6404, 0x7404, 0x7604, 0x7804, 0x7a04, 0x7c04, 0x7e04, 0x7f04,
+	0x789b, 0x0010, 0xa046, 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784,
+	0x0007, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e0, 0x3680,
+	0x007c, 0x79d8, 0x7adc, 0x78d0, 0x801b, 0x00c8, 0x2cc0, 0x8000,
+	0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x007c, 0x0f7e, 0x2079,
+	0x0100, 0x2009, 0x3540, 0x2091, 0x8000, 0x2104, 0x0079, 0x2cd0,
+	0x2d02, 0x2cda, 0x2cda, 0x2cda, 0x2cda, 0x2cda, 0x2cd8, 0x2cd8,
+	0x1078, 0x1b81, 0x784b, 0x0004, 0x7848, 0xa084, 0x0004, 0x00c0,
+	0x2cdc, 0x784b, 0x0008, 0x7848, 0xa084, 0x0008, 0x00c0, 0x2ce3,
+	0x68b0, 0xa085, 0x4000, 0x68b2, 0x7858, 0xa085, 0x4000, 0x785a,
+	0x7830, 0xa084, 0x0080, 0x00c0, 0x2d02, 0x0018, 0x2d02, 0x6818,
+	0xa084, 0x0020, 0x00c0, 0x2d00, 0x781b, 0x00dd, 0x0078, 0x2d02,
+	0x781b, 0x00e4, 0x2091, 0x8001, 0x0f7f, 0x007c, 0x0c7e, 0x6810,
+	0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0, 0x3600,
+	0x6004, 0xa084, 0x000a, 0x00c0, 0x2d39, 0x6108, 0xa194, 0xff00,
+	0x0040, 0x2d39, 0xa18c, 0x00ff, 0x2001, 0x0019, 0xa106, 0x0040,
+	0x2d28, 0x2001, 0x0032, 0xa106, 0x0040, 0x2d2c, 0x0078, 0x2d30,
+	0x2009, 0x0020, 0x0078, 0x2d32, 0x2009, 0x003f, 0x0078, 0x2d32,
+	0x2011, 0x0000, 0x2100, 0xa205, 0x600a, 0x6004, 0xa085, 0x0002,
+	0x6006, 0x0c7f, 0x007c, 0x781b, 0x006a, 0x0078, 0x1bd3, 0x781b,
+	0x0069, 0x0078, 0x1bd3, 0x781b, 0x0058, 0x0078, 0x1bd3, 0x781b,
+	0x0055, 0x0078, 0x1bd3, 0x781b, 0x00dd, 0x0078, 0x1bd3, 0x781b,
+	0x00dc, 0x0078, 0x1bd3, 0x781b, 0x00e4, 0x0078, 0x1bd3, 0x781b,
+	0x00e3, 0x0078, 0x1bd3, 0x781b, 0x009e, 0x0078, 0x1bd3, 0x781b,
+	0x009d, 0x0078, 0x1bd3, 0x70a3, 0x0001, 0x781b, 0x0046, 0x0078,
+	0x1bd3, 0x007e, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2d80, 0x7808,
+	0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec,
+	0xa084, 0x0021, 0x0040, 0x2d80, 0x7808, 0xa085, 0x0002, 0x780a,
+	0x007f, 0x007c, 0x7808, 0xa085, 0x0002, 0x780a, 0x007c, 0x7830,
+	0xa084, 0x0040, 0x00c0, 0x2d87, 0x0098, 0x2d90, 0x78ac, 0x007c,
+	0x7808, 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005,
+	0x78ec, 0xa084, 0x0021, 0x0040, 0x2d9f, 0x0098, 0x2d9d, 0x78ac,
+	0x007e, 0x7808, 0xa085, 0x0002, 0x780a, 0x007f, 0x007c, 0xa784,
+	0x0070, 0x0040, 0x2dab, 0x6817, 0x0003, 0x7858, 0xa084, 0x3f00,
+	0x681a, 0x682f, 0x0000, 0x682b, 0x0000, 0x784b, 0x0008, 0x78e4,
+	0xa005, 0x00d0, 0x2018, 0xa084, 0x0020, 0x0040, 0x2018, 0x78ec,
+	0xa084, 0x0003, 0x0040, 0x2018, 0x0018, 0x2018, 0x0078, 0x2b7b,
+	0x0c7e, 0x6810, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+	0xa080, 0x3600, 0x2060, 0x2048, 0x704a, 0x6000, 0x704e, 0x6004,
+	0x7052, 0x0c7f, 0x007c, 0x0020, 0x0020, 0x0000, 0x0020, 0x0000,
+	0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+	0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+	0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+	0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0062, 0x0009, 0x0014,
+	0x0014, 0x9847, 0x0014, 0x0014, 0x98f5, 0x98e7, 0x0014, 0x0014,
+	0x0080, 0x00bf, 0x0100, 0x0402, 0x2008, 0xf880, 0xa20a, 0x0014,
+	0x300b, 0xa20c, 0x0014, 0xa200, 0x8838, 0x817e, 0x842a, 0x84a0,
+	0x3806, 0x8839, 0x28c2, 0x9cc3, 0xa805, 0x0864, 0xa83b, 0x3008,
+	0x28c1, 0x9cc3, 0xa201, 0x300c, 0x2847, 0x8161, 0x846a, 0x8000,
+	0x84a4, 0x1856, 0x883a, 0xa808, 0x28e2, 0x9ca0, 0xa8f3, 0x0864,
+	0xa829, 0x300c, 0xa801, 0x3008, 0x28e1, 0x9ca0, 0x280d, 0xa204,
+	0x64c0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677,
+	0xa80f, 0x786e, 0x883e, 0xa80c, 0x282b, 0xa205, 0x64a0, 0x67a0,
+	0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677, 0xa801, 0x883e,
+	0x2069, 0x28c1, 0x9cc3, 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8dc,
+	0xa207, 0x0014, 0xa203, 0x8000, 0x84a8, 0x85a4, 0x1872, 0x849a,
+	0x883c, 0x1fe2, 0xf601, 0xa208, 0x856e, 0x866f, 0x0704, 0x3008,
+	0x9ca0, 0x0014, 0xa202, 0x8000, 0x85a4, 0x3009, 0x84a8, 0x19e2,
+	0xf848, 0x8174, 0x86eb, 0x85eb, 0x872e, 0x87a9, 0x883f, 0x08e6,
+	0xa8f1, 0xf861, 0xa8e8, 0xf801, 0x0014, 0xf881, 0x0016, 0x85b2,
+	0x80f0, 0x9532, 0xfaa2, 0x1de2, 0x0014, 0x8532, 0xf221, 0x0014,
+	0x1de2, 0x84a8, 0xd6e0, 0x1fe6, 0x0014, 0xa206, 0x6865, 0x817f,
+	0x842a, 0x1dc1, 0x8823, 0x0016, 0x6042, 0x8008, 0xa8fa, 0x8000,
+	0x84a4, 0x8160, 0x842a, 0xf021, 0x3008, 0x84a8, 0x1dc6, 0x20d7,
+	0x8822, 0x0016, 0x8000, 0x2848, 0x1011, 0xa8fc, 0x3008, 0x8000,
+	0xa000, 0x2802, 0x1011, 0xa8fd, 0xa887, 0x3008, 0x283d, 0x1011,
+	0xa8fd, 0xa209, 0x0017, 0x300c, 0x8000, 0x85a4, 0x1de2, 0xdac1,
+	0x0014, 0x26e0, 0x873a, 0xfaa2, 0x19f2, 0x1fe2, 0x0014, 0xa20b,
+	0x0014, 0xa20d, 0x817e, 0x842a, 0x84a0, 0x3806, 0x0210, 0x9ccd,
+	0x0704, 0x0000, 0x127e, 0x2091, 0x2200, 0x2049, 0x2eca, 0x7000,
+	0x7204, 0xa205, 0x720c, 0xa215, 0x7008, 0xa084, 0xfffd, 0xa205,
+	0x0040, 0x2edc, 0x0078, 0x2ee1, 0x7003, 0x0000, 0x127f, 0x2000,
+	0x007c, 0x7000, 0xa084, 0x0001, 0x00c0, 0x2f0f, 0x7108, 0x8104,
+	0x00c8, 0x2eee, 0x1078, 0x2fab, 0x0078, 0x2ee6, 0x700c, 0xa08c,
+	0x007f, 0x0040, 0x2f0f, 0x7004, 0x8004, 0x00c8, 0x2f06, 0x7014,
+	0xa005, 0x00c0, 0x2f02, 0x7010, 0xa005, 0x0040, 0x2f06, 0xa102,
+	0x00c8, 0x2ee6, 0x7007, 0x0010, 0x0078, 0x2f0f, 0x8aff, 0x0040,
+	0x2f0f, 0x1078, 0x316e, 0x00c0, 0x2f09, 0x0040, 0x2ee6, 0x1078,
+	0x2f59, 0x7003, 0x0000, 0x127f, 0x2000, 0x007c, 0x6424, 0x84ff,
+	0x0040, 0x2f33, 0x2c70, 0x2039, 0x2f38, 0x2704, 0xae68, 0x680c,
+	0xa630, 0x6808, 0xa529, 0x8421, 0x0040, 0x2f33, 0x8738, 0x2704,
+	0xa005, 0x00c0, 0x2f1e, 0x7098, 0xa075, 0x0040, 0x2f33, 0x2039,
+	0x2f35, 0x0078, 0x2f1d, 0x007c, 0x0000, 0x0004, 0x0008, 0x000c,
+	0x0010, 0x0014, 0x0018, 0x001c, 0x0000, 0x127e, 0x2091, 0x2200,
+	0x2079, 0x3500, 0x2071, 0x0010, 0x7007, 0x000a, 0x7007, 0x0002,
+	0x7003, 0x0000, 0x2071, 0x0020, 0x7007, 0x000a, 0x7007, 0x0002,
+	0x7003, 0x0000, 0x2049, 0x0000, 0x78b3, 0x0000, 0x127f, 0x2000,
+	0x007c, 0x2049, 0x2f59, 0x7004, 0x8004, 0x00c8, 0x2f85, 0x7007,
+	0x0012, 0x7108, 0x7008, 0xa106, 0x00c0, 0x2f61, 0xa184, 0x0030,
+	0x0040, 0x2f6e, 0xa086, 0x0030, 0x00c0, 0x2f61, 0x7000, 0xa084,
+	0x0001, 0x00c0, 0x2f85, 0x7008, 0xa084, 0x000c, 0x00c0, 0x2f83,
+	0x710c, 0xa184, 0x0300, 0x00c0, 0x2f83, 0xa184, 0x007f, 0x00c0,
+	0x2f59, 0x0078, 0x2f85, 0x6817, 0x0003, 0x7007, 0x0012, 0x7007,
+	0x0008, 0x7004, 0xa084, 0x0008, 0x00c0, 0x2f89, 0x7007, 0x0012,
+	0x7108, 0x8104, 0x0048, 0x2f8e, 0x78b3, 0x0000, 0x7003, 0x0000,
+	0x2049, 0x0000, 0x007c, 0x107e, 0x007e, 0x127e, 0x157e, 0x2091,
+	0x2200, 0x7108, 0x1078, 0x2fab, 0x157f, 0x127f, 0x2091, 0x8001,
+	0x007f, 0x107f, 0x007c, 0x7204, 0x2118, 0x7108, 0x700c, 0xa084,
+	0x0300, 0x00c0, 0x2fed, 0xa184, 0x000c, 0x00c0, 0x2fed, 0x8213,
+	0x8213, 0x8213, 0x8213, 0xa284, 0x0100, 0xa10d, 0x810b, 0x810b,
+	0x810f, 0xa184, 0x0007, 0x0079, 0x2fc5, 0x2fcf, 0x2fdf, 0x2fed,
+	0x2fdf, 0x3001, 0x3001, 0x2fed, 0x2fff, 0x1078, 0x1b81, 0x7007,
+	0x0002, 0x8aff, 0x00c0, 0x2fd8, 0x2049, 0x0000, 0x0078, 0x2fdc,
+	0x1078, 0x316e, 0x00c0, 0x2fd8, 0x78b3, 0x0000, 0x007c, 0x7007,
+	0x0002, 0x8aff, 0x00c0, 0x2fe6, 0x0078, 0x2fea, 0x1078, 0x316e,
+	0x00c0, 0x2fe6, 0x78b3, 0x0000, 0x007c, 0x7007, 0x0002, 0x1078,
+	0x2f59, 0x1078, 0x2cc6, 0x6814, 0xa084, 0x8000, 0x0040, 0x2ffa,
+	0x6817, 0x0002, 0x007c, 0x1078, 0x1b81, 0x1078, 0x1b81, 0x1078,
+	0x3053, 0x7210, 0x7114, 0x700c, 0xa09c, 0x007f, 0x2800, 0xa300,
+	0xa211, 0xa189, 0x0000, 0x78b0, 0xa005, 0x0040, 0x3013, 0x78b3,
+	0x0000, 0x0078, 0x3036, 0x1078, 0x3053, 0x2704, 0x2c58, 0xac60,
+	0x630c, 0x2200, 0xa322, 0x6308, 0x2100, 0xa31b, 0x2400, 0xa305,
+	0x0040, 0x302c, 0x00c8, 0x302c, 0x8412, 0x8210, 0x830a, 0xa189,
+	0x0000, 0x2b60, 0x0078, 0x3013, 0x2b60, 0x8a07, 0xa7ba, 0x2f35,
+	0xa73d, 0x2c00, 0x6882, 0x6f86, 0x6c8e, 0x6b8a, 0x7007, 0x0012,
+	0x1078, 0x2f59, 0x007c, 0x8738, 0x2704, 0xa005, 0x00c0, 0x3047,
+	0x6098, 0xa005, 0x0040, 0x3050, 0x2060, 0x2039, 0x2f35, 0x8a51,
+	0x0040, 0x304f, 0x7008, 0xa084, 0x00c0, 0xa086, 0x00c0, 0x007c,
+	0x2051, 0x0000, 0x007c, 0x8a50, 0x8739, 0x2704, 0xa004, 0x00c0,
+	0x3060, 0x2039, 0x2f3b, 0x6000, 0xa064, 0x00c0, 0x3060, 0x2d60,
+	0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x6880, 0x2060,
+	0x6884, 0x6b88, 0x6c8c, 0x8057, 0xaad4, 0x00ff, 0xa084, 0x00ff,
+	0xa0b8, 0x2f35, 0x7e08, 0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040,
+	0x0040, 0x307c, 0xa6b5, 0x0001, 0x0f7e, 0x2079, 0x0100, 0x7858,
+	0x0f7f, 0xa084, 0x0040, 0x0040, 0x308b, 0xa684, 0x0001, 0x00c0,
+	0x308b, 0xa6b5, 0x0001, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004,
+	0x00c0, 0x308d, 0x7000, 0xa005, 0x0040, 0x3098, 0x1078, 0x1b81,
+	0x2400, 0xa305, 0x00c0, 0x309e, 0x0078, 0x30db, 0x2c58, 0x2704,
+	0xac60, 0x6004, 0xa400, 0x007e, 0x701a, 0x6000, 0xa301, 0x701e,
+	0x2009, 0x04fd, 0x2104, 0xa086, 0x04fd, 0x007f, 0x00c0, 0x30cb,
+	0xa084, 0x0001, 0x0040, 0x30cb, 0xa684, 0x0001, 0x00c0, 0x30cb,
+	0x7013, 0x0001, 0x7017, 0x0000, 0x7602, 0x7007, 0x0001, 0x78b3,
+	0x0001, 0xa4a0, 0x0001, 0xa399, 0x0000, 0x6004, 0xa400, 0x701a,
+	0x6000, 0xa301, 0x701e, 0x620c, 0x2400, 0xa202, 0x7012, 0x6208,
+	0x2300, 0xa203, 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078,
+	0x303b, 0x0078, 0x30dd, 0x1078, 0x316e, 0x00c0, 0x30db, 0x127f,
+	0x2000, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x7007,
+	0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x30e9, 0x7003, 0x0008,
+	0x127f, 0x2000, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f,
+	0x2049, 0x30f3, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0,
+	0x30fc, 0x7000, 0xa005, 0x0040, 0x3107, 0x1078, 0x1b81, 0x7e08,
+	0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040, 0x0040, 0x3111, 0xa6b5,
+	0x0001, 0x6824, 0xa005, 0x0040, 0x311d, 0x2050, 0x2039, 0x2f38,
+	0x2d60, 0x1078, 0x316e, 0x00c0, 0x3119, 0x127f, 0x2000, 0x007c,
+	0x127e, 0x007e, 0x017e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x037f,
+	0x047f, 0x7e08, 0xa6b5, 0x000c, 0x6818, 0xa084, 0x0040, 0x0040,
+	0x3133, 0xa6b5, 0x0001, 0x2049, 0x3120, 0x6824, 0xa055, 0x0040,
+	0x316b, 0x2d70, 0x2e60, 0x2039, 0x2f38, 0x2704, 0xae68, 0x680c,
+	0xa422, 0x6808, 0xa31b, 0x0048, 0x3158, 0x8a51, 0x00c0, 0x314a,
+	0x1078, 0x1b81, 0x8738, 0x2704, 0xa005, 0x00c0, 0x313e, 0x7098,
+	0xa075, 0x2060, 0x0040, 0x316b, 0x2039, 0x2f35, 0x0078, 0x313d,
+	0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x690c, 0x2400, 0xa122,
+	0x6908, 0x2300, 0xa11b, 0x00c8, 0x3167, 0x1078, 0x1b81, 0x2071,
+	0x0020, 0x0078, 0x308b, 0x127f, 0x2000, 0x007c, 0x7008, 0xa084,
+	0x00c0, 0xa086, 0x00c0, 0x0040, 0x3196, 0x2704, 0xac08, 0x2104,
+	0x701e, 0x8108, 0x2104, 0x701a, 0x8108, 0x2104, 0x7016, 0x8108,
+	0x2104, 0x7012, 0x0f7e, 0x2079, 0x0100, 0x7858, 0x0f7f, 0xa084,
+	0x0040, 0x0040, 0x3191, 0xa684, 0x0001, 0x00c0, 0x3191, 0xa6b5,
+	0x0001, 0x7602, 0x7007, 0x0001, 0x1078, 0x303b, 0x007c, 0x127e,
+	0x007e, 0x0d7e, 0x2091, 0x2200, 0x2049, 0x3197, 0x0d7f, 0x087f,
+	0x7108, 0xa184, 0x00c0, 0x00c0, 0x31ad, 0x6824, 0xa005, 0x0040,
+	0x31bd, 0x0078, 0x2ee1, 0x0078, 0x31bd, 0x7108, 0x8104, 0x00c8,
+	0x31b5, 0x1078, 0x2fab, 0x0078, 0x31a0, 0x7007, 0x0010, 0x7108,
+	0x8104, 0x00c8, 0x31b7, 0x1078, 0x2fab, 0x7008, 0xa086, 0x0002,
+	0x00c0, 0x31a0, 0x7000, 0xa005, 0x00c0, 0x31a0, 0x7003, 0x0000,
+	0x2049, 0x0000, 0x127f, 0x2000, 0x007c, 0x127e, 0x147e, 0x137e,
+	0x157e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049, 0x31cd, 0xad80,
+	0x0010, 0x20a0, 0x2099, 0x0031, 0x700c, 0xa084, 0x007f, 0x6826,
+	0x7007, 0x0008, 0x7007, 0x0002, 0x7003, 0x0001, 0x0040, 0x31eb,
+	0x8000, 0x80ac, 0x53a5, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004,
+	0x00c0, 0x31ed, 0x2049, 0x0000, 0x7003, 0x0000, 0x157f, 0x137f,
+	0x147f, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e, 0x0d7e, 0x2091,
+	0x2200, 0x0d7f, 0x2049, 0x31fc, 0x6880, 0x2060, 0x6884, 0x6b88,
+	0x6c8c, 0x8057, 0xaad4, 0x00ff, 0xa084, 0x00ff, 0xa0b8, 0x2f35,
+	0x7e08, 0xa6b5, 0x0004, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004,
+	0x00c0, 0x3215, 0x2c58, 0x2704, 0xac60, 0x6004, 0xa400, 0x701a,
+	0x6000, 0xa301, 0x701e, 0x7013, 0x0001, 0x7017, 0x0000, 0x7602,
+	0x7007, 0x0001, 0x007f, 0x8007, 0x2009, 0x0031, 0x200a, 0x00a0,
+	0x322f, 0x7108, 0x7007, 0x0002, 0x810c, 0x00c8, 0x322f, 0x810c,
+	0x0048, 0x323c, 0x0078, 0x2fed, 0xa4a0, 0x0001, 0xa399, 0x0000,
+	0x6b8a, 0x6c8e, 0x7007, 0x0004, 0x2049, 0x0000, 0x7003, 0x0000,
+	0x127f, 0x2000, 0x007c, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086,
+	0x818e, 0x00c8, 0x3254, 0xa200, 0x00f0, 0x324f, 0x8086, 0x818e,
+	0x007c, 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x327a, 0xa11a,
+	0x00c8, 0x327a, 0x8213, 0x818d, 0x0048, 0x326d, 0xa11a, 0x00c8,
+	0x326e, 0x00f0, 0x3262, 0x0078, 0x3272, 0xa11a, 0x2308, 0x8210,
+	0x00f0, 0x3262, 0x007e, 0x3200, 0xa084, 0xf7ff, 0x2080, 0x007f,
+	0x157f, 0x007c, 0x007e, 0x3200, 0xa085, 0x0800, 0x0078, 0x3276,
+	0x00e0, 0x32c2, 0x2091, 0x6000, 0x7820, 0x8001, 0x7822, 0x00c0,
+	0x32bc, 0x7824, 0x7822, 0x2091, 0x8000, 0x2069, 0x3540, 0x6800,
+	0xa084, 0x0007, 0x0040, 0x32a4, 0xa086, 0x0002, 0x0040, 0x32a4,
+	0x6830, 0xa00d, 0x0040, 0x32a4, 0x2104, 0xa005, 0x0040, 0x32a4,
+	0x8001, 0x200a, 0x0040, 0x3372, 0x2061, 0x3680, 0x20a9, 0x0080,
+	0x6034, 0xa005, 0x0040, 0x32b6, 0x8001, 0x6036, 0x00c0, 0x32b6,
+	0x6010, 0xa005, 0x0040, 0x32b6, 0x1078, 0x1a23, 0xace0, 0x0010,
+	0x0070, 0x32bc, 0x0078, 0x32a8, 0x1078, 0x32d7, 0x1078, 0x32c5,
+	0x1078, 0x32fc, 0x2091, 0x8001, 0x007c, 0x783c, 0x8001, 0x783e,
+	0x00c0, 0x32d6, 0x7840, 0x783e, 0x7848, 0xa005, 0x0040, 0x32d6,
+	0x8001, 0x784a, 0x00c0, 0x32d6, 0x1078, 0x1a23, 0x007c, 0x7834,
+	0x8001, 0x7836, 0x00c0, 0x32fb, 0x7838, 0x7836, 0x2091, 0x8000,
+	0x7844, 0xa005, 0x00c0, 0x32e6, 0x2001, 0x0101, 0x8001, 0x7846,
+	0xa080, 0x3e80, 0x2040, 0x2004, 0xa065, 0x0040, 0x32fb, 0x6020,
+	0xa005, 0x0040, 0x32f7, 0x8001, 0x6022, 0x0040, 0x332b, 0x6000,
+	0x2c40, 0x0078, 0x32ec, 0x007c, 0x7828, 0x8001, 0x782a, 0x00c0,
+	0x332a, 0x782c, 0x782a, 0x7830, 0xa005, 0x00c0, 0x3309, 0x2001,
+	0x0080, 0x8001, 0x7832, 0x8003, 0x8003, 0x8003, 0x8003, 0xa090,
+	0x3680, 0xa298, 0x0002, 0x2304, 0xa084, 0x0008, 0x0040, 0x332a,
+	0xa290, 0x0009, 0x2204, 0xa005, 0x0040, 0x3322, 0x8001, 0x2012,
+	0x00c0, 0x332a, 0x2304, 0xa084, 0xfff7, 0xa085, 0x0080, 0x201a,
+	0x1078, 0x1a23, 0x007c, 0x2069, 0x3540, 0x6800, 0xa005, 0x0040,
+	0x3335, 0x683c, 0xac06, 0x0040, 0x3372, 0x6017, 0x0006, 0x60b0,
+	0xa084, 0x3f00, 0x601a, 0x601c, 0xa084, 0x00ff, 0xa085, 0x0060,
+	0x601e, 0x6000, 0x2042, 0x6710, 0x6fb6, 0x1078, 0x169c, 0x6818,
+	0xa005, 0x0040, 0x334d, 0x8001, 0x681a, 0x6808, 0xa084, 0xffef,
+	0x680a, 0x6810, 0x8001, 0x00d0, 0x3357, 0x1078, 0x1b81, 0x6812,
+	0x602f, 0x0000, 0x602b, 0x0000, 0x2c68, 0x1078, 0x17e7, 0x2069,
+	0x3540, 0x2001, 0x0006, 0x68a2, 0x7944, 0xa184, 0x0100, 0x00c0,
+	0x336d, 0x69ba, 0x2001, 0x0004, 0x68a2, 0x1078, 0x1a1e, 0x2091,
+	0x8001, 0x007c, 0x2009, 0x354f, 0x2164, 0x2069, 0x0100, 0x6017,
+	0x0006, 0x6858, 0xa084, 0x3f00, 0x601a, 0x601c, 0xa084, 0x00ff,
+	0xa085, 0x0048, 0x601e, 0x602f, 0x0000, 0x602b, 0x0000, 0x6830,
+	0xa084, 0x0040, 0x0040, 0x33ac, 0x684b, 0x0004, 0x20a9, 0x0014,
+	0x6848, 0xa084, 0x0004, 0x0040, 0x3399, 0x0070, 0x3399, 0x0078,
+	0x3390, 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001,
+	0x0040, 0x33a6, 0x0070, 0x33a6, 0x0078, 0x339d, 0x20a9, 0x00fa,
+	0x0070, 0x33ac, 0x0078, 0x33a8, 0x6808, 0xa084, 0xfffd, 0x680a,
+	0x681b, 0x0046, 0x2009, 0x3568, 0x200b, 0x0007, 0x784c, 0x784a,
+	0x2091, 0x8001, 0x007c, 0x2079, 0x3500, 0x1078, 0x3404, 0x1078,
+	0x33cc, 0x1078, 0x33e1, 0x1078, 0x33f6, 0x7833, 0x0000, 0x7847,
+	0x0000, 0x784b, 0x0000, 0x007c, 0x2019, 0x000a, 0x2011, 0x3546,
+	0x2204, 0xa086, 0x0032, 0x0040, 0x33de, 0x2019, 0x000c, 0x2204,
+	0xa086, 0x003c, 0x0040, 0x33de, 0x2019, 0x0008, 0x7b2a, 0x7b2e,
+	0x007c, 0x2019, 0x0030, 0x2011, 0x3546, 0x2204, 0xa086, 0x0032,
+	0x0040, 0x33f3, 0x2019, 0x0039, 0x2204, 0xa086, 0x003c, 0x0040,
+	0x33f3, 0x2019, 0x0027, 0x7b36, 0x7b3a, 0x007c, 0x2019, 0x000d,
+	0x2011, 0x3546, 0x2204, 0xa086, 0x003c, 0x0040, 0x3401, 0x2019,
+	0x000a, 0x7b3e, 0x7b42, 0x007c, 0x2019, 0x2faf, 0x2011, 0x3546,
+	0x2204, 0xa086, 0x0032, 0x0040, 0x3416, 0x2019, 0x3971, 0x2204,
+	0xa086, 0x003c, 0x0040, 0x3416, 0x2019, 0x2626, 0x7b22, 0x7b26,
+	0x007c, 0xda3e
+};
+
+unsigned short sbus_risc_code_length01 = 0x241a;
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.2.3/linux/drivers/scsi/scsi.c	Tue Jan 19 11:32:51 1999
+++ linux/drivers/scsi/scsi.c	Thu Mar 18 15:59:16 1999
@@ -162,7 +162,11 @@
     "Scanner          ",
     "Optical Device   ",
     "Medium Changer   ",
-    "Communications   "
+    "Communications   ",
+    "Unknown          ",
+    "Unknown          ",
+    "Unknown          ",
+    "Enclosure        ",
 };
 
 /* 
@@ -241,6 +245,7 @@
 {"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},
 {"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},
 {"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-8012","*", BLIST_NOLUN},
 {"TANDBERG","TDC 3600","U07", BLIST_NOLUN},     /* Locks up if polled for lun != 0 */
 {"TEAC","CD-R55S","1.0H", BLIST_NOLUN},		/* Locks up if polled for lun != 0 */
 {"TEAC","CD-ROM","1.06", BLIST_NOLUN},          /* causes failed REQUEST SENSE on lun 1
@@ -276,12 +281,11 @@
 {"EMULEX","MD21/S2     ESDI","*", BLIST_SINGLELUN},
 {"CANON","IPUBJD","*", BLIST_SPARSELUN},
 {"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
+{"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"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 */
 {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
-{"IBM","DPES-","*", BLIST_NOTQ | BLIST_NOLUN},
-{"WDIGTL","WDE","*", BLIST_NOTQ | BLIST_NOLUN},
 /*
  * Must be at end of list...
  */
@@ -759,6 +763,7 @@
   case TYPE_PROCESSOR:
   case TYPE_SCANNER:
   case TYPE_MEDIUM_CHANGER:
+  case TYPE_ENCLOSURE:
     SDpnt->writeable = 1;
     break;
   case TYPE_WORM:
@@ -2471,7 +2476,8 @@
             }
             else if (SDpnt->type == TYPE_SCANNER ||
                      SDpnt->type == TYPE_PROCESSOR ||
-                     SDpnt->type == TYPE_MEDIUM_CHANGER) {
+                     SDpnt->type == TYPE_MEDIUM_CHANGER ||
+                     SDpnt->type == TYPE_ENCLOSURE) {
                 new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
             }
             else {
@@ -3293,7 +3299,7 @@
 
     /* One bit per sector to indicate free/busy */
     size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
-    dma_malloc_freelist = (unsigned char *) scsi_init_malloc(size, GFP_ATOMIC);
+    dma_malloc_freelist = (FreeSectorBitmap *) scsi_init_malloc(size, GFP_ATOMIC);
     memset(dma_malloc_freelist, 0, size);
 
     /* One pointer per page for the page list */
diff -u --recursive --new-file v2.2.3/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.2.3/linux/drivers/scsi/scsi.h	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/scsi.h	Sun Mar 21 14:19:03 1999
@@ -39,7 +39,7 @@
 # define FALSE 0
 #endif
 
-#define MAX_SCSI_DEVICE_CODE 10
+#define MAX_SCSI_DEVICE_CODE 14
 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
 
 #ifdef DEBUG
diff -u --recursive --new-file v2.2.3/linux/drivers/sound/awacs_defs.h linux/drivers/sound/awacs_defs.h
--- v2.2.3/linux/drivers/sound/awacs_defs.h	Thu Aug  6 14:06:33 1998
+++ linux/drivers/sound/awacs_defs.h	Wed Mar 10 21:48:46 1999
@@ -157,4 +157,68 @@
 
 #define RATE_LOW	1	/* HIGH = 48kHz, etc;  LOW = 44.1kHz, etc. */
 
+
+/* Burgundy values */
+
+#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
+#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
+
+#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
+
+#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
+#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
+
+#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
+
+#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
+
+#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
+#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
+#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
+#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
+#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
+#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
+#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+
+/* These are all default values for the burgundy */
+#define DEF_BURGUNDY_INPSEL21 (0xAA)
+#define DEF_BURGUNDY_INPSEL3 (0x0A)
+
+#define DEF_BURGUNDY_GAINCD (0x33)
+#define DEF_BURGUNDY_GAINLINE (0x44)
+#define DEF_BURGUNDY_GAINMIC (0x44)
+#define DEF_BURGUNDY_GAINMODEM (0x06)
+
+/* Remember: lowest volume here is 0x9b */
+#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
+#define DEF_BURGUNDY_VOLLINE (0x00000000)
+#define DEF_BURGUNDY_VOLMIC (0x00000000)
+#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
+
+#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
+#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
+
+#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF)
+
+#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
+
+#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
+#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
+#define DEF_BURGUNDY_ATTENHP (0xCC)
+
 #endif /* _AWACS_DEFS_H_ */
diff -u --recursive --new-file v2.2.3/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.2.3/linux/drivers/sound/dmasound.c	Thu Jan  7 15:11:37 1999
+++ linux/drivers/sound/dmasound.c	Wed Mar 10 21:48:46 1999
@@ -173,9 +173,11 @@
 static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma;
 static int awacs_rate_index;
 static int awacs_subframe;
-static int awacs_revision;
 static int awacs_spkr_vol;
 
+static int awacs_revision;
+#define AWACS_BURGUNDY	100		/* fake revision # for burgundy */
+
 /*
  * Space for the DBDMA command blocks.
  */
@@ -184,6 +186,7 @@
 
 /*
  * Cached values of AWACS registers (we can't read them).
+ * Except on the burgundy. XXX
  */
 int awacs_reg[5];
 
@@ -240,6 +243,14 @@
 static volatile struct dbdma_cmd *beep_dbdma_cmd;
 static void (*orig_mksound)(unsigned int, unsigned int);
 
+/* Burgundy functions */
+static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
+static unsigned awacs_burgundy_rcw(unsigned addr);
+static void awacs_burgundy_write_volume(unsigned address, int volume);
+static int awacs_burgundy_read_volume(unsigned address);
+static void awacs_burgundy_write_mvolume(unsigned address, int volume);
+static int awacs_burgundy_read_mvolume(unsigned address);
+
 #ifdef CONFIG_PMAC_PBOOK
 /*
  * Stuff for restoring after a sleep.
@@ -3007,6 +3018,9 @@
 	if (beep_buf)
 		kfree(beep_buf);
 	kd_mksound = orig_mksound;
+#ifdef CONFIG_PMAC_PBOOK
+	notifier_chain_unregister(&sleep_notifier_list, &awacs_sleep_notifier);
+#endif
 }
 #endif /* MODULE */
 
@@ -3040,8 +3054,9 @@
 	 * If we have a sample rate which is within catchRadius percent
 	 * of the requested value, we don't have to expand the samples.
 	 * Otherwise choose the next higher rate.
+	 * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz.
 	 */
-	i = 8;
+	i = (awacs_revision >= AWACS_BURGUNDY)? 1: 8;
 	do {
 		tolerance = catchRadius * awacs_freqs[--i] / 100;
 	} while (sound.soft.speed > awacs_freqs[i] + tolerance && i > 0);
@@ -3053,7 +3068,9 @@
 	awacs_rate_index = i;
 
 	PMacSilence();
-	out_le32(&awacs->control, MASK_IEPC | MASK_IEE | (i << 8) | 0x11);
+	/* XXX disable error interrupt on burgundy for now */
+	out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
+		 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
 	awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
 	awacs_write(awacs_reg[1] | MASK_ADDR1);
 	out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE);
@@ -3213,8 +3230,9 @@
 		/* do something when headphone is plugged/unplugged? */
 	}
 	if (ctrl & MASK_CNTLERR) {
-		printk(KERN_ERR "AWACS: error, status = %x\n",
-		       in_le32(&awacs->codec_stat));
+		int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
+		if (err != 0 && awacs_revision < AWACS_BURGUNDY)
+			printk(KERN_ERR "AWACS: error %x\n", err);
 	}
 	/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
 	out_le32(&awacs->control, ctrl);
@@ -3223,6 +3241,8 @@
 static void
 awacs_write(int val)
 {
+	if (awacs_revision >= AWACS_BURGUNDY)
+		return;
 	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
 		;	/* XXX should have timeout */
 	out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
@@ -3247,7 +3267,8 @@
 static void awacs_mksound(unsigned int hz, unsigned int ticks)
 {
 	unsigned long flags;
-	int srate = awacs_freqs[BEEP_SPEED];
+	int beep_speed = (awacs_revision < AWACS_BURGUNDY)? BEEP_SPEED: 0;
+	int srate = awacs_freqs[beep_speed];
 	int period, ncycles, nsamples;
 	int i, j, f;
 	short *p;
@@ -3308,7 +3329,7 @@
 		out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
-			 | (BEEP_SPEED << 8));
+			 | (beep_speed << 8));
 		out_le32(&awacs->byteswap, 0);
 		out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
 		out_le32(&awacs_txdma->control, RUN | (RUN << 16));
@@ -3330,8 +3351,9 @@
 		PMacSilence();
 		break;
 	case PBOOK_WAKE:
-		out_le32(&awacs->control, MASK_IEPC | MASK_IEE |
-			 (awacs_rate_index << 8) | 0x11);
+		out_le32(&awacs->control, MASK_IEPC
+			 | (awacs_rate_index << 8) | 0x11
+			 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
 		awacs_write(awacs_reg[0] | MASK_ADDR0);
 		awacs_write(awacs_reg[1] | MASK_ADDR1);
 		awacs_write(awacs_reg[2] | MASK_ADDR2);
@@ -3342,6 +3364,220 @@
 }
 #endif /* CONFIG_PMAC_PBOOK */
 
+
+/* All the burgundy functions: */
+
+/* Waits for busy flag to clear */
+inline static void
+awacs_burgundy_busy_wait(void)
+{
+	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
+		;
+}
+
+inline static void
+awacs_burgundy_extend_wait(void)
+{
+	while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND))
+		;
+	while (in_le32(&awacs->codec_stat) & MASK_EXTEND)
+		;
+}
+
+static void
+awacs_burgundy_wcw(unsigned addr, unsigned val)
+{
+	out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
+	awacs_burgundy_busy_wait();
+	out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
+	awacs_burgundy_busy_wait();	     	 
+	out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
+	awacs_burgundy_busy_wait();	     	 
+	out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
+	awacs_burgundy_busy_wait();
+}
+
+static unsigned
+awacs_burgundy_rcw(unsigned addr)
+{
+	unsigned val = 0;
+	int flags;
+
+	/* should have timeouts here */
+	save_flags(flags); cli();
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100000);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100100);
+	awacs_burgundy_busy_wait();	     	 
+	awacs_burgundy_extend_wait();
+	val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8;
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100200);
+	awacs_burgundy_busy_wait();	     	 
+	awacs_burgundy_extend_wait();
+	val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16;
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100300);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24;
+
+	restore_flags(flags);
+
+	return val;
+}
+
+
+static void
+awacs_burgundy_wcb(unsigned addr, unsigned val)
+{
+	out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
+	awacs_burgundy_busy_wait();
+}
+
+static unsigned
+awacs_burgundy_rcb(unsigned addr)
+{
+	unsigned val = 0;
+	int flags;
+
+	/* should have timeouts here */
+	save_flags(flags); cli();
+
+	out_le32(&awacs->codec_ctrl, addr + 0x100000);
+	awacs_burgundy_busy_wait();
+	awacs_burgundy_extend_wait();
+	val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
+
+	restore_flags(flags);
+
+	return val;
+}
+
+static int
+awacs_burgundy_check(void)
+{
+	/* Checks to see the chip is alive and kicking */
+	int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE;
+  
+	return error == 0xf0000;
+}
+
+static int
+awacs_burgundy_init(void)
+{
+	if (awacs_burgundy_check()) {
+		printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n");
+		return 1;
+	}
+
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES,
+			   DEF_BURGUNDY_OUTPUTENABLES);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+			   DEF_BURGUNDY_MORE_OUTPUTENABLES);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
+			   DEF_BURGUNDY_OUTPUTSELECTS);
+	
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21,
+			   DEF_BURGUNDY_INPSEL21);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3,
+			   DEF_BURGUNDY_INPSEL3);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD,
+			   DEF_BURGUNDY_GAINCD);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE,
+			   DEF_BURGUNDY_GAINLINE);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC,
+			   DEF_BURGUNDY_GAINMIC);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM,
+			   DEF_BURGUNDY_GAINMODEM);
+	
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER,
+			   DEF_BURGUNDY_ATTENSPEAKER);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT,
+			   DEF_BURGUNDY_ATTENLINEOUT);
+	awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP,
+			   DEF_BURGUNDY_ATTENHP);
+	
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME,
+			   DEF_BURGUNDY_MASTER_VOLUME);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD,
+			   DEF_BURGUNDY_VOLCD);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE,
+			   DEF_BURGUNDY_VOLLINE);
+	awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC,
+			   DEF_BURGUNDY_VOLMIC);
+	return 0;
+}
+
+static void
+awacs_burgundy_write_volume(unsigned address, int volume)
+{
+	int hardvolume,lvolume,rvolume;
+
+	lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0;
+	rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0;
+
+	hardvolume = lvolume + (rvolume << 16);
+
+	awacs_burgundy_wcw(address, hardvolume);
+}
+
+static int
+awacs_burgundy_read_volume(unsigned address)
+{
+	int softvolume,wvolume;
+
+	wvolume = awacs_burgundy_rcw(address);
+
+	softvolume = (wvolume & 0xff) - 155;
+	softvolume += (((wvolume >> 16) & 0xff) - 155)<<8;
+
+	return softvolume > 0 ? softvolume : 0;
+}
+
+
+
+
+static int
+awacs_burgundy_read_mvolume(unsigned address)
+{
+	int lvolume,rvolume,wvolume;
+
+	wvolume = awacs_burgundy_rcw(address);
+
+	wvolume &= 0xffff;
+	
+	rvolume = (wvolume & 0xff) - 155;
+	lvolume = ((wvolume & 0xff00)>>8) - 155;
+
+	return lvolume + (rvolume << 8);
+}
+
+
+static void
+awacs_burgundy_write_mvolume(unsigned address, int volume)
+{
+	int lvolume,rvolume,hardvolume;
+
+	lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0;
+	rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0;
+
+	hardvolume = lvolume + (rvolume << 8);
+	hardvolume += (hardvolume << 16);
+
+	awacs_burgundy_wcw(address, hardvolume);
+}
+
+/* End burgundy functions */
+
+
+
+
+
 /* Turn on sound output, needed on G3 desktop powermacs */
 static void
 awacs_enable_amp(int spkr_vol)
@@ -3715,117 +3951,244 @@
 
 #ifdef CONFIG_PPC
 	case DMASND_AWACS:
-		switch (cmd) {
-		case SOUND_MIXER_READ_DEVMASK:
-			data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
-				| SOUND_MASK_LINE | SOUND_MASK_MIC
-				| SOUND_MASK_CD | SOUND_MASK_RECLEV
-				| SOUND_MASK_ALTPCM;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_RECMASK:
-			data = SOUND_MASK_LINE | SOUND_MASK_MIC
-				| SOUND_MASK_CD;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_RECSRC:
-			data = 0;
-			if (awacs_reg[0] & MASK_MUX_AUDIN)
-				data |= SOUND_MASK_LINE;
-			if (awacs_reg[0] & MASK_MUX_MIC)
-				data |= SOUND_MASK_MIC;
-			if (awacs_reg[0] & MASK_MUX_CD)
-				data |= SOUND_MASK_CD;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_RECSRC:
-			IOCTL_IN(arg, data);
-			data &= (SOUND_MASK_LINE
-				 | SOUND_MASK_MIC | SOUND_MASK_CD);
-			awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
-					| MASK_MUX_AUDIN);
-			if (data & SOUND_MASK_LINE)
-				awacs_reg[0] |= MASK_MUX_AUDIN;
-			if (data & SOUND_MASK_MIC)
-				awacs_reg[0] |= MASK_MUX_MIC;
-			if (data & SOUND_MASK_CD)
-				awacs_reg[0] |= MASK_MUX_CD;
-			awacs_write(awacs_reg[0] | MASK_ADDR0);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_STEREODEVS:
-			data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
-				| SOUND_MASK_RECLEV;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_CAPS:
-			return IOCTL_OUT(arg, 0);
-		case SOUND_MIXER_READ_VOLUME:
-			data = (awacs_reg[1] & MASK_AMUTE)? 0:
-				awacs_get_volume(awacs_reg[2], 6);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_VOLUME:
-			IOCTL_IN(arg, data);
-			return IOCTL_OUT(arg, sound_set_volume(data));
-		case SOUND_MIXER_READ_SPEAKER:
-			if (awacs_revision >= 3 && adb_hardware == ADB_VIACUDA)
-				data = awacs_spkr_vol;
-			else
-				data = (awacs_reg[1] & MASK_CMUTE)? 0:
-					awacs_get_volume(awacs_reg[4], 6);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_SPEAKER:
-			IOCTL_IN(arg, data);
-			if (awacs_revision >= 3 && adb_hardware == ADB_VIACUDA)
-				awacs_enable_amp(data);
-			else
-				data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
-			IOCTL_IN(arg, data);
-			beep_volume = data & 0xff;
-			/* fall through */
-		case SOUND_MIXER_READ_ALTPCM:
-			return IOCTL_OUT(arg, beep_volume);
-		case SOUND_MIXER_WRITE_LINE:
-			IOCTL_IN(arg, data);
-			awacs_reg[0] &= ~MASK_MUX_AUDIN;
-			if ((data & 0xff) >= 50)
-				awacs_reg[0] |= MASK_MUX_AUDIN;
-			awacs_write(MASK_ADDR0 | awacs_reg[0]);
-			/* fall through */
-		case SOUND_MIXER_READ_LINE:
-			data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_MIC:
-			IOCTL_IN(arg, data);
-			data &= 0xff;
-			awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
-			if (data >= 25) {
-				awacs_reg[0] |= MASK_MUX_MIC;
-				if (data >= 75)
-					awacs_reg[0] |= MASK_GAINLINE;
+		if (awacs_revision<AWACS_BURGUNDY) { /* Different IOCTLS for burgundy*/
+			switch (cmd) {
+			case SOUND_MIXER_READ_DEVMASK:
+				data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+					| SOUND_MASK_LINE | SOUND_MASK_MIC
+					| SOUND_MASK_CD | SOUND_MASK_RECLEV
+					| SOUND_MASK_ALTPCM;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_RECMASK:
+				data = SOUND_MASK_LINE | SOUND_MASK_MIC
+					| SOUND_MASK_CD;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_RECSRC:
+				data = 0;
+				if (awacs_reg[0] & MASK_MUX_AUDIN)
+					data |= SOUND_MASK_LINE;
+				if (awacs_reg[0] & MASK_MUX_MIC)
+					data |= SOUND_MASK_MIC;
+				if (awacs_reg[0] & MASK_MUX_CD)
+					data |= SOUND_MASK_CD;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_RECSRC:
+				IOCTL_IN(arg, data);
+				data &= (SOUND_MASK_LINE
+					 | SOUND_MASK_MIC | SOUND_MASK_CD);
+				awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
+						  | MASK_MUX_AUDIN);
+				if (data & SOUND_MASK_LINE)
+					awacs_reg[0] |= MASK_MUX_AUDIN;
+				if (data & SOUND_MASK_MIC)
+					awacs_reg[0] |= MASK_MUX_MIC;
+				if (data & SOUND_MASK_CD)
+					awacs_reg[0] |= MASK_MUX_CD;
+				awacs_write(awacs_reg[0] | MASK_ADDR0);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_STEREODEVS:
+				data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+					| SOUND_MASK_RECLEV;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_CAPS:
+				return IOCTL_OUT(arg, 0);
+			case SOUND_MIXER_READ_VOLUME:
+				data = (awacs_reg[1] & MASK_AMUTE)? 0:
+					awacs_get_volume(awacs_reg[2], 6);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_VOLUME:
+				IOCTL_IN(arg, data);
+				return IOCTL_OUT(arg, sound_set_volume(data));
+			case SOUND_MIXER_READ_SPEAKER:
+				if (awacs_revision == 3 && adb_hardware == ADB_VIACUDA)
+					data = awacs_spkr_vol;
+				else
+					data = (awacs_reg[1] & MASK_CMUTE)? 0:
+						awacs_get_volume(awacs_reg[4], 6);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_SPEAKER:
+				IOCTL_IN(arg, data);
+				if (awacs_revision == 3 && adb_hardware == ADB_VIACUDA)
+					awacs_enable_amp(data);
+				else
+					data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
+				IOCTL_IN(arg, data);
+				beep_volume = data & 0xff;
+				/* fall through */
+			case SOUND_MIXER_READ_ALTPCM:
+				return IOCTL_OUT(arg, beep_volume);
+			case SOUND_MIXER_WRITE_LINE:
+				IOCTL_IN(arg, data);
+				awacs_reg[0] &= ~MASK_MUX_AUDIN;
+				if ((data & 0xff) >= 50)
+					awacs_reg[0] |= MASK_MUX_AUDIN;
+				awacs_write(MASK_ADDR0 | awacs_reg[0]);
+				/* fall through */
+			case SOUND_MIXER_READ_LINE:
+				data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_MIC:
+				IOCTL_IN(arg, data);
+				data &= 0xff;
+				awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
+				if (data >= 25) {
+					awacs_reg[0] |= MASK_MUX_MIC;
+					if (data >= 75)
+						awacs_reg[0] |= MASK_GAINLINE;
+				}
+				awacs_write(MASK_ADDR0 | awacs_reg[0]);
+				/* fall through */
+			case SOUND_MIXER_READ_MIC:
+				data = (awacs_reg[0] & MASK_MUX_MIC)?
+					(awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_CD:
+				IOCTL_IN(arg, data);
+				awacs_reg[0] &= ~MASK_MUX_CD;
+				if ((data & 0xff) >= 50)
+					awacs_reg[0] |= MASK_MUX_CD;
+				awacs_write(MASK_ADDR0 | awacs_reg[0]);
+				/* fall through */
+			case SOUND_MIXER_READ_CD:
+				data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_RECLEV:
+				IOCTL_IN(arg, data);
+				data = awacs_volume_setter(data, 0, 0, 4);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_RECLEV:
+				data = awacs_get_volume(awacs_reg[0], 4);
+				return IOCTL_OUT(arg, data);
 			}
-			awacs_write(MASK_ADDR0 | awacs_reg[0]);
-			/* fall through */
-		case SOUND_MIXER_READ_MIC:
-			data = (awacs_reg[0] & MASK_MUX_MIC)?
-				(awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_CD:
-			IOCTL_IN(arg, data);
-			awacs_reg[0] &= ~MASK_MUX_CD;
-			if ((data & 0xff) >= 50)
-				awacs_reg[0] |= MASK_MUX_CD;
-			awacs_write(MASK_ADDR0 | awacs_reg[0]);
-			/* fall through */
-		case SOUND_MIXER_READ_CD:
-			data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_WRITE_RECLEV:
-			IOCTL_IN(arg, data);
-			data = awacs_volume_setter(data, 0, 0, 4);
-			return IOCTL_OUT(arg, data);
-		case SOUND_MIXER_READ_RECLEV:
-			data = awacs_get_volume(awacs_reg[0], 4);
-			return IOCTL_OUT(arg, data);
+			break;
+		} else {
+			/* We are, we are, we are... Burgundy or better */
+			switch(cmd) {
+			case SOUND_MIXER_READ_DEVMASK:
+				data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
+					SOUND_MASK_LINE | SOUND_MASK_MIC |
+					SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_RECMASK:
+				data = SOUND_MASK_LINE | SOUND_MASK_MIC
+					| SOUND_MASK_CD;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_RECSRC:
+				data = 0;
+				if (awacs_reg[0] & MASK_MUX_AUDIN)
+					data |= SOUND_MASK_LINE;
+				if (awacs_reg[0] & MASK_MUX_MIC)
+					data |= SOUND_MASK_MIC;
+				if (awacs_reg[0] & MASK_MUX_CD)
+					data |= SOUND_MASK_CD;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_RECSRC:
+				IOCTL_IN(arg, data);
+				data &= (SOUND_MASK_LINE
+					 | SOUND_MASK_MIC | SOUND_MASK_CD);
+				awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
+						  | MASK_MUX_AUDIN);
+				if (data & SOUND_MASK_LINE)
+					awacs_reg[0] |= MASK_MUX_AUDIN;
+				if (data & SOUND_MASK_MIC)
+					awacs_reg[0] |= MASK_MUX_MIC;
+				if (data & SOUND_MASK_CD)
+					awacs_reg[0] |= MASK_MUX_CD;
+				awacs_write(awacs_reg[0] | MASK_ADDR0);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_STEREODEVS:
+				data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+					| SOUND_MASK_RECLEV | SOUND_MASK_CD
+					| SOUND_MASK_LINE;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_CAPS:
+				return IOCTL_OUT(arg, 0);
+			case SOUND_MIXER_WRITE_VOLUME:
+				IOCTL_IN(arg, data);
+				awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data);
+				/* Fall through */
+			case SOUND_MIXER_READ_VOLUME:
+				return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
+			case SOUND_MIXER_WRITE_SPEAKER:
+				IOCTL_IN(arg, data);
+
+				if (!(data & 0xff)) {
+				  /* Mute the left speaker */
+				  awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+						     awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2);
+				} else {
+				  /* Unmute the left speaker */
+				  awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+						     awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2);
+				}
+				if (!(data & 0xff00)) {
+				  /* Mute the right speaker */
+				  awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+						     awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4);
+				} else {
+				  /* Unmute the right speaker */
+				  awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
+						     awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4);
+				}
+
+				data = (((data&0xff)*16)/100 > 0xf ? 0xf :
+					(((data&0xff)*16)/100)) + 
+					 ((((data>>8)*16)/100 > 0xf ? 0xf :
+					((((data>>8)*16)/100)))<<4);
+
+				awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data);
+				/* Fall through */
+			case SOUND_MIXER_READ_SPEAKER:
+				data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
+				data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
+				return IOCTL_OUT(arg, ~data);
+			case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
+				IOCTL_IN(arg, data);
+				beep_volume = data & 0xff;
+				/* fall through */
+			case SOUND_MIXER_READ_ALTPCM:
+				return IOCTL_OUT(arg, beep_volume);
+			case SOUND_MIXER_WRITE_LINE:
+				IOCTL_IN(arg, data);
+				awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);
+
+				/* fall through */
+			case SOUND_MIXER_READ_LINE:
+				data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);				
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_MIC:
+				IOCTL_IN(arg, data);
+				/* Mic is mono device */
+				data = (data << 8) + (data << 24);
+				awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
+				/* fall through */
+			case SOUND_MIXER_READ_MIC:
+				data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);				
+				data <<= 24;
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_CD:
+				IOCTL_IN(arg, data);
+				awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
+				/* fall through */
+			case SOUND_MIXER_READ_CD:
+				data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_WRITE_RECLEV:
+				IOCTL_IN(arg, data);
+				data = awacs_volume_setter(data, 0, 0, 4);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_READ_RECLEV:
+				data = awacs_get_volume(awacs_reg[0], 4);
+				return IOCTL_OUT(arg, data);
+			case SOUND_MIXER_OUTMASK:
+				break;
+			case SOUND_MIXER_OUTSRC:
+				break;
+			}
+			break;
 		}
-		break;
 #endif
 	}
 
@@ -3946,9 +4309,7 @@
 
 	sq.front = sq.count = 0;
 	sq.rear = -1;
-	sq.write_queue = sq.open_queue = sq.sync_queue = 0;
 	sq.syncing = 0;
-
 	sq.playing = 0;
 
 #ifdef CONFIG_ATARI
@@ -4129,21 +4490,21 @@
 static int sq_release(struct inode *inode, struct file *file)
 {
 	int rc = 0;
-	if (sq.busy) {
+
+	if (sq.busy)
 		rc = sq_fsync(file, file->f_dentry);
-		sq.busy = 0;
-		WAKE_UP(sq.open_queue);
-		/* Wake up a process waiting for the queue being released.
-		 * Note: There may be several processes waiting for a call
-		 * to open() returning. */
-	}
 	sound.soft = sound.dsp;
 	sound.hard = sound.dsp;
 	sound_silence();
-	if (rc == 0) {
-		sq_release_buffers();
-		MOD_DEC_USE_COUNT;
-	}
+	sq_release_buffers();
+	MOD_DEC_USE_COUNT;
+
+	sq.busy = 0;
+	WAKE_UP(sq.open_queue);
+	/* Wake up a process waiting for the queue being released.
+	 * Note: There may be several processes waiting for a call
+	 * to open() returning. */
+
 	return rc;
 }
 
@@ -4262,6 +4623,9 @@
 	if (sq_unit < 0)
 		return;
 
+	sq.write_queue = sq.open_queue = sq.sync_queue = 0;
+	sq.busy = 0;
+
 	/* whatever you like as startup mode for /dev/dsp,
 	 * (/dev/audio hasn't got a startup mode). note that
 	 * once changed a new open() will *not* restore these!
@@ -4332,7 +4696,7 @@
 #endif /* CONFIG_AMIGA */
 #ifdef CONFIG_PPC
 	case DMASND_AWACS:
-		mach = "PowerMac ";
+		sprintf(mach, "PowerMac (AWACS rev %d) ", awacs_revision);
 		break;
 #endif /* CONFIG_PPC */
 	}
@@ -4510,6 +4874,7 @@
 
 #ifdef CONFIG_PPC
 	awacs_subframe = 0;
+	awacs_revision = 0;
 	np = find_devices("awacs");
 	if (np == 0) {
 		/*
@@ -4524,6 +4889,8 @@
 			sfprop = (int *) get_property(sound, "sub-frame", 0);
 			if (sfprop != 0 && *sfprop >= 0 && *sfprop < 16)
 				awacs_subframe = *sfprop;
+			if (device_is_compatible(sound, "burgundy"))
+				awacs_revision = AWACS_BURGUNDY;
 		}
 	}
 	if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
@@ -4542,7 +4909,7 @@
 		awacs_tx_cmd_space = kmalloc((numBufs + 4) * sizeof(struct dbdma_cmd),
 					     GFP_KERNEL);
 		if (awacs_tx_cmd_space == NULL) {
-			printk("DMA sound driver: Not enough buffer memory, driver disabled!\n");
+			printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
 			return;
 		}
 		awacs_tx_cmds = (volatile struct dbdma_cmd *)
@@ -4560,17 +4927,22 @@
 		awacs_write(awacs_reg[4] + MASK_ADDR4);
 
 		/* Initialize recent versions of the awacs */
-		awacs_revision = (in_le32(&awacs->codec_stat) >> 12) & 0xf;
-		if (awacs_revision >= 3) {
-			awacs_write(0x6000);
-			awacs_enable_amp(100 * 0x101);
+		if (awacs_revision == 0) {
+			awacs_revision =
+				(in_le32(&awacs->codec_stat) >> 12) & 0xf;
+			if (awacs_revision == 3) {
+				awacs_write(0x6000);
+				awacs_enable_amp(100 * 0x101);
+			}
 		}
+		if (awacs_revision >= AWACS_BURGUNDY)
+			awacs_burgundy_init();
 
 		/* Initialize beep stuff */
 		beep_dbdma_cmd = awacs_tx_cmds + (numBufs + 1);
 		orig_mksound = kd_mksound;
 		kd_mksound = awacs_mksound;
-		beep_buf = (short *) kmalloc(BEEP_BUFLEN * 2, GFP_KERNEL);
+		beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
 		if (beep_buf == NULL)
 			printk(KERN_WARNING "dmasound: no memory for "
 			       "beep buffer\n");
@@ -4596,15 +4968,15 @@
 	mixer_init();
 
 	if (!sound.mach.irqinit()) {
-		printk("DMA sound driver: Interrupt initialization failed\n");
+		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
 		return;
 	}
 #ifdef MODULE
 	irq_installed = 1;
 #endif
 
-	printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs,
-	       bufSize);
+	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n",
+	       numBufs, bufSize);
 
 	return;
 }
diff -u --recursive --new-file v2.2.3/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.2.3/linux/drivers/sound/es1370.c	Wed Mar 10 15:29:47 1999
+++ linux/drivers/sound/es1370.c	Sun Mar 21 07:11:37 1999
@@ -127,6 +127,8 @@
 /* --------------------------------------------------------------------- */
 
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
+#define DBG(x) {}
+/*#define DBG(x) {x}*/
 
 /* --------------------------------------------------------------------- */
 
@@ -1019,7 +1021,7 @@
 		tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
 		if (!schedule_timeout(tmo ? : 1) && tmo)
-			printk(KERN_DEBUG "es1370: dma timed out??\n");
+			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac1.wait, &wait);
         current->state = TASK_RUNNING;
@@ -1054,7 +1056,7 @@
 		tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
 		if (!schedule_timeout(tmo ? : 1) && tmo)
-			printk(KERN_DEBUG "es1370: dma timed out??\n");
+			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac2.wait, &wait);
         current->state = TASK_RUNNING;
@@ -2189,7 +2191,7 @@
 			}
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
-				printk(KERN_DEBUG "es1370: midi timed out??\n");
+				DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");)
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
 		current->state = TASK_RUNNING;
diff -u --recursive --new-file v2.2.3/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v2.2.3/linux/drivers/sound/gus_card.c	Fri Jan  8 22:36:10 1999
+++ linux/drivers/sound/gus_card.c	Wed Mar 10 17:03:52 1999
@@ -44,8 +44,6 @@
 
 void attach_gus_card(struct address_info *hw_config)
 {
-	if(request_irq(hw_config->irq, gusintr, 0,  "Gravis Ultrasound", hw_config)<0)
-		printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq);
 
 	gus_wave_init(hw_config);
 
@@ -60,6 +58,9 @@
 #ifdef CONFIG_MIDI
 	gus_midi_init(hw_config);
 #endif
+	if(request_irq(hw_config->irq, gusintr, 0,  "Gravis Ultrasound", hw_config)<0)
+		printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq);
+
 }
 
 int probe_gus(struct address_info *hw_config)
diff -u --recursive --new-file v2.2.3/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v2.2.3/linux/drivers/sound/midi_synth.c	Tue Feb 23 15:21:34 1999
+++ linux/drivers/sound/midi_synth.c	Sun Mar 21 07:11:37 1999
@@ -84,7 +84,7 @@
 
 	  case 0xE0:
 		  STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
-			      (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7)));
+			      (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7)));
 		  break;
 
 	  default:
diff -u --recursive --new-file v2.2.3/linux/drivers/video/Config.in linux/drivers/video/Config.in
--- v2.2.3/linux/drivers/video/Config.in	Wed Mar 10 15:29:47 1999
+++ linux/drivers/video/Config.in	Wed Mar 17 09:17:22 1999
@@ -73,6 +73,9 @@
     bool 'VESA VGA graphics console' CONFIG_FB_VESA
     define_bool CONFIG_VIDEO_SELECT y
   fi
+  if [ "$CONFIG_VISWS" = "y" ]; then
+    tristate 'SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW
+  fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     if [ "$CONFIG_PCI" != "n" ]; then
       tristate 'Matrox acceleration' CONFIG_FB_MATROX
@@ -174,7 +177,8 @@
 	 "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \
 	 "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \
          "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \
-         "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" ]; then
+         "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \
+         "$CONFIG_FB_SGIVW" = "y" ]; then
       define_bool CONFIG_FBCON_CFB8 y
     else
       if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \
@@ -187,7 +191,8 @@
 	   "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \
 	   "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \
            "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \
-           "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" ]; then
+           "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \
+           "$CONFIG_FB_SGIVW" = "m" ]; then
 	define_bool CONFIG_FBCON_CFB8 m
       fi
     fi
@@ -199,7 +204,7 @@
 	 "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \
 	 "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \
 	 "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \
-	 "$CONFIG_FB_PM2" = "y" ]; then
+	 "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" ]; then
       define_bool CONFIG_FBCON_CFB16 y
     else
       if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
@@ -210,7 +215,7 @@
 	   "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \
  	   "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \
  	   "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \
-	   "$CONFIG_FB_PM2" = "m" ]; then
+	   "$CONFIG_FB_PM2" = "m" -o "$CONFIG_FB_SGIVW" = "m" ]; then
 	define_bool CONFIG_FBCON_CFB16 m
       fi
     fi
@@ -230,14 +235,15 @@
 	 "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \
 	 "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \
 	 "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \
-	 "$CONFIG_FB_FM2" = "y" ]; then
+	 "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" ]; then
       define_bool CONFIG_FBCON_CFB32 y
     else
       if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
 	   "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \
 	   "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \
 	   "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \
-	   "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" ]; then
+	   "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \
+           "$CONFIG_FB_SGIVW" = "m" ]; then
 	define_bool CONFIG_FBCON_CFB32 m
       fi
     fi
diff -u --recursive --new-file v2.2.3/linux/drivers/video/Makefile linux/drivers/video/Makefile
--- v2.2.3/linux/drivers/video/Makefile	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/Makefile	Wed Mar 17 09:17:22 1999
@@ -132,6 +132,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_FB_SGIVW),y)
+L_OBJS += sgivwfb.o
+else
+  ifeq ($(CONFIG_FB_SGIVW),m)
+  M_OBJS += sgivwfb.o
+  endif
+endif
+
 ifeq ($(CONFIG_FB_MAC),y)
 L_OBJS += macfb.o
 endif
diff -u --recursive --new-file v2.2.3/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- v2.2.3/linux/drivers/video/atyfb.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/atyfb.c	Mon Mar 15 16:11:31 1999
@@ -1,4 +1,4 @@
-/*  $Id: atyfb.c,v 1.102 1999/01/21 22:44:42 geert Exp $
+/*  $Id: atyfb.c,v 1.103 1999/03/09 14:01:44 davem Exp $
  *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
  *	Copyright (C) 1997-1998  Geert Uytterhoeven
diff -u --recursive --new-file v2.2.3/linux/drivers/video/cgsixfb.c linux/drivers/video/cgsixfb.c
--- v2.2.3/linux/drivers/video/cgsixfb.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/cgsixfb.c	Mon Mar 15 16:11:31 1999
@@ -1,4 +1,4 @@
-/* $Id: cgsixfb.c,v 1.13 1999/01/28 08:19:18 ecd Exp $
+/* $Id: cgsixfb.c,v 1.16 1999/03/09 14:01:49 davem Exp $
  * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --recursive --new-file v2.2.3/linux/drivers/video/creatorfb.c linux/drivers/video/creatorfb.c
--- v2.2.3/linux/drivers/video/creatorfb.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/creatorfb.c	Mon Mar 15 16:11:31 1999
@@ -1,4 +1,4 @@
-/* $Id: creatorfb.c,v 1.19 1999/02/22 16:20:25 jj Exp $
+/* $Id: creatorfb.c,v 1.26 1999/03/11 00:29:54 davem Exp $
  * creatorfb.c: Creator/Creator3D frame buffer driver
  *
  * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
@@ -132,18 +132,10 @@
 
 struct ffb_fbc {
 	/* Next vertex registers */
-	u32		xxx1[3];
-	volatile u32	alpha;
-	volatile u32	red;
-	volatile u32	green;
-	volatile u32	blue;
-	volatile u32	depth;
-	volatile u32	y;
-	volatile u32	x;
-	u32		xxx2[2];
-	volatile u32	ryf;
-	volatile u32	rxf;
-	u32		xxx3[2];
+	u32		xxx1[3]; volatile u32 alpha; volatile u32 red;
+	volatile u32 green; volatile u32 blue; volatile u32 depth; volatile
+	u32 y; volatile u32 x; u32 xxx2[2]; volatile u32 ryf; volatile u32
+	rxf; u32 xxx3[2];
 	
 	volatile u32	dmyf;
 	volatile u32	dmxf;
@@ -283,8 +275,7 @@
 
 	if (cache - n < 0) {
 		fbc = fb->s.ffb.fbc;
-		do {
-			cache = (fbc->ucsr & FFB_UCSR_FIFO_MASK) - 4;
+		do {	cache = (fbc->ucsr & FFB_UCSR_FIFO_MASK) - 8;
 		} while (cache - n < 0);
 	}
 	fb->s.ffb.fifo_cache = cache - n;
diff -u --recursive --new-file v2.2.3/linux/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- v2.2.3/linux/drivers/video/fbmem.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/fbmem.c	Wed Mar 17 09:17:22 1999
@@ -91,12 +91,17 @@
 extern void fm2fb_init(void);
 extern void fm2fb_setup(char *options, int *ints);
 extern void q40fb_init(void);
+extern void sgivwfb_init(void);
+extern void sgivwfb_setup(char* options, int *ints);
 
 static struct {
 	const char *name;
 	void (*init)(void);
 	void (*setup)(char *options, int *ints);
 } fb_drivers[] __initdata = {
+#ifdef CONFIG_FB_SGIVW
+	{ "sgivw", sgivwfb_init, sgivwfb_setup },
+#endif
 #ifdef CONFIG_FB_RETINAZ3
 	{ "retz3", retz3fb_init, retz3fb_setup },
 #endif
diff -u --recursive --new-file v2.2.3/linux/drivers/video/leofb.c linux/drivers/video/leofb.c
--- v2.2.3/linux/drivers/video/leofb.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/leofb.c	Mon Mar 15 16:11:31 1999
@@ -1,4 +1,4 @@
-/* $Id: leofb.c,v 1.4 1998/09/04 15:43:45 jj Exp $
+/* $Id: leofb.c,v 1.5 1999/03/09 14:02:29 davem Exp $
  * leofb.c: Leo (ZX) 24/8bit frame buffer driver
  *
  * Copyright (C) 1996,1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
diff -u --recursive --new-file v2.2.3/linux/drivers/video/offb.c linux/drivers/video/offb.c
--- v2.2.3/linux/drivers/video/offb.c	Wed Jan 20 23:14:06 1999
+++ linux/drivers/video/offb.c	Fri Mar 19 10:51:42 1999
@@ -476,6 +476,9 @@
 	    printk("no framebuffer address found for %s\n", dp->full_name);
 	    return;
 	}
+	/* needed for the gxt on the f50 -- Cort */
+	if ( dp->addrs[i].address < isa_mem_base )
+		(u_long)dp->addrs[i].address += isa_mem_base;
 	address = (u_long)dp->addrs[i].address;
 
 	/* kludge for valkyrie */
diff -u --recursive --new-file v2.2.3/linux/drivers/video/pm2fb.c linux/drivers/video/pm2fb.c
--- v2.2.3/linux/drivers/video/pm2fb.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/pm2fb.c	Mon Mar 15 16:11:31 1999
@@ -62,7 +62,7 @@
  * The _DEFINITIVE_ memory mapping/unmapping functions.
  * This is due to the fact that they're changing soooo often...
  */
-#define MMAP(a,b)	ioremap((u32 )(a), b)
+#define MMAP(a,b)	ioremap((unsigned long )(a), b)
 #define UNMAP(a,b)	iounmap(a)
 
 /*
diff -u --recursive --new-file v2.2.3/linux/drivers/video/promcon.c linux/drivers/video/promcon.c
--- v2.2.3/linux/drivers/video/promcon.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/promcon.c	Mon Mar 15 16:11:31 1999
@@ -1,4 +1,4 @@
-/* $Id: promcon.c,v 1.13 1999/01/19 09:56:46 jj Exp $
+/* $Id: promcon.c,v 1.14 1999/03/09 14:02:42 davem Exp $
  * Console driver utilizing PROM sun terminal emulation
  *
  * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
diff -u --recursive --new-file v2.2.3/linux/drivers/video/sbusfb.c linux/drivers/video/sbusfb.c
--- v2.2.3/linux/drivers/video/sbusfb.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/sbusfb.c	Mon Mar 15 16:11:31 1999
@@ -229,7 +229,12 @@
 		for (i = 0; fb->mmap_map[i].size; i++)
 			if (fb->mmap_map[i].voff == vma->vm_offset+page) {
 				map_size = sbusfb_mmapsize(fb,fb->mmap_map[i].size);
-				map_offset = (fb->physbase + fb->mmap_map[i].poff) & PAGE_MASK;
+#ifdef __sparc_v9__
+#define POFF_MASK	(PAGE_MASK|0x1UL)
+#else
+#define POFF_MASK	(PAGE_MASK)
+#endif				
+				map_offset = (fb->physbase + fb->mmap_map[i].poff) & POFF_MASK;
 				break;
 			}
 		if (!map_size){
diff -u --recursive --new-file v2.2.3/linux/drivers/video/sgivwfb.c linux/drivers/video/sgivwfb.c
--- v2.2.3/linux/drivers/video/sgivwfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/sgivwfb.c	Wed Mar 17 09:17:22 1999
@@ -0,0 +1,1036 @@
+/*
+ *  linux/drivers/video/sgivwfb.c -- SGI DBE frame buffer device
+ *
+ *	Copyright (C) 1999 Silicon Graphics, Inc.
+ *      Jeffrey Newquist, newquist@engr.sgi.som
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/mtrr.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
+
+#define INCLUDE_TIMING_TABLE_DATA
+#define DBE_REG_BASE regs
+#include "sgivwfb.h"
+
+struct sgivwfb_par {
+  struct fb_var_screeninfo var;
+  u_long timing_num;
+  int valid;
+};
+
+/*
+ *  RAM we reserve for the frame buffer. This defines the maximum screen
+ *  size
+ *
+ *  The default can be overridden if the driver is compiled as a module
+ */
+
+/* set by arch/i386/kernel/setup.c */
+u_long                sgivwfb_mem_phys;
+u_long                sgivwfb_mem_size;
+
+static volatile char  *fbmem;
+static asregs         *regs;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+static char           sgivwfb_name[16] = "SGI Vis WS FB";
+static u32            cmap_fifo;
+static int            ypan       = 0;
+static int            ywrap      = 0;
+
+/* console related variables */
+static int currcon = 0;
+static struct display disp;
+
+static union {
+#ifdef FBCON_HAS_CFB16
+  u16 cfb16[16];
+#endif
+#ifdef FBCON_HAS_CFB32
+  u32 cfb32[16];
+#endif
+} fbcon_cmap;
+
+static struct sgivwfb_par par_current = {
+  {                             /* var (screeninfo) */
+    /* 640x480, 8 bpp */
+    640, 480, 640, 480, 0, 0, 8, 0,
+    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+    0, 0, -1, -1, 0, 20000, 64, 64, 32, 32, 64, 2,
+    0, FB_VMODE_NONINTERLACED
+  },
+  0,                            /* timing_num */
+  0				/* par not activated */
+};
+
+/*
+ *  Interface used by the world
+ */
+void sgivwfb_setup(char *options, int *ints);
+
+static int sgivwfb_open(struct fb_info *info, int user);
+static int sgivwfb_release(struct fb_info *info, int user);
+static int sgivwfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info);
+static int sgivwfb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+static int sgivwfb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+static int sgivwfb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info);
+static int sgivwfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int sgivwfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int sgivwfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			 u_long arg, int con, struct fb_info *info);
+static int sgivwfb_mmap(struct fb_info *info, struct file *file,
+                        struct vm_area_struct *vma);
+
+static struct fb_ops sgivwfb_ops = {
+  sgivwfb_open,
+  sgivwfb_release,
+  sgivwfb_get_fix,
+  sgivwfb_get_var,
+  sgivwfb_set_var,
+  sgivwfb_get_cmap,
+  sgivwfb_set_cmap,
+  sgivwfb_pan_display,
+  sgivwfb_ioctl,
+  sgivwfb_mmap
+};
+
+/*
+ *  Interface to the low level console driver
+ */
+void sgivwfb_init(void);
+static int sgivwfbcon_switch(int con, struct fb_info *info);
+static int sgivwfbcon_updatevar(int con, struct fb_info *info);
+static void sgivwfbcon_blank(int blank, struct fb_info *info);
+
+/*
+ *  Internal routines
+ */
+static u_long get_line_length(int xres_virtual, int bpp);
+static unsigned long bytes_per_pixel(int bpp);
+static void activate_par(struct sgivwfb_par *par);
+static void sgivwfb_encode_fix(struct fb_fix_screeninfo *fix,
+			       struct fb_var_screeninfo *var);
+static int sgivwfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			     u_int *transp, struct fb_info *info);
+static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			     u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+static unsigned long get_line_length(int xres_virtual, int bpp)
+{
+  return(xres_virtual * bytes_per_pixel(bpp));
+}
+
+static unsigned long bytes_per_pixel(int bpp)
+{
+  unsigned long length;
+
+  switch (bpp) {
+  case 8:
+    length = 1;
+    break;
+  case 16:
+    length = 2;
+    break;
+  case 32:
+    length = 4;
+    break;
+  default:
+    printk("sgivwfb: unsupported bpp=%d\n", bpp);
+    length = 0;
+    break;
+  }
+  return(length);
+}
+
+/*
+ * Function:	dbe_TurnOffDma
+ * Parameters:	(None)
+ * Description:	This should turn off the monitor and dbe.  This is used
+ *              when switching between the serial console and the graphics
+ *              console.
+ */
+
+static void dbe_TurnOffDma(void)
+{
+  int i;
+  unsigned int readVal;
+
+  // Check to see if things are already turned off:
+  // 1) Check to see if dbe is not using the internal dotclock.
+  // 2) Check to see if the xy counter in dbe is already off.
+
+  DBE_GETREG(ctrlstat, readVal);
+  if (GET_DBE_FIELD(CTRLSTAT, PCLKSEL, readVal) < 2)
+    return;
+
+  DBE_GETREG(vt_xy, readVal);
+  if (GET_DBE_FIELD(VT_XY, VT_FREEZE, readVal) == 1)
+    return;
+
+  // Otherwise, turn off dbe
+
+  DBE_GETREG(ovr_control, readVal);
+  SET_DBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, readVal, 0);
+  DBE_SETREG(ovr_control, readVal);
+  udelay(1000);
+  DBE_GETREG(frm_control, readVal);
+  SET_DBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, readVal, 0);
+  DBE_SETREG(frm_control, readVal);
+  udelay(1000);
+  DBE_GETREG(did_control, readVal);
+  SET_DBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, readVal, 0);
+  DBE_SETREG(did_control, readVal);
+  udelay(1000);
+
+  // XXX HACK:
+  //
+  //    This was necessary for GBE--we had to wait through two
+  //    vertical retrace periods before the pixel DMA was
+  //    turned off for sure.  I've left this in for now, in
+  //    case dbe needs it.
+
+  for (i = 0; i < 10000; i++)
+    {
+      DBE_GETREG(frm_inhwctrl, readVal);
+      if (GET_DBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, readVal) == 0)
+        udelay(10);
+      else
+        {
+          DBE_GETREG(ovr_inhwctrl, readVal);
+          if (GET_DBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, readVal) == 0)
+            udelay(10);
+          else
+            {
+              DBE_GETREG(did_inhwctrl, readVal);
+              if (GET_DBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, readVal) == 0)
+                udelay(10);
+              else
+                break;
+            }
+        }
+    }
+}
+
+/*
+ *  Set the hardware according to 'par'.
+ */
+static void activate_par(struct sgivwfb_par *par)
+{
+  int i,j, htmp, temp;
+  u32 readVal, outputVal;
+  int wholeTilesX, maxPixelsPerTileX;
+  int frmWrite1, frmWrite2, frmWrite3b;
+  dbe_timing_info_t *currentTiming;    /* Current Video Timing */
+  int xpmax, ypmax;                       // Monitor resolution
+  int bytesPerPixel;                      // Bytes per pixel
+
+  currentTiming = &dbeVTimings[par->timing_num];
+  bytesPerPixel = bytes_per_pixel(par->var.bits_per_pixel);
+  xpmax = currentTiming->width;
+  ypmax = currentTiming->height;
+
+  /* dbe_InitGraphicsBase(); */
+  /* Turn on dotclock PLL */
+  DBE_SETREG(ctrlstat, 0x20000000);
+
+  dbe_TurnOffDma();
+
+  /* dbe_CalculateScreenParams(); */
+  maxPixelsPerTileX = 512/bytesPerPixel;
+  wholeTilesX = xpmax/maxPixelsPerTileX;
+  if (wholeTilesX*maxPixelsPerTileX < xpmax)
+    wholeTilesX++;
+
+  printk("sgivwfb: pixPerTile=%d wholeTilesX=%d\n",
+	 maxPixelsPerTileX, wholeTilesX);
+
+  /* dbe_InitGammaMap(); */
+  udelay(10);
+
+  for (i = 0; i < 256; i++)
+    {
+      DBE_ISETREG(gmap, i, (i << 24) | (i << 16) | (i << 8));
+    }
+
+  /* dbe_TurnOn(); */
+  DBE_GETREG(vt_xy, readVal);
+  if (GET_DBE_FIELD(VT_XY, VT_FREEZE, readVal) == 1)
+    {
+      DBE_SETREG(vt_xy, 0x00000000);
+      udelay(1);
+    }
+  else
+    dbe_TurnOffDma();
+
+  /* dbe_Initdbe(); */
+  for (i = 0; i < 256; i++)
+    {
+      for (j = 0; j < 100; j++)
+        {
+          DBE_GETREG(cm_fifo, readVal);
+          if (readVal != 0x00000000)
+            break;
+          else
+            udelay(10);
+        }
+
+      // DBE_ISETREG(cmap, i, 0x00000000);
+      DBE_ISETREG(cmap, i, (i<<8)|(i<<16)|(i<<24));
+    }
+
+  /* dbe_InitFramebuffer(); */
+  frmWrite1 = 0;
+  SET_DBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, frmWrite1, wholeTilesX);
+  SET_DBE_FIELD(FRM_SIZE_TILE, FRM_RHS, frmWrite1, 0);
+
+  switch(bytesPerPixel)
+    {
+      case 1:
+        SET_DBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, frmWrite1, DBE_FRM_DEPTH_8);
+        break;
+      case 2:
+        SET_DBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, frmWrite1, DBE_FRM_DEPTH_16);
+        break;
+      case 4:
+        SET_DBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, frmWrite1, DBE_FRM_DEPTH_32);
+        break;
+    }
+
+  frmWrite2 = 0;
+  SET_DBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, frmWrite2, ypmax);
+
+  // Tell dbe about the framebuffer location and type
+  // XXX What format is the FRM_TILE_PTR??  64K aligned address?
+  frmWrite3b = 0;
+  SET_DBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, frmWrite3b, sgivwfb_mem_phys>>9);
+  SET_DBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, frmWrite3b, 1);
+  SET_DBE_FIELD(FRM_CONTROL, FRM_LINEAR, frmWrite3b, 1);
+
+  /* Initialize DIDs */
+
+  outputVal = 0;
+  switch(bytesPerPixel)
+    {
+      case 1:
+        SET_DBE_FIELD(WID, TYP, outputVal, DBE_CMODE_I8);
+        break;
+      case 2:
+        SET_DBE_FIELD(WID, TYP, outputVal, DBE_CMODE_RGBA5);
+        break;
+      case 4:
+        SET_DBE_FIELD(WID, TYP, outputVal, DBE_CMODE_RGB8);
+        break;
+    }
+  SET_DBE_FIELD(WID, BUF, outputVal, DBE_BMODE_BOTH);
+
+  for (i = 0; i < 32; i++)
+    {
+      DBE_ISETREG(mode_regs, i, outputVal);
+    }
+
+  /* dbe_InitTiming(); */
+  DBE_SETREG(vt_intr01, 0xffffffff);
+  DBE_SETREG(vt_intr23, 0xffffffff);
+
+  DBE_GETREG(dotclock, readVal);
+  DBE_SETREG(dotclock, readVal & 0xffff);
+
+  DBE_SETREG(vt_xymax, 0x00000000);
+  outputVal = 0;
+  SET_DBE_FIELD(VT_VSYNC, VT_VSYNC_ON, outputVal, currentTiming->vsync_start);
+  SET_DBE_FIELD(VT_VSYNC, VT_VSYNC_OFF, outputVal, currentTiming->vsync_end);
+  DBE_SETREG(vt_vsync, outputVal);
+  outputVal = 0;
+  SET_DBE_FIELD(VT_HSYNC, VT_HSYNC_ON, outputVal, currentTiming->hsync_start);
+  SET_DBE_FIELD(VT_HSYNC, VT_HSYNC_OFF, outputVal, currentTiming->hsync_end);
+  DBE_SETREG(vt_hsync, outputVal);
+  outputVal = 0;
+  SET_DBE_FIELD(VT_VBLANK, VT_VBLANK_ON, outputVal, currentTiming->vblank_start);
+  SET_DBE_FIELD(VT_VBLANK, VT_VBLANK_OFF, outputVal, currentTiming->vblank_end);
+  DBE_SETREG(vt_vblank, outputVal);
+  outputVal = 0;
+  SET_DBE_FIELD(VT_HBLANK, VT_HBLANK_ON, outputVal, currentTiming->hblank_start);
+  SET_DBE_FIELD(VT_HBLANK, VT_HBLANK_OFF, outputVal, currentTiming->hblank_end-3);
+  DBE_SETREG(vt_hblank, outputVal);
+  outputVal = 0;
+  SET_DBE_FIELD(VT_VCMAP, VT_VCMAP_ON, outputVal, currentTiming->vblank_start);
+  SET_DBE_FIELD(VT_VCMAP, VT_VCMAP_OFF, outputVal, currentTiming->vblank_end);
+  DBE_SETREG(vt_vcmap, outputVal);
+  outputVal = 0;
+  SET_DBE_FIELD(VT_HCMAP, VT_HCMAP_ON, outputVal, currentTiming->hblank_start);
+  SET_DBE_FIELD(VT_HCMAP, VT_HCMAP_OFF, outputVal, currentTiming->hblank_end-3);
+  DBE_SETREG(vt_hcmap, outputVal);
+
+  outputVal = 0;
+  temp = currentTiming->vblank_start - currentTiming->vblank_end - 1;
+  if (temp > 0)
+    temp = -temp;
+
+  SET_DBE_FIELD(DID_START_XY, DID_STARTY, outputVal, (u32)temp);
+  if (currentTiming->hblank_end >= 20)
+    SET_DBE_FIELD(DID_START_XY, DID_STARTX, outputVal,
+                      currentTiming->hblank_end - 20);
+  else
+    SET_DBE_FIELD(DID_START_XY, DID_STARTX, outputVal,
+                      currentTiming->htotal - (20 - currentTiming->hblank_end));
+  DBE_SETREG(did_start_xy, outputVal);
+
+  outputVal = 0;
+  SET_DBE_FIELD(CRS_START_XY, CRS_STARTY, outputVal, (u32)(temp+1));
+  if (currentTiming->hblank_end >= DBE_CRS_MAGIC)
+    SET_DBE_FIELD(CRS_START_XY, CRS_STARTX, outputVal,
+                      currentTiming->hblank_end - DBE_CRS_MAGIC);
+  else
+    SET_DBE_FIELD(CRS_START_XY, CRS_STARTX, outputVal,
+                      currentTiming->htotal - (DBE_CRS_MAGIC - currentTiming->hblank_end));
+  DBE_SETREG(crs_start_xy, outputVal);
+
+  outputVal = 0;
+  SET_DBE_FIELD(VC_START_XY, VC_STARTY, outputVal, (u32)temp);
+  SET_DBE_FIELD(VC_START_XY, VC_STARTX, outputVal,
+                    currentTiming->hblank_end - 4);
+  DBE_SETREG(vc_start_xy, outputVal);
+
+  DBE_SETREG(frm_size_tile, frmWrite1);
+  DBE_SETREG(frm_size_pixel, frmWrite2);
+
+  outputVal = 0;
+  SET_DBE_FIELD(DOTCLK, M, outputVal, currentTiming->pll_m-1);
+  SET_DBE_FIELD(DOTCLK, N, outputVal, currentTiming->pll_n-1);
+  SET_DBE_FIELD(DOTCLK, P, outputVal, currentTiming->pll_p);
+  SET_DBE_FIELD(DOTCLK, RUN, outputVal, 1);
+  DBE_SETREG(dotclock, outputVal);
+
+  udelay(11*1000);
+
+  DBE_SETREG(vt_vpixen, 0xffffff);
+  DBE_SETREG(vt_hpixen, 0xffffff);
+
+  outputVal = 0;
+  SET_DBE_FIELD(VT_XYMAX, VT_MAXX, outputVal, currentTiming->htotal);
+  SET_DBE_FIELD(VT_XYMAX, VT_MAXY, outputVal, currentTiming->vtotal);
+  DBE_SETREG(vt_xymax, outputVal);
+
+  outputVal = frmWrite1;
+  SET_DBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, outputVal, 1);
+  DBE_SETREG(frm_size_tile, outputVal);
+  DBE_SETREG(frm_size_tile, frmWrite1);
+
+  outputVal = 0;
+  SET_DBE_FIELD(OVR_WIDTH_TILE, OVR_FIFO_RESET, outputVal, 1);
+  DBE_SETREG(ovr_width_tile, outputVal);
+  DBE_SETREG(ovr_width_tile, 0);
+
+  DBE_SETREG(frm_control, frmWrite3b);
+  DBE_SETREG(did_control, 0);
+
+  // Wait for dbe to take frame settings
+  for (i=0; i<100000; i++)
+    {
+      DBE_GETREG(frm_inhwctrl, readVal);
+      if (GET_DBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, readVal) != 0)
+        break;
+      else
+        udelay(1);
+    }
+
+  if (i==100000)
+    printk("sgivwfb: timeout waiting for frame DMA enable.\n");
+
+  outputVal = 0;
+  htmp = currentTiming->hblank_end - 19;
+  if (htmp < 0)
+    htmp += currentTiming->htotal;    /* allow blank to wrap around */
+  SET_DBE_FIELD(VT_HPIXEN, VT_HPIXEN_ON, outputVal, htmp);
+  SET_DBE_FIELD(VT_HPIXEN, VT_HPIXEN_OFF, outputVal,
+                    ((htmp + currentTiming->width - 2) % currentTiming->htotal));
+  DBE_SETREG(vt_hpixen, outputVal);
+
+  outputVal = 0;
+  SET_DBE_FIELD(VT_VPIXEN, VT_VPIXEN_OFF, outputVal,
+                    currentTiming->vblank_start);
+  SET_DBE_FIELD(VT_VPIXEN, VT_VPIXEN_ON, outputVal,
+                    currentTiming->vblank_end);
+  DBE_SETREG(vt_vpixen, outputVal);
+
+  // Turn off mouse cursor
+  regs->crs_ctl = 0;
+
+  // XXX What's this section for??
+  DBE_GETREG(ctrlstat, readVal);
+  readVal &= 0x02000000;
+
+  if (readVal != 0)
+    {
+      DBE_SETREG(ctrlstat, 0x30000000);
+    }
+}
+
+static void sgivwfb_encode_fix(struct fb_fix_screeninfo *fix,
+			       struct fb_var_screeninfo *var)
+{
+  memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+  strcpy(fix->id, sgivwfb_name);
+  fix->smem_start = (char *) sgivwfb_mem_phys;
+  fix->smem_len = sgivwfb_mem_size;
+  fix->type = FB_TYPE_PACKED_PIXELS;
+  fix->type_aux = 0;
+  switch (var->bits_per_pixel) {
+  case 8:
+    fix->visual = FB_VISUAL_PSEUDOCOLOR;
+    break;
+  default:
+    fix->visual = FB_VISUAL_TRUECOLOR;
+    break;
+  }
+  fix->ywrapstep = ywrap;
+  fix->xpanstep = 0;
+  fix->ypanstep = ypan;
+  fix->line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+  fix->mmio_start = (char *) DBE_REG_PHYS;
+  fix->mmio_len = DBE_REG_SIZE;
+}
+
+/*
+ *  Read a single color register and split it into
+ *  colors/transparent. Return != 0 for invalid regno.
+ */
+static int sgivwfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                             u_int *transp, struct fb_info *info)
+{
+  if (regno > 255)
+    return 1;
+
+  *red =   palette[regno].red << 8;
+  *green = palette[regno].green << 8;
+  *blue =  palette[regno].blue << 8;
+  *transp = 0;
+  return 0;
+}
+
+/*
+ *  Set a single color register. The values supplied are already
+ *  rounded down to the hardware's capabilities (according to the
+ *  entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                             u_int transp, struct fb_info *info)
+{
+  if (regno > 255)
+    return 1;
+  red >>= 8;
+  green >>= 8;
+  blue >>= 8;
+  palette[regno].red = red;
+  palette[regno].green = green;
+  palette[regno].blue = blue;
+
+  /* wait for the color map FIFO to have a free entry */
+  while (cmap_fifo == 0)
+    cmap_fifo = regs->cm_fifo;
+
+  regs->cmap[regno] = (red << 24) | (green << 16) | (blue << 8);
+  cmap_fifo--;			/* assume FIFO is filling up */
+  return 0;
+}
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+    if (con != currcon)
+	return;
+    if (fb_display[con].cmap.len)
+	fb_set_cmap(&fb_display[con].cmap, 1, sgivwfb_setcolreg, info);
+    else
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1,
+		    sgivwfb_setcolreg, info);
+}
+
+/* ---------------------------------------------------- */
+
+/*
+ *  Open/Release the frame buffer device
+ */
+static int sgivwfb_open(struct fb_info *info, int user)
+{
+  /*
+   *  Nothing, only a usage count for the moment
+   */
+  MOD_INC_USE_COUNT;
+  return(0);
+}
+
+static int sgivwfb_release(struct fb_info *info, int user)
+{
+  MOD_DEC_USE_COUNT;
+  return(0);
+}
+
+/*
+ *  Get the Fixed Part of the Display
+ */
+static int sgivwfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info)
+{
+  struct fb_var_screeninfo *var;
+
+  if (con == -1)
+    var = &par_current.var;
+  else
+    var = &fb_display[con].var;
+  sgivwfb_encode_fix(fix, var);
+  return 0;
+}
+
+/*
+ *  Get the User Defined Part of the Display. If a real par get it form there
+ */
+static int sgivwfb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
+{
+  if (con == -1)
+    *var = par_current.var;
+  else
+    *var = fb_display[con].var;
+  return 0;
+}
+
+/*
+ *  Set the User Defined Part of the Display. Again if par use it to get
+ *  real video mode.
+ */
+static int sgivwfb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
+{
+  int err, activate = var->activate;
+  int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+  u_long line_length;
+  u_long min_mode;
+  int req_dot;
+  int test_mode;
+
+  struct dbe_timing_info *timing;
+
+  struct display *display;
+
+  if (con >= 0)
+    display = &fb_display[con];
+  else
+    display = &disp;	/* used during initialization */
+
+  /*
+   *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+   *  as FB_VMODE_SMOOTH_XPAN is only used internally
+   */
+
+  if (var->vmode & FB_VMODE_CONUPDATE) {
+    var->vmode |= FB_VMODE_YWRAP;
+    var->xoffset = display->var.xoffset;
+    var->yoffset = display->var.yoffset;
+  }
+
+  /* XXX FIXME - forcing var's */
+  var->xoffset = 0;
+  var->yoffset = 0;
+
+  /* Limit bpp to 8, 16, and 32 */
+  if (var->bits_per_pixel <= 8)
+    var->bits_per_pixel = 8;
+  else if (var->bits_per_pixel <= 16)
+    var->bits_per_pixel = 16;
+  else if (var->bits_per_pixel <= 32)
+    var->bits_per_pixel = 32;
+  else
+    return -EINVAL;
+
+  var->grayscale = 0;           /* No grayscale for now */
+
+  /* determine valid resolution and timing */
+  for (min_mode=0; min_mode<DBE_VT_SIZE; min_mode++) {
+    if (dbeVTimings[min_mode].width >= var->xres &&
+        dbeVTimings[min_mode].height >= var->yres)
+      break;
+  }
+
+  if (min_mode == DBE_VT_SIZE)
+    return -EINVAL;             /* Resolution to high */
+
+  /* XXX FIXME - should try to pick best refresh rate */
+  /* for now, pick closest dot-clock within 3MHz*/
+  req_dot = (int)((1.0e3/1.0e6) / (1.0e-12 * (float)var->pixclock));
+  printk("sgivwfb: requested pixclock=%d ps (%d KHz)\n", var->pixclock,
+	 req_dot);
+  test_mode=min_mode;
+  while (dbeVTimings[min_mode].width == dbeVTimings[test_mode].width) {
+    if (dbeVTimings[test_mode].cfreq+3000 > req_dot)
+      break;
+    test_mode++;
+  }
+  if (dbeVTimings[min_mode].width != dbeVTimings[test_mode].width)
+    test_mode--;
+  min_mode = test_mode;
+  timing = &dbeVTimings[min_mode];
+  printk("sgivwfb: granted dot-clock=%d KHz\n", timing->cfreq);
+
+  /* Adjust virtual resolution, if necessary */
+  if (var->xres > var->xres_virtual || (!ywrap && !ypan))
+    var->xres_virtual = var->xres;
+  if (var->yres > var->yres_virtual || (!ywrap && !ypan))
+    var->yres_virtual = var->yres;
+
+  /*
+   *  Memory limit
+   */
+  line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+  if (line_length*var->yres_virtual > sgivwfb_mem_size)
+    return -ENOMEM;             /* Virtual resolution to high */
+
+  switch (var->bits_per_pixel)
+    {
+    case 8:
+      var->red.offset = 0;
+      var->red.length = 8;
+      var->green.offset = 0;
+      var->green.length = 8;
+      var->blue.offset = 0;
+      var->blue.length = 8;
+      var->transp.offset = 0;
+      var->transp.length = 0;
+      break;
+    case 16:	/* RGBA 5551 */
+      var->red.offset = 11;
+      var->red.length = 5;
+      var->green.offset = 6;
+      var->green.length = 5;
+      var->blue.offset = 1;
+      var->blue.length = 5;
+      var->transp.offset = 0;
+      var->transp.length = 0;
+      break;
+    case 32:	/* RGB 8888 */
+      var->red.offset = 0;
+      var->red.length = 8;
+      var->green.offset = 8;
+      var->green.length = 8;
+      var->blue.offset = 16;
+      var->blue.length = 8;
+      var->transp.offset = 24;
+      var->transp.length = 8;
+      break;
+    }
+  var->red.msb_right = 0;
+  var->green.msb_right = 0;
+  var->blue.msb_right = 0;
+  var->transp.msb_right = 0;
+
+  /* set video timing information */
+  var->pixclock = (__u32)(1.0e+9/(float)timing->cfreq);
+  var->left_margin = timing->htotal - timing->hsync_end;
+  var->right_margin = timing->hsync_start - timing->width;
+  var->upper_margin = timing->vtotal - timing->vsync_end;
+  var->lower_margin = timing->vsync_start - timing->height;
+  var->hsync_len = timing->hsync_end - timing->hsync_start;
+  var->vsync_len = timing->vsync_end -  timing->vsync_start;
+
+  if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+    oldxres = display->var.xres;
+    oldyres = display->var.yres;
+    oldvxres = display->var.xres_virtual;
+    oldvyres = display->var.yres_virtual;
+    oldbpp = display->var.bits_per_pixel;
+    display->var = *var;
+    par_current.var = *var;
+    par_current.timing_num = min_mode;
+    if (oldxres != var->xres || oldyres != var->yres ||
+	oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+	oldbpp != var->bits_per_pixel || !par_current.valid) {
+      struct fb_fix_screeninfo fix;
+      printk("sgivwfb: new video mode xres=%d yres=%d bpp=%d\n",
+	     var->xres, var->yres, var->bits_per_pixel);
+      printk("         vxres=%d vyres=%d\n",
+	     var->xres_virtual, var->yres_virtual);
+      activate_par(&par_current);
+      sgivwfb_encode_fix(&fix, var);
+      display->screen_base = (char *)fbmem;
+      display->visual = fix.visual;
+      display->type = fix.type;
+      display->type_aux = fix.type_aux;
+      display->ypanstep = fix.ypanstep;
+      display->ywrapstep = fix.ywrapstep;
+      display->line_length = fix.line_length;
+      display->can_soft_blank = 1;
+      display->inverse = 0;
+      if (oldbpp != var->bits_per_pixel || !par_current.valid) {
+	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+	  return err;
+	do_install_cmap(con, info);
+      }
+      switch (var->bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+      case 8:
+	display->dispsw = &fbcon_cfb8;
+	break;
+#endif
+#ifdef FBCON_HAS_CFB16
+      case 16:
+	display->dispsw = &fbcon_cfb16;
+	display->dispsw_data = fbcon_cmap.cfb16;
+	break;
+#endif
+#ifdef FBCON_HAS_CFB32
+      case 32:
+	display->dispsw = &fbcon_cfb32;
+	display->dispsw_data = fbcon_cmap.cfb32;
+	break;
+#endif
+      default:
+	display->dispsw = &fbcon_dummy;
+	break;
+      }
+      par_current.valid = 1;
+      if (fb_info.changevar)
+	(*fb_info.changevar)(con);
+    }
+  }
+  return 0;
+}
+
+/*
+ *  Pan or Wrap the Display
+ *
+ *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int sgivwfb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info)
+{
+#if 0
+  if (var->vmode & FB_VMODE_YWRAP) {
+    if (var->yoffset < 0 ||
+	var->yoffset >= fb_display[con].var.yres_virtual ||
+	var->xoffset)
+      return -EINVAL;
+  } else {
+    if (var->xoffset+fb_display[con].var.xres >
+	fb_display[con].var.xres_virtual ||
+	var->yoffset+fb_display[con].var.yres >
+	fb_display[con].var.yres_virtual)
+      return -EINVAL;
+  }
+  fb_display[con].var.xoffset = var->xoffset;
+  fb_display[con].var.yoffset = var->yoffset;
+  if (var->vmode & FB_VMODE_YWRAP)
+    fb_display[con].var.vmode |= FB_VMODE_YWRAP;
+  else
+    fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+  return 0;
+#endif
+  return -EINVAL;
+}
+
+/*
+ *  Get the Colormap
+ */
+static int sgivwfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
+{
+  if (con == currcon) /* current console? */
+    return fb_get_cmap(cmap, kspc, sgivwfb_getcolreg, info);
+  else if (fb_display[con].cmap.len) /* non default colormap? */
+    fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+  else
+    fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+		 cmap, kspc ? 0 : 2);
+  return 0;
+}
+
+/*
+ *  Set the Colormap
+ */
+static int sgivwfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
+{
+  int err;
+
+  if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+    if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+			     1<<fb_display[con].var.bits_per_pixel, 0)))
+      return err;
+  }
+  if (con == currcon)			/* current console? */
+    return fb_set_cmap(cmap, kspc, sgivwfb_setcolreg, info);
+  else
+    fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+  return 0;
+}
+
+/*
+ *  Virtual Frame Buffer Specific ioctls
+ */
+static int sgivwfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			 u_long arg, int con, struct fb_info *info)
+{
+  return -EINVAL;
+}
+
+static int sgivwfb_mmap(struct fb_info *info, struct file *file,
+                        struct vm_area_struct *vma)
+{
+  unsigned long size = vma->vm_end - vma->vm_start;
+  unsigned long offset = sgivwfb_mem_phys + vma->vm_offset;
+  if (vma->vm_offset+size > sgivwfb_mem_size)
+    return -EINVAL;
+  pgprot_val(vma->vm_page_prot) = pgprot_val(vma->vm_page_prot) | _PAGE_PCD;
+  vma->vm_flags |= VM_IO;
+  if (remap_page_range(vma->vm_start, offset, size, vma->vm_page_prot))
+    return -EAGAIN;
+  vma->vm_file = file;
+  printk("sgivwfb: mmap framebuffer P(%lx)->V(%lx)\n", offset, vma->vm_start);
+  return 0;
+}
+
+__initfunc(void sgivwfb_setup(char *options, int *ints))
+{
+  char *this_opt;
+
+  fb_info.fontname[0] = '\0';
+
+  if (!options || !*options)
+    return;
+
+  for (this_opt = strtok(options, ","); this_opt;
+       this_opt = strtok(NULL, ",")) {
+    if (!strncmp(this_opt, "font:", 5))
+      strcpy(fb_info.fontname, this_opt+5);
+  }
+}
+
+/*
+ *  Initialisation
+ */
+__initfunc(void sgivwfb_init(void))
+{
+  printk("sgivwfb: framebuffer at 0x%lx, size %ldk\n",
+	 sgivwfb_mem_phys, sgivwfb_mem_size/1024);
+
+  regs = (asregs*)ioremap_nocache(DBE_REG_PHYS, DBE_REG_SIZE);
+  if (!regs) {
+    printk("sgivwfb: couldn't ioremap registers\n");
+    goto fail_ioremap_regs;
+  }
+
+#ifdef CONFIG_MTRR
+  mtrr_add((unsigned long)sgivwfb_mem_phys, sgivwfb_mem_size, MTRR_TYPE_WRCOMB, 1);
+#endif
+
+  strcpy(fb_info.modename, sgivwfb_name);
+  fb_info.changevar = NULL;
+  fb_info.node = -1;
+  fb_info.fbops = &sgivwfb_ops;
+  fb_info.disp = &disp;
+  fb_info.switch_con = &sgivwfbcon_switch;
+  fb_info.updatevar = &sgivwfbcon_updatevar;
+  fb_info.blank = &sgivwfbcon_blank;
+  fb_info.flags = FBINFO_FLAG_DEFAULT;
+
+  fbmem = ioremap_nocache((unsigned long)sgivwfb_mem_phys, sgivwfb_mem_size);
+  if (!fbmem) {
+    printk("sgivwfb: couldn't ioremap fbmem\n");
+    goto fail_ioremap_fbmem;
+  }
+
+  /* turn on default video mode */
+  sgivwfb_set_var(&par_current.var, -1, &fb_info);
+
+  if (register_framebuffer(&fb_info) < 0) {
+    printk("sgivwfb: couldn't register framebuffer\n");
+    goto fail_register_framebuffer;
+  }
+
+  printk("fb%d: Virtual frame buffer device, using %ldK of video memory\n",
+	 GET_FB_IDX(fb_info.node), sgivwfb_mem_size>>10);
+
+  return;
+
+ fail_register_framebuffer:
+  iounmap((char*)fbmem);
+ fail_ioremap_fbmem:
+  iounmap(regs);
+ fail_ioremap_regs:
+  return;
+}
+
+static int sgivwfbcon_switch(int con, struct fb_info *info)
+{
+  /* Do we have to save the colormap? */
+  if (fb_display[currcon].cmap.len)
+    fb_get_cmap(&fb_display[currcon].cmap, 1, sgivwfb_getcolreg, info);
+
+  currcon = con;
+  /* Install new colormap */
+  do_install_cmap(con, info);
+  return 0;
+}
+
+/*
+ *  Update the `var' structure (called by fbcon.c)
+ */
+static int sgivwfbcon_updatevar(int con, struct fb_info *info)
+{
+    /* Nothing */
+    return 0;
+}
+
+/*
+ *  Blank the display.
+ */
+static void sgivwfbcon_blank(int blank, struct fb_info *info)
+{
+    /* Nothing */
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+  sgivwfb_init();
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  unregister_framebuffer(&fb_info);
+  dbe_TurnOffDma();
+  iounmap(regs);
+  iounmap(fbmem);
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.2.3/linux/drivers/video/sgivwfb.h linux/drivers/video/sgivwfb.h
--- v2.2.3/linux/drivers/video/sgivwfb.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/sgivwfb.h	Wed Mar 17 09:17:22 1999
@@ -0,0 +1,660 @@
+/*
+ *  linux/drivers/video/sgivwfb.h -- SGI DBE frame buffer device header
+ *
+ *      Copyright (C) 1999 Silicon Graphics, Inc.
+ *      Jeffrey Newquist, newquist@engr.sgi.som
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#ifndef __SGIVWFB_H__
+#define __SGIVWFB_H__
+
+#define DBE_GETREG(reg, dest)       ((dest) = DBE_REG_BASE->##reg)
+#define DBE_SETREG(reg, src)        DBE_REG_BASE->##reg = (src)
+#define DBE_IGETREG(reg, idx, dest) ((dest) = DBE_REG_BASE->##reg##[idx])
+#define DBE_ISETREG(reg, idx, src)  (DBE_REG_BASE->##reg##[idx] = (src))
+
+#define MASK(msb, lsb)          ( (((u32)1<<((msb)-(lsb)+1))-1) << (lsb) )
+#define GET(v, msb, lsb)        ( ((u32)(v) & MASK(msb,lsb)) >> (lsb) )
+#define SET(v, f, msb, lsb)     ( (v) = ((v)&~MASK(msb,lsb)) | (( (u32)(f)<<(lsb) ) & MASK(msb,lsb)) )
+
+#define GET_DBE_FIELD(reg, field, v)        GET((v), DBE_##reg##_##field##_MSB, DBE_##reg##_##field##_LSB)
+#define SET_DBE_FIELD(reg, field, v, f)     SET((v), (f), DBE_##reg##_##field##_MSB, DBE_##reg##_##field##_LSB)
+
+/* NOTE: All loads/stores must be 32 bits and uncached */
+
+#define DBE_REG_PHYS	0xd0000000
+#define DBE_REG_SIZE        0x01000000
+
+typedef struct {
+  volatile u32 ctrlstat;     /* 0x000000 general control */
+  volatile u32 dotclock;     /* 0x000004 dot clock PLL control */
+  volatile u32 i2c;          /* 0x000008 crt I2C control */
+  volatile u32 sysclk;       /* 0x00000c system clock PLL control */
+  volatile u32 i2cfp;        /* 0x000010 flat panel I2C control */
+  volatile u32 id;           /* 0x000014 device id/chip revision */
+  volatile u32 config;       /* 0x000018 power on configuration */
+  volatile u32 bist;         /* 0x00001c internal bist status */
+
+  char _pad0[ 0x010000 - 0x000020 ];
+
+  volatile u32 vt_xy;        /* 0x010000 current dot coords */
+  volatile u32 vt_xymax;     /* 0x010004 maximum dot coords */
+  volatile u32 vt_vsync;     /* 0x010008 vsync on/off */
+  volatile u32 vt_hsync;     /* 0x01000c hsync on/off */
+  volatile u32 vt_vblank;    /* 0x010010 vblank on/off */
+  volatile u32 vt_hblank;    /* 0x010014 hblank on/off */
+  volatile u32 vt_flags;     /* 0x010018 polarity of vt signals */
+  volatile u32 vt_f2rf_lock; /* 0x01001c f2rf & framelck y coord */
+  volatile u32 vt_intr01;    /* 0x010020 intr 0,1 y coords */
+  volatile u32 vt_intr23;    /* 0x010024 intr 2,3 y coords */
+  volatile u32 fp_hdrv;      /* 0x010028 flat panel hdrv on/off */
+  volatile u32 fp_vdrv;      /* 0x01002c flat panel vdrv on/off */
+  volatile u32 fp_de;        /* 0x010030 flat panel de on/off */
+  volatile u32 vt_hpixen;    /* 0x010034 intrnl horiz pixel on/off*/
+  volatile u32 vt_vpixen;    /* 0x010038 intrnl vert pixel on/off */
+  volatile u32 vt_hcmap;     /* 0x01003c cmap write (horiz) */
+  volatile u32 vt_vcmap;     /* 0x010040 cmap write (vert) */
+  volatile u32 did_start_xy; /* 0x010044 eol/f did/xy reset val */
+  volatile u32 crs_start_xy; /* 0x010048 eol/f crs/xy reset val */
+  volatile u32 vc_start_xy;  /* 0x01004c eol/f vc/xy reset val */
+
+  char _pad1[ 0x020000 - 0x010050 ];
+
+  volatile u32 ovr_width_tile; /* 0x020000 overlay plane ctrl 0 */
+  volatile u32 ovr_inhwctrl;   /* 0x020004 overlay plane ctrl 1 */
+  volatile u32 ovr_control;    /* 0x020008 overlay plane ctrl 1 */
+
+  char _pad2[ 0x030000 - 0x02000C ];
+
+  volatile u32 frm_size_tile;  /* 0x030000 normal plane ctrl 0 */
+  volatile u32 frm_size_pixel; /* 0x030004 normal plane ctrl 1 */
+  volatile u32 frm_inhwctrl;   /* 0x030008 normal plane ctrl 2 */
+  volatile u32 frm_control;	   /* 0x03000C normal plane ctrl 3 */
+
+  char _pad3[ 0x040000 - 0x030010 ];
+
+  volatile u32 did_inhwctrl;   /* 0x040000 DID control */
+  volatile u32 did_control;    /* 0x040004 DID shadow */
+
+  char _pad4[ 0x048000 - 0x040008 ];
+
+  volatile u32 mode_regs[32];  /* 0x048000 - 0x04807c WID table */
+
+  char _pad5[ 0x050000 - 0x048080 ];
+
+  volatile u32 cmap[6144];     /* 0x050000 - 0x055ffc color map */
+
+  char _pad6[ 0x058000 - 0x056000 ];
+
+  volatile u32 cm_fifo;        /* 0x058000 color map fifo status */
+
+  char _pad7[ 0x060000 - 0x058004 ];
+
+  volatile u32 gmap[256];      /* 0x060000 - 0x0603fc gamma map */
+
+  char _pad8[ 0x068000 - 0x060400 ];
+
+  volatile u32 gmap10[1024];   /* 0x068000 - 0x068ffc gamma map */
+
+  char _pad9[ 0x070000 - 0x069000 ];
+
+  volatile u32 crs_pos;        /* 0x070000 cusror control 0 */
+  volatile u32 crs_ctl;        /* 0x070004 cusror control 1 */
+  volatile u32 crs_cmap[3];    /* 0x070008 - 0x070010 crs cmap */
+
+  char _pad10[ 0x078000 - 0x070014 ];
+
+  volatile u32 crs_glyph[64];  /* 0x078000 - 0x0780fc crs glyph */
+
+  char _pad11[ 0x080000 - 0x078100 ];
+
+  volatile u32 vc_0;           /* 0x080000 video capture crtl 0 */
+  volatile u32 vc_1;           /* 0x080004 video capture crtl 1 */
+  volatile u32 vc_2;           /* 0x080008 video capture crtl 2 */
+  volatile u32 vc_3;           /* 0x08000c video capture crtl 3 */
+  volatile u32 vc_4;           /* 0x080010 video capture crtl 3 */
+  volatile u32 vc_5;           /* 0x080014 video capture crtl 3 */
+  volatile u32 vc_6;           /* 0x080018 video capture crtl 3 */
+  volatile u32 vc_7;           /* 0x08001c video capture crtl 3 */
+  volatile u32 vc_8;           /* 0x08000c video capture crtl 3 */
+} asregs;
+
+/* Bit mask information */
+
+#define DBE_CTRLSTAT_CHIPID_MSB     3
+#define DBE_CTRLSTAT_CHIPID_LSB     0
+#define DBE_CTRLSTAT_SENSE_N_MSB    4
+#define DBE_CTRLSTAT_SENSE_N_LSB    4
+#define DBE_CTRLSTAT_PCLKSEL_MSB    29
+#define DBE_CTRLSTAT_PCLKSEL_LSB    28
+
+#define DBE_DOTCLK_M_MSB            7
+#define DBE_DOTCLK_M_LSB            0
+#define DBE_DOTCLK_N_MSB            13
+#define DBE_DOTCLK_N_LSB            8
+#define DBE_DOTCLK_P_MSB            15
+#define DBE_DOTCLK_P_LSB            14
+#define DBE_DOTCLK_RUN_MSB          20
+#define DBE_DOTCLK_RUN_LSB          20
+
+#define DBE_VT_XY_VT_FREEZE_MSB     31
+#define DBE_VT_XY_VT_FREEZE_LSB     31
+
+#define DBE_VT_VSYNC_VT_VSYNC_ON_MSB        23
+#define DBE_VT_VSYNC_VT_VSYNC_ON_LSB        12
+#define DBE_VT_VSYNC_VT_VSYNC_OFF_MSB       11
+#define DBE_VT_VSYNC_VT_VSYNC_OFF_LSB       0
+
+#define DBE_VT_HSYNC_VT_HSYNC_ON_MSB        23
+#define DBE_VT_HSYNC_VT_HSYNC_ON_LSB        12
+#define DBE_VT_HSYNC_VT_HSYNC_OFF_MSB       11
+#define DBE_VT_HSYNC_VT_HSYNC_OFF_LSB       0
+
+#define DBE_VT_VBLANK_VT_VBLANK_ON_MSB        23
+#define DBE_VT_VBLANK_VT_VBLANK_ON_LSB        12
+#define DBE_VT_VBLANK_VT_VBLANK_OFF_MSB       11
+#define DBE_VT_VBLANK_VT_VBLANK_OFF_LSB       0
+
+#define DBE_VT_HBLANK_VT_HBLANK_ON_MSB        23
+#define DBE_VT_HBLANK_VT_HBLANK_ON_LSB        12
+#define DBE_VT_HBLANK_VT_HBLANK_OFF_MSB       11
+#define DBE_VT_HBLANK_VT_HBLANK_OFF_LSB       0
+
+#define DBE_VT_VCMAP_VT_VCMAP_ON_MSB        23
+#define DBE_VT_VCMAP_VT_VCMAP_ON_LSB        12
+#define DBE_VT_VCMAP_VT_VCMAP_OFF_MSB       11
+#define DBE_VT_VCMAP_VT_VCMAP_OFF_LSB       0
+
+#define DBE_VT_HCMAP_VT_HCMAP_ON_MSB        23
+#define DBE_VT_HCMAP_VT_HCMAP_ON_LSB        12
+#define DBE_VT_HCMAP_VT_HCMAP_OFF_MSB       11
+#define DBE_VT_HCMAP_VT_HCMAP_OFF_LSB       0
+
+#define DBE_VT_XYMAX_VT_MAXX_MSB    11
+#define DBE_VT_XYMAX_VT_MAXX_LSB    0
+#define DBE_VT_XYMAX_VT_MAXY_MSB    23
+#define DBE_VT_XYMAX_VT_MAXY_LSB    12
+
+#define DBE_VT_HPIXEN_VT_HPIXEN_ON_MSB      23
+#define DBE_VT_HPIXEN_VT_HPIXEN_ON_LSB      12
+#define DBE_VT_HPIXEN_VT_HPIXEN_OFF_MSB     11
+#define DBE_VT_HPIXEN_VT_HPIXEN_OFF_LSB     0
+
+#define DBE_VT_VPIXEN_VT_VPIXEN_ON_MSB      23
+#define DBE_VT_VPIXEN_VT_VPIXEN_ON_LSB      12
+#define DBE_VT_VPIXEN_VT_VPIXEN_OFF_MSB     11
+#define DBE_VT_VPIXEN_VT_VPIXEN_OFF_LSB     0
+
+#define DBE_OVR_CONTROL_OVR_DMA_ENABLE_MSB  0
+#define DBE_OVR_CONTROL_OVR_DMA_ENABLE_LSB  0
+
+#define DBE_OVR_INHWCTRL_OVR_DMA_ENABLE_MSB 0
+#define DBE_OVR_INHWCTRL_OVR_DMA_ENABLE_LSB 0
+
+#define DBE_OVR_WIDTH_TILE_OVR_FIFO_RESET_MSB       13
+#define DBE_OVR_WIDTH_TILE_OVR_FIFO_RESET_LSB       13
+
+#define DBE_FRM_CONTROL_FRM_DMA_ENABLE_MSB  0
+#define DBE_FRM_CONTROL_FRM_DMA_ENABLE_LSB  0
+#define DBE_FRM_CONTROL_FRM_TILE_PTR_MSB    31
+#define DBE_FRM_CONTROL_FRM_TILE_PTR_LSB    9
+#define DBE_FRM_CONTROL_FRM_LINEAR_MSB      1
+#define DBE_FRM_CONTROL_FRM_LINEAR_LSB      1
+
+#define DBE_FRM_INHWCTRL_FRM_DMA_ENABLE_MSB 0
+#define DBE_FRM_INHWCTRL_FRM_DMA_ENABLE_LSB 0
+
+#define DBE_FRM_SIZE_TILE_FRM_WIDTH_TILE_MSB        12
+#define DBE_FRM_SIZE_TILE_FRM_WIDTH_TILE_LSB        5
+#define DBE_FRM_SIZE_TILE_FRM_RHS_MSB       4
+#define DBE_FRM_SIZE_TILE_FRM_RHS_LSB       0
+#define DBE_FRM_SIZE_TILE_FRM_DEPTH_MSB     14
+#define DBE_FRM_SIZE_TILE_FRM_DEPTH_LSB     13
+#define DBE_FRM_SIZE_TILE_FRM_FIFO_RESET_MSB        15
+#define DBE_FRM_SIZE_TILE_FRM_FIFO_RESET_LSB        15
+
+#define DBE_FRM_SIZE_PIXEL_FB_HEIGHT_PIX_MSB        31
+#define DBE_FRM_SIZE_PIXEL_FB_HEIGHT_PIX_LSB        16
+
+#define DBE_DID_CONTROL_DID_DMA_ENABLE_MSB  0
+#define DBE_DID_CONTROL_DID_DMA_ENABLE_LSB  0
+#define DBE_DID_INHWCTRL_DID_DMA_ENABLE_MSB 0
+#define DBE_DID_INHWCTRL_DID_DMA_ENABLE_LSB 0
+
+#define DBE_DID_START_XY_DID_STARTY_MSB     23
+#define DBE_DID_START_XY_DID_STARTY_LSB     12
+#define DBE_DID_START_XY_DID_STARTX_MSB     11
+#define DBE_DID_START_XY_DID_STARTX_LSB     0
+
+#define DBE_CRS_START_XY_CRS_STARTY_MSB     23
+#define DBE_CRS_START_XY_CRS_STARTY_LSB     12
+#define DBE_CRS_START_XY_CRS_STARTX_MSB     11
+#define DBE_CRS_START_XY_CRS_STARTX_LSB     0
+
+#define DBE_WID_TYP_MSB     4
+#define DBE_WID_TYP_LSB     2
+#define DBE_WID_BUF_MSB     1
+#define DBE_WID_BUF_LSB     0
+
+#define DBE_VC_START_XY_VC_STARTY_MSB       23
+#define DBE_VC_START_XY_VC_STARTY_LSB       12
+#define DBE_VC_START_XY_VC_STARTX_MSB       11
+#define DBE_VC_START_XY_VC_STARTX_LSB       0
+
+/* Constants */
+
+#define DBE_FRM_DEPTH_8     0
+#define DBE_FRM_DEPTH_16    1
+#define DBE_FRM_DEPTH_32    2
+
+#define DBE_CMODE_I8        0
+#define DBE_CMODE_I12       1
+#define DBE_CMODE_RG3B2     2
+#define DBE_CMODE_RGB4      3
+#define DBE_CMODE_ARGB5     4
+#define DBE_CMODE_RGB8      5
+#define DBE_CMODE_RGBA5     6
+#define DBE_CMODE_RGB10     7
+
+#define DBE_BMODE_BOTH      3
+
+#define DBE_CRS_MAGIC       54
+
+/* Config Register (DBE Only) Definitions */
+
+#define DBE_CONFIG_VDAC_ENABLE       0x00000001
+#define DBE_CONFIG_VDAC_GSYNC        0x00000002
+#define DBE_CONFIG_VDAC_PBLANK       0x00000004
+#define DBE_CONFIG_FPENABLE          0x00000008
+#define DBE_CONFIG_LENDIAN           0x00000020
+#define DBE_CONFIG_TILEHIST          0x00000040
+#define DBE_CONFIG_EXT_ADDR          0x00000080
+
+#define DBE_CONFIG_FBDEV        ( DBE_CONFIG_VDAC_ENABLE | \
+                                      DBE_CONFIG_VDAC_GSYNC  | \
+                                      DBE_CONFIG_VDAC_PBLANK | \
+                                      DBE_CONFIG_LENDIAN     | \
+                                      DBE_CONFIG_EXT_ADDR )
+
+/*
+ * Available Video Timings and Corresponding Indices
+ */
+
+typedef enum {
+  DBE_VT_640_480_60,
+
+  DBE_VT_800_600_60,
+  DBE_VT_800_600_75,
+  DBE_VT_800_600_120,
+
+  DBE_VT_1024_768_50,
+  DBE_VT_1024_768_60,
+  DBE_VT_1024_768_75,
+  DBE_VT_1024_768_85,
+  DBE_VT_1024_768_120,
+
+  DBE_VT_1280_1024_50,
+  DBE_VT_1280_1024_60,
+  DBE_VT_1280_1024_75,
+  DBE_VT_1280_1024_85,
+
+  DBE_VT_1600_1024_53,
+  DBE_VT_1600_1024_60,
+
+  DBE_VT_1600_1200_50,
+  DBE_VT_1600_1200_60,
+  DBE_VT_1600_1200_75,
+
+  DBE_VT_1920_1080_50,
+  DBE_VT_1920_1080_60,
+  DBE_VT_1920_1080_72,
+
+  DBE_VT_1920_1200_50,
+  DBE_VT_1920_1200_60,
+  DBE_VT_1920_1200_66,
+
+  DBE_VT_UNKNOWN
+} dbe_timing_t;
+
+
+
+/*
+ * Crime Video Timing Data Structure
+ */
+
+typedef struct dbe_timing_info
+{
+  dbe_timing_t type;
+  int flags;				
+  short width;		    /* Monitor resolution		*/
+  short height;
+  int fields_sec;	    /* fields/sec  (Hz -3 dec. places */
+  int cfreq;		    /* pixel clock frequency (MHz -3 dec. places) */
+  short htotal;		    /* Horizontal total pixels	*/
+  short hblank_start;	    /* Horizontal blank start	*/
+  short hblank_end;	    /* Horizontal blank end		*/
+  short hsync_start;	    /* Horizontal sync start	*/
+  short hsync_end;	    /* Horizontal sync end		*/
+  short vtotal;		    /* Vertical total lines		*/
+  short vblank_start;	    /* Vertical blank start		*/
+  short vblank_end;	    /* Vertical blank end		*/
+  short vsync_start;	    /* Vertical sync start		*/
+  short vsync_end;	    /* Vertical sync end		*/
+  short pll_m;		    /* PLL M parameter		*/
+  short pll_n;		    /* PLL P parameter		*/
+  short pll_p;		    /* PLL N parameter		*/
+} dbe_timing_info_t;
+
+/* Defines for dbe_vof_info_t flags */
+
+#define DBE_VOF_UNKNOWNMON    1
+#define DBE_VOF_STEREO        2
+#define DBE_VOF_DO_GENSYNC    4          /* enable incoming sync */
+#define DBE_VOF_SYNC_ON_GREEN 8          /* sync on green */
+#define DBE_VOF_FLATPANEL     0x1000     /* FLATPANEL Timing */
+#define DBE_VOF_MAGICKEY      0x2000     /* Backdoor key */
+
+/*
+ * DBE Timing Tables
+ */
+
+#ifdef INCLUDE_TIMING_TABLE_DATA
+struct dbe_timing_info dbeVTimings[] = {
+  {
+    DBE_VT_640_480_60,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,  	640,			480,		59940,			25175,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    800,	640,		    800,		656,	    	752,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    525,	480,		    525,		490,		    492,
+    /*	pll_m,	pll_n,			pll_p */
+    15,	    2,				3
+  },
+
+  {
+    DBE_VT_800_600_60,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    800,			600,		60317,			40000,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1056,	800,		    1056,		840,		    968,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    628,	600,		    628,		601,		    605,
+    /*	pll_m,	pll_n,			pll_p */
+    3,	    1,				1
+  },
+
+  {
+    DBE_VT_800_600_75,
+    /*	flags,	width,		    height,		fields_sec,	    cfreq */
+    0,	    800,		    600,		75000,		    49500,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1056,	800,		    1056,		816,		    896,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    625,	600,		    625,		601,		    604,
+    /*	pll_m,	pll_n,		    pll_p */
+    11,	    3,		        1
+  },
+
+  {
+    DBE_VT_800_600_120,
+    /*	flags,					width,		height,			fields_sec,	    cfreq */
+    DBE_VOF_STEREO,	    800,		600,			119800,		    82978,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1040,	800,		    1040,		856,		    976,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    666,	600,		    666,		637,		    643,
+    /*	pll_m,	pll_n,		    pll_p */
+    31,	    5,		        1
+  },
+
+  {
+    DBE_VT_1024_768_50,
+    /*	flags,	width,		    height,		fields_sec,	    cfreq */
+    0,	    1024,		    768,		50000,		    54163,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1344,	1024,		    1344,		1048,		    1184,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    806,	768,		    806,		771,		    777,
+    /*	pll_m,	pll_n,		    pll_p */
+    4,	    1,		        1
+  },
+
+  {
+    DBE_VT_1024_768_60,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    1024,			768,		60004,			65000,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1344,	1024,		    1344,		1048,		    1184,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    806,	768,		    806,		771,		    777,
+    /*	pll_m,	pll_n,			pll_p */
+    12,	    5,				0
+  },
+
+  {
+    DBE_VT_1024_768_75,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    1024,			768,		75029,			78750,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1312,	1024,		    1312,		1040,		    1136,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    800,	768,		    800,		769,		    772,
+    /*	pll_m,	pll_n,			pll_p */
+    29,	    5,				1
+  },
+
+  {
+    DBE_VT_1024_768_85,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    1024,			768,		84997,			94500,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1376,	1024,		    1376,		1072,		    1168,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    808,	768,		    808,		769,		    772,
+    /*	pll_m,	pll_n,			pll_p */
+    7,	    2,				0
+  },
+
+  {
+    DBE_VT_1024_768_120,
+    /*	flags,					width,		height,			fields_sec,		cfreq */
+    DBE_VOF_STEREO,	    1024,		768,			119800,			133195,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1376,	1024,		    1376,		1072,		    1168,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    808,	768,		    808,		769,		    772,
+    /*	pll_m,	pll_n,			pll_p */
+    5,	    1,				0
+  },
+
+  {
+    DBE_VT_1280_1024_50,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    1280,			1024,		50000,			89460,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1680,	1280,		    1680,		1360,		    1480,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    1065,	1024,		    1065,		1027,		    1030,
+    /*	pll_m,	pll_n,			pll_p */
+    10,	    3,				0
+  },
+
+  {
+    DBE_VT_1280_1024_60,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    1280,			1024,		60020,			108000,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1688,	1280,		    1688,		1328,		    1440,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    1066,	1024,		    1066,		1025,		    1028,
+    /*	pll_m,	pll_n,			pll_p */
+    4,	    1,			    0
+  },
+
+  {
+    DBE_VT_1280_1024_75,
+    /*	flags,	width,			height,		fields_sec,		cfreq */
+    0,	    1280,			1024,		75025,			135000,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1688,	1280,		    1688,		1296,		    1440,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    1066,	1024,		    1066,		1025,		    1028,
+    /*	pll_m,	pll_n,			pll_p */
+    5,	    1,				0
+  },
+
+  {
+    DBE_VT_1280_1024_85,
+    /*	flags,	width,		    height,		fields_sec,	    cfreq */
+    0,	    1280,		    1024,		85024,		    157500,
+    /*	htotal,	hblank_start,	hblank_end,	hsync_start,	hsync_end */
+    1728,	1280,		    1728,		1344,		    1504,
+    /*	vtotal,	vblank_start,	vblank_end,	vsync_start,	vsync_end */
+    1072,	1024,		    1072,		1025,		    1028,
+    /*	pll_m,	pll_n,		    pll_p */
+    29,	    5,		        0
+  },
+
+  {
+    DBE_VT_1600_1024_53,
+    /* flags,	width,			height,		fields_sec,     cfreq */
+    DBE_VOF_FLATPANEL | DBE_VOF_MAGICKEY,
+    1600,			1024,		53000,			107447,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    1900,   1600,           1900,           1630,           1730,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1067,   1024,           1067,           1027,           1030,
+    /* pll_m,  pll_n,          pll_p */
+    4,      1,              0
+  },
+
+  {
+    DBE_VT_1600_1024_60,
+    /* flags,					width,          height,			fields_sec,     cfreq */
+    DBE_VOF_FLATPANEL,   1600,           1024,			60000,          106913,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    1670,   1600,           1670,           1630,           1650,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1067,   1024,           1067,           1027,           1030,
+    /* pll_m,  pll_n,          pll_p */
+    4,      1,              0
+  },
+
+  {
+    DBE_VT_1600_1200_50,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1600,           1200,           50000,          130500,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2088,   1600,           2088,           1644,           1764,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1250,   1200,           1250,           1205,           1211,
+    /* pll_m,  pll_n,          pll_p */
+    24,     5,              0
+  },
+
+  {
+    DBE_VT_1600_1200_60,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1600,           1200,           59940,          162000,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2160,   1600,           2160,           1644,           1856,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1250,   1200,           1250,           1201,           1204,
+    /* pll_m,  pll_n,          pll_p */
+    6,		1,              0
+  },
+
+  {
+    DBE_VT_1600_1200_75,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1600,           1200,           75000,          202500,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2160,   1600,           2160,           1644,           1856,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1250,   1200,           1250,           1201,           1204,
+    /* pll_m,  pll_n,          pll_p */
+    15,		2,              0
+  },
+
+  {
+    DBE_VT_1920_1080_50,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1920,           1080,           50000,          133200,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2368,   1920,           2368,           1952,           2096,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1125,   1080,           1125,           1083,           1086,
+    /* pll_m,  pll_n,          pll_p */
+    5,      1,              0
+  },
+
+  {
+    DBE_VT_1920_1080_60,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1920,           1080,           59940,          159840,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2368,   1920,           2368,           1952,           2096,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1125,   1080,           1125,           1083,           1086,
+    /* pll_m,  pll_n,          pll_p */
+    6,      1,              0
+  },
+
+  {
+    DBE_VT_1920_1080_72,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1920,           1080,           72000,          216023,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2560,   1920,           2560,           1968,           2184,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1172,   1080,           1172,           1083,           1086,
+    /* pll_m,  pll_n,          pll_p */
+    8,      1,              0
+  },
+
+  {
+    DBE_VT_1920_1200_50,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1920,           1200,           50000,          161500,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2584,   1920,           2584,           1984,           2240,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1250,   1200,           1250,           1203,           1206,
+    /* pll_m,  pll_n,          pll_p */
+    6,      1,              0
+  },
+	
+  {
+    DBE_VT_1920_1200_60,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1920,           1200,           59940,          193800,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2584,   1920,           2584,           1984,           2240,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1250,   1200,           1250,           1203,           1206,
+    /* pll_m,  pll_n,          pll_p */
+    29,     4,              0
+  },
+
+  {
+    DBE_VT_1920_1200_66,
+    /* flags,  width,          height,         fields_sec,     cfreq */
+    0,      1920,           1200,           66000,          213180,
+    /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
+    2584,   1920,           2584,           1984,           2240,
+    /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
+    1250,   1200,           1250,           1203,           1206,
+    /* pll_m,  pll_n,          pll_p */
+    8,      1,              0
+  }
+};
+
+#define DBE_VT_SIZE  (sizeof(dbeVTimings)/sizeof(dbeVTimings[0]))
+#endif // INCLUDE_TIMING_TABLE_DATA
+
+#endif // ! __SGIVWFB_H__
diff -u --recursive --new-file v2.2.3/linux/fs/affs/Changes linux/fs/affs/Changes
--- v2.2.3/linux/fs/affs/Changes	Wed Jun 24 22:54:08 1998
+++ linux/fs/affs/Changes	Sun Mar 21 07:11:37 1999
@@ -28,6 +28,15 @@
 
 Please direct bug reports to: hjw@zvw.de
 
+Version 3.10
+------------
+
+- Changed partition checker to allow devices
+  with physical blocks != 512 bytes.
+
+- The partition checker now also ignores the
+  word at 0xd0 that Windows likes to write to.
+
 Version 3.9
 -----------
 
diff -u --recursive --new-file v2.2.3/linux/fs/autofs/root.c linux/fs/autofs/root.c
--- v2.2.3/linux/fs/autofs/root.c	Tue Feb 23 15:21:34 1999
+++ linux/fs/autofs/root.c	Wed Mar 10 15:07:56 1999
@@ -281,7 +281,7 @@
 	autofs_say(dentry->d_name.name,dentry->d_name.len);
 
 	if ( !autofs_oz_mode(sbi) )
-		return -EPERM;
+		return -EACCES;
 
 	if ( dentry->d_name.len > NAME_MAX )
 		return -ENAMETOOLONG;
@@ -349,17 +349,20 @@
 	struct autofs_dir_ent *ent;
 	unsigned int n;
 
-	if ( !autofs_oz_mode(sbi) )
-		return -EPERM;
+	/* This allows root to remove symlinks */
+	if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
+		return -EACCES;
 
 	ent = autofs_hash_lookup(dh, &dentry->d_name);
 	if ( !ent )
 		return -ENOENT;
 
 	n = ent->ino - AUTOFS_FIRST_SYMLINK;
-	if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) )
-		return -EINVAL;	/* Not a symlink inode, can't unlink */
-       
+	if ( n >= AUTOFS_MAX_SYMLINKS )
+		return -EISDIR;	/* It's a directory, dummy */
+	if ( !test_bit(n,sbi->symlink_bitmap) )
+		return -EINVAL;	/* Nonexistent symlink?  Shouldn't happen */
+	
 	dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL;
 	autofs_hash_delete(ent);
 	clear_bit(n,sbi->symlink_bitmap);
@@ -376,7 +379,7 @@
 	struct autofs_dir_ent *ent;
 
 	if ( !autofs_oz_mode(sbi) )
-		return -EPERM;
+		return -EACCES;
 
 	ent = autofs_hash_lookup(dh, &dentry->d_name);
 	if ( !ent )
@@ -405,7 +408,7 @@
 	ino_t ino;
 
 	if ( !autofs_oz_mode(sbi) )
-		return -EPERM;
+		return -EACCES;
 
 	if ( dentry->d_name.len > NAME_MAX )
 		return -ENAMETOOLONG;
diff -u --recursive --new-file v2.2.3/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.2.3/linux/fs/binfmt_aout.c	Thu Nov 12 16:21:22 1998
+++ linux/fs/binfmt_aout.c	Fri Mar 19 16:27:40 1999
@@ -59,13 +59,13 @@
  * macros to write out all the necessary info.
  */
 #define DUMP_WRITE(addr,nr) \
-while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) goto close_coredump
+while (file->f_op->write(file,(char *)(addr),(nr),&file->f_pos) != (nr)) goto close_coredump
 
 #define DUMP_SEEK(offset) \
-if (file.f_op->llseek) { \
-	if (file.f_op->llseek(&file,(offset),0) != (offset)) \
+if (file->f_op->llseek) { \
+	if (file->f_op->llseek(file,(offset),0) != (offset)) \
  		goto close_coredump; \
-} else file.f_pos = (offset)
+} else file->f_pos = (offset)
 
 /*
  * Routine writes a core dump image in the current directory.
@@ -82,7 +82,7 @@
 {
 	struct dentry * dentry = NULL;
 	struct inode * inode = NULL;
-	struct file file;
+	struct file * file;
 	mm_segment_t fs;
 	int has_dumped = 0;
 	char corefile[6+sizeof(current->comm)];
@@ -116,21 +116,16 @@
 #else
 	corefile[4] = '\0';
 #endif
-	dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
-	if (IS_ERR(dentry)) {
-		dentry = NULL;
+	file = filp_open(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
+	if (IS_ERR(file))
 		goto end_coredump;
-	}
+	dentry = file->f_dentry;
 	inode = dentry->d_inode;
 	if (!S_ISREG(inode->i_mode))
-		goto end_coredump;
+		goto close_coredump;
 	if (!inode->i_op || !inode->i_op->default_file_ops)
-		goto end_coredump;
-	if (get_write_access(inode))
-		goto end_coredump;
-	if (init_private_file(&file, dentry, 3))
-		goto end_coredump_write;
-	if (!file.f_op->write)
+		goto close_coredump;
+	if (!file->f_op->write)
 		goto close_coredump;
 	has_dumped = 1;
 	current->flags |= PF_DUMPCORE;
@@ -211,13 +206,9 @@
 	set_fs(KERNEL_DS);
 	DUMP_WRITE(current,sizeof(*current));
 close_coredump:
-	if (file.f_op->release)
-		file.f_op->release(inode,&file);
-end_coredump_write:
-	put_write_access(inode);
+	close_fp(file, NULL);
 end_coredump:
 	set_fs(fs);
-	dput(dentry);
 	return has_dumped;
 }
 
diff -u --recursive --new-file v2.2.3/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.2.3/linux/fs/binfmt_elf.c	Sun Nov  8 14:03:05 1998
+++ linux/fs/binfmt_elf.c	Fri Mar 19 16:21:36 1999
@@ -1032,10 +1032,10 @@
 #undef DUMP_SEEK
 
 #define DUMP_WRITE(addr, nr)	\
-	if (!dump_write(&file, (addr), (nr))) \
+	if (!dump_write(file, (addr), (nr))) \
 		goto close_coredump;
 #define DUMP_SEEK(off)	\
-	if (!dump_seek(&file, (off))) \
+	if (!dump_seek(file, (off))) \
 		goto close_coredump;
 /*
  * Actual dumper
@@ -1047,7 +1047,7 @@
 static int elf_core_dump(long signr, struct pt_regs * regs)
 {
 	int has_dumped = 0;
-	struct file file;
+	struct file *file;
 	struct dentry *dentry;
 	struct inode *inode;
 	mm_segment_t fs;
@@ -1118,30 +1118,28 @@
 
 	fs = get_fs();
 	set_fs(KERNEL_DS);
+
 	memcpy(corefile,"core.",5);
 #if 0
 	memcpy(corefile+5,current->comm,sizeof(current->comm));
 #else
 	corefile[4] = '\0';
 #endif
-	dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
-	if (IS_ERR(dentry)) {
-		dentry = NULL;
+	file = filp_open(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
+	if (IS_ERR(file))
 		goto end_coredump;
-	}
+	dentry = file->f_dentry;
 	inode = dentry->d_inode;
-
-	if(inode->i_nlink > 1)
-		goto end_coredump;	/* multiple links - don't dump */
+	if (inode->i_nlink > 1)
+		goto close_coredump;	/* multiple links - don't dump */
 
 	if (!S_ISREG(inode->i_mode))
-		goto end_coredump;
+		goto close_coredump;
 	if (!inode->i_op || !inode->i_op->default_file_ops)
-		goto end_coredump;
-	if (init_private_file(&file, dentry, 3))
-		goto end_coredump;
-	if (!file.f_op->write)
 		goto close_coredump;
+	if (!file->f_op->write)
+		goto close_coredump;
+
 	has_dumped = 1;
 	current->flags |= PF_DUMPCORE;
 
@@ -1297,7 +1295,7 @@
 	}
 
 	for(i = 0; i < numnote; i++)
-		if (!writenote(&notes[i], &file))
+		if (!writenote(&notes[i], file))
 			goto close_coredump;
 
 	set_fs(fs);
@@ -1319,19 +1317,17 @@
 		DUMP_WRITE((void *)addr, len);
 	}
 
-	if ((off_t) file.f_pos != offset) {
+	if ((off_t) file->f_pos != offset) {
 		/* Sanity check */
-		printk("elf_core_dump: file.f_pos (%ld) != offset (%ld)\n",
-		       (off_t) file.f_pos, offset);
+		printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n",
+		       (off_t) file->f_pos, offset);
 	}
 
  close_coredump:
-	if (file.f_op->release)
-		file.f_op->release(inode,&file);
+ 	close_fp(file, NULL);
 
  end_coredump:
 	set_fs(fs);
-	dput(dentry);
 #ifndef CONFIG_BINFMT_ELF
 	MOD_DEC_USE_COUNT;
 #endif
diff -u --recursive --new-file v2.2.3/linux/fs/buffer.c linux/fs/buffer.c
--- v2.2.3/linux/fs/buffer.c	Wed Mar 10 15:29:49 1999
+++ linux/fs/buffer.c	Sat Mar 20 12:42:40 1999
@@ -76,6 +76,7 @@
 static int nr_buffers_type[NR_LIST] = {0,};
 static int nr_buffer_heads = 0;
 static int nr_unused_buffer_heads = 0;
+static int nr_hashed_buffers = 0;
 
 /* This is used by some architectures to estimate available memory. */
 int buffermem = 0;
@@ -434,6 +435,7 @@
 		*pprev = next;
 		bh->b_pprev = NULL;
 	}
+	nr_hashed_buffers--;
 }
 
 static inline void remove_from_lru_list(struct buffer_head * bh)
@@ -568,6 +570,7 @@
 			*bhp = bh;
 			bh->b_pprev = bhp;
 		}
+		nr_hashed_buffers++;
 	}
 }
 
@@ -839,21 +842,19 @@
 }
 
 /*
- * bforget() is like brelse(), except it removes the buffer
- * from the hash-queues (so that it won't be re-used if it's
- * shared).
+ * bforget() is like brelse(), except it puts the buffer on the
+ * free list if it can.. We can NOT free the buffer if:
+ *  - there are other users of it
+ *  - it is locked and thus can have active IO
  */
 void __bforget(struct buffer_head * buf)
 {
-	mark_buffer_clean(buf);
-	clear_bit(BH_Protected, &buf->b_state);
-	remove_from_hash_queue(buf);
-	buf->b_dev = NODEV;
-	refile_buffer(buf);
-	if (!--buf->b_count)
+	if (buf->b_count != 1 || buffer_locked(buf)) {
+		__brelse(buf);
 		return;
-	printk("VFS: forgot an in-use buffer! (count=%d)\n",
-		buf->b_count);
+	}
+	remove_from_queues(buf);
+	put_last_free(buf);
 }
 
 /*
@@ -862,20 +863,17 @@
  */
 struct buffer_head * bread(kdev_t dev, int block, int size)
 {
-	struct buffer_head * bh = getblk(dev, block, size);
+	struct buffer_head * bh;
 
-	if (bh) {
-		touch_buffer(bh);
-		if (buffer_uptodate(bh))
-			return bh;
-		ll_rw_block(READ, 1, &bh);
-		wait_on_buffer(bh);
-		if (buffer_uptodate(bh))
-			return bh;
-		brelse(bh);
-		return NULL;
-	}
-	printk("VFS: bread: impossible error\n");
+	bh = getblk(dev, block, size);
+	touch_buffer(bh);
+	if (buffer_uptodate(bh))
+		return bh;
+	ll_rw_block(READ, 1, &bh);
+	wait_on_buffer(bh);
+	if (buffer_uptodate(bh))
+		return bh;
+	brelse(bh);
 	return NULL;
 }
 
@@ -899,9 +897,10 @@
 	if (pos >= filesize)
 		return NULL;
 
-	if (block < 0 || !(bh = getblk(dev,block,bufsize)))
+	if (block < 0)
 		return NULL;
 
+	bh = getblk(dev, block, bufsize);
 	index = BUFSIZE_INDEX(bh->b_size);
 
 	touch_buffer(bh);
@@ -1010,9 +1009,9 @@
 
 	/* This is critical.  We can't swap out pages to get
 	 * more buffer heads, because the swap-out may need
-	 * more buffer-heads itself.  Thus SLAB_ATOMIC.
+	 * more buffer-heads itself.  Thus SLAB_BUFFER.
 	 */
-	if((bh = kmem_cache_alloc(bh_cachep, SLAB_ATOMIC)) != NULL) {
+	if((bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER)) != NULL) {
 		memset(bh, 0, sizeof(*bh));
 		nr_buffer_heads++;
 		return bh;
@@ -1491,6 +1490,7 @@
 	printk("Buffer memory:   %6dkB\n",buffermem>>10);
 	printk("Buffer heads:    %6d\n",nr_buffer_heads);
 	printk("Buffer blocks:   %6d\n",nr_buffers);
+	printk("Buffer hashed:   %6d\n",nr_hashed_buffers);
 
 	for(nlist = 0; nlist < NR_LIST; nlist++) {
 	  found = locked = dirty = used = lastused = protected = 0;
diff -u --recursive --new-file v2.2.3/linux/fs/coda/inode.c linux/fs/coda/inode.c
--- v2.2.3/linux/fs/coda/inode.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/coda/inode.c	Wed Mar 10 17:03:52 1999
@@ -23,9 +23,6 @@
 
 #include <linux/fs.h>
 #include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/locks.h>
-#include <linux/string.h>
 #include <asm/uaccess.h>
 #include <linux/vmalloc.h>
 #include <asm/segment.h>
diff -u --recursive --new-file v2.2.3/linux/fs/coda/upcall.c linux/fs/coda/upcall.c
--- v2.2.3/linux/fs/coda/upcall.c	Fri Jan  8 22:36:13 1999
+++ linux/fs/coda/upcall.c	Wed Mar 10 17:03:52 1999
@@ -30,7 +30,6 @@
 #include <linux/string.h>
 #include <asm/uaccess.h>
 #include <linux/vmalloc.h>
-#include <asm/segment.h>
 
 #include <linux/coda.h>
 #include <linux/coda_linux.h>
diff -u --recursive --new-file v2.2.3/linux/fs/dcache.c linux/fs/dcache.c
--- v2.2.3/linux/fs/dcache.c	Wed Mar 10 15:29:49 1999
+++ linux/fs/dcache.c	Fri Mar 19 08:08:54 1999
@@ -2,7 +2,8 @@
  * fs/dcache.c
  *
  * Complete reimplementation
- * (C) 1997 Thomas Schoebel-Theuer
+ * (C) 1997 Thomas Schoebel-Theuer,
+ * with heavy changes by Linus Torvalds
  */
 
 /*
diff -u --recursive --new-file v2.2.3/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.2.3/linux/fs/ext2/inode.c	Thu May  7 22:51:53 1998
+++ linux/fs/ext2/inode.c	Wed Mar 10 10:42:56 1999
@@ -697,7 +697,7 @@
 		if (buffer_req(bh) && !buffer_uptodate(bh)) {
 			printk ("IO error syncing ext2 inode ["
 				"%s:%08lx]\n",
-				kdevname(inode->i_dev), inode->i_ino);
+				bdevname(inode->i_dev), inode->i_ino);
 			err = -EIO;
 		}
 	}
diff -u --recursive --new-file v2.2.3/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v2.2.3/linux/fs/ext2/super.c	Sun Nov  8 14:03:05 1998
+++ linux/fs/ext2/super.c	Wed Mar 10 11:04:27 1999
@@ -56,9 +56,9 @@
 	    (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
 	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
 		panic ("EXT2-fs panic (device %s): %s: %s\n",
-		       kdevname(sb->s_dev), function, error_buf);
+		       bdevname(sb->s_dev), function, error_buf);
 	printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
-		kdevname(sb->s_dev), function, error_buf);
+		bdevname(sb->s_dev), function, error_buf);
 	if (test_opt (sb, ERRORS_RO) ||
 	    (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
 	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
@@ -87,7 +87,7 @@
 		sb->s_lock=0;
 	sb->s_flags |= MS_RDONLY;
 	panic ("EXT2-fs panic (device %s): %s: %s\n",
-	       kdevname(sb->s_dev), function, error_buf);
+	       bdevname(sb->s_dev), function, error_buf);
 }
 
 void ext2_warning (struct super_block * sb, const char * function,
@@ -99,7 +99,7 @@
 	vsprintf (error_buf, fmt, args);
 	va_end (args);
 	printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n",
-		kdevname(sb->s_dev), function, error_buf);
+		bdevname(sb->s_dev), function, error_buf);
 }
 
 void ext2_put_super (struct super_block * sb)
@@ -444,7 +444,7 @@
 	if (sb->s_magic != EXT2_SUPER_MAGIC) {
 		if (!silent)
 			printk ("VFS: Can't find an ext2 filesystem on dev "
-				"%s.\n", kdevname(dev));
+				"%s.\n", bdevname(dev));
 	failed_mount:
 		sb->s_dev = 0;
 		unlock_super (sb);
@@ -457,14 +457,14 @@
 		if (le32_to_cpu(es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) {
 			printk("EXT2-fs: %s: couldn't mount because of "
 			       "unsupported optional features.\n",
-			       kdevname(dev));
+			       bdevname(dev));
 			goto failed_mount;
 		}
 		if (!(sb->s_flags & MS_RDONLY) &&
 		    (le32_to_cpu(es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
 			printk("EXT2-fs: %s: couldn't mount RDWR because of "
 			       "unsupported optional features.\n",
-			       kdevname(dev));
+			       bdevname(dev));
 			goto failed_mount;
 		}
 	}
@@ -553,13 +553,13 @@
 		if (!silent)
 			printk ("VFS: Can't find an ext2 filesystem on dev "
 				"%s.\n",
-				kdevname(dev));
+				bdevname(dev));
 		goto failed_mount;
 	}
 	if (sb->s_blocksize != bh->b_size) {
 		if (!silent)
 			printk ("VFS: Unsupported blocksize on dev "
-				"%s.\n", kdevname(dev));
+				"%s.\n", bdevname(dev));
 		goto failed_mount;
 	}
 
diff -u --recursive --new-file v2.2.3/linux/fs/inode.c linux/fs/inode.c
--- v2.2.3/linux/fs/inode.c	Tue Feb 23 15:21:34 1999
+++ linux/fs/inode.c	Mon Mar 15 10:18:17 1999
@@ -404,13 +404,20 @@
 	spin_unlock(&inode_lock);
 }
 
-#define INODES_PER_PAGE PAGE_SIZE/sizeof(struct inode)
+
 /*
  * This is called with the spinlock held, but releases
  * the lock when freeing or allocating inodes.
  * Look out! This returns with the inode lock held if
  * it got an inode..
+ *
+ * We do inode allocations two pages at a time to reduce
+ * fragmentation.
  */
+#define INODE_PAGE_ORDER	1
+#define INODE_ALLOCATION_SIZE	(PAGE_SIZE << INODE_PAGE_ORDER)
+#define INODES_PER_ALLOCATION	(INODE_ALLOCATION_SIZE/sizeof(struct inode))
+
 static struct inode * grow_inodes(void)
 {
 	struct inode * inode;
@@ -431,14 +438,14 @@
 	}
 		
 	spin_unlock(&inode_lock);
-	inode = (struct inode *)__get_free_page(GFP_KERNEL);
+	inode = (struct inode *)__get_free_pages(GFP_KERNEL,INODE_PAGE_ORDER);
 	if (inode) {
 		int size;
 		struct inode * tmp;
 
-		spin_lock(&inode_lock);
-		size = PAGE_SIZE - 2*sizeof(struct inode);
+		size = INODE_ALLOCATION_SIZE - 2*sizeof(struct inode);
 		tmp = inode;
+		spin_lock(&inode_lock);
 		do {
 			tmp++;
 			init_once(tmp);
@@ -449,8 +456,8 @@
 		/*
 		 * Update the inode statistics
 		 */
-		inodes_stat.nr_inodes += INODES_PER_PAGE;
-		inodes_stat.nr_free_inodes += INODES_PER_PAGE - 1;
+		inodes_stat.nr_inodes += INODES_PER_ALLOCATION;
+		inodes_stat.nr_free_inodes += INODES_PER_ALLOCATION - 1;
 		return inode;
 	}
 
diff -u --recursive --new-file v2.2.3/linux/fs/locks.c linux/fs/locks.c
--- v2.2.3/linux/fs/locks.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/locks.c	Tue Mar 16 11:31:28 1999
@@ -111,7 +111,7 @@
 
 #include <asm/uaccess.h>
 
-#define OFFSET_MAX	((off_t)0x7fffffff)	/* FIXME: move elsewhere? */
+#define OFFSET_MAX	((off_t)LONG_MAX)	/* FIXME: move elsewhere? */
 
 static int flock_make_lock(struct file *filp, struct file_lock *fl,
 			       unsigned int cmd);
@@ -1170,7 +1170,7 @@
 
 static char *lock_get_status(struct file_lock *fl, int id, char *pfx)
 {
-	static char temp[129];
+	static char temp[155];
 	char *p = temp;
 	struct inode *inode;
 
diff -u --recursive --new-file v2.2.3/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.2.3/linux/fs/ncpfs/inode.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/ncpfs/inode.c	Wed Mar 10 15:16:58 1999
@@ -472,7 +472,7 @@
 		goto out;
 
 	if (((attr->ia_valid & ATTR_GID) &&
-	     (attr->ia_uid != NCP_SERVER(inode)->m.gid)))
+	     (attr->ia_gid != NCP_SERVER(inode)->m.gid)))
 		goto out;
 
 	if (((attr->ia_valid & ATTR_MODE) &&
diff -u --recursive --new-file v2.2.3/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v2.2.3/linux/fs/nfs/file.c	Wed Mar 10 15:29:49 1999
+++ linux/fs/nfs/file.c	Fri Mar 19 14:20:47 1999
@@ -36,7 +36,6 @@
 static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *);
 static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *);
 static int  nfs_file_flush(struct file *);
-static int  nfs_file_close(struct inode *, struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry);
 
 static struct file_operations nfs_file_operations = {
@@ -49,7 +48,7 @@
 	nfs_file_mmap,		/* mmap */
 	NULL,			/* no special open is needed */
 	nfs_file_flush,		/* flush */
-	nfs_file_close,		/* release */
+	NULL,			/* release */
 	nfs_fsync,		/* fsync */
 	NULL,			/* fasync */
 	NULL,			/* check_media_change */
@@ -86,35 +85,23 @@
 #endif
 
 /*
- * Sync the file..
+ * Flush all dirty pages, and check for write errors.
+ *
  */
 static int
-nfs_file_close(struct inode *inode, struct file *file)
+nfs_file_flush(struct file *file)
 {
-	int	status;
+	struct inode	*inode = file->f_dentry->d_inode;
+	int		status;
 
-	dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino);
+	dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
-	status = nfs_wb_all(inode);
-	if (!status)
+	status = nfs_wb_file(inode, file);
+	if (!status) {
 		status = file->f_error;
+		file->f_error = 0;
+	}
 	return status;
-}
-
-/*
- * Flush all dirty pages, and check for write errors.
- *
- * We should probably do this better - this does get called at every
- * close, so we should probably just flush the changes that "this"
- * file has done and report on only those.
- *
- * Right now we use the "flush everything" approach. Overkill, but
- * works.
- */
-static int
-nfs_file_flush(struct file *file)
-{
-	return nfs_file_close(file->f_dentry->d_inode, file);
 }
 
 static ssize_t
diff -u --recursive --new-file v2.2.3/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.2.3/linux/fs/nfsd/nfsfh.c	Tue Jan 19 11:32:52 1999
+++ linux/fs/nfsd/nfsfh.c	Sat Mar 20 12:28:14 1999
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/dcache.h>
+#include <asm/pgtable.h>
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
@@ -235,7 +236,7 @@
 /*
  * Search for a path entry for the specified (dev, inode).
  */
-struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino)
+static struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino)
 {
 	struct nfsd_path *pe;
 	struct list_head *tmp;
@@ -713,7 +714,6 @@
 		dentry = dget(fhe->dentry);
 		goto out;
 	}
-
 	/*
 	 * Search the path cache ...
 	 */
@@ -859,6 +859,34 @@
 	return NULL;
 }
 
+void
+expire_all(void)
+{
+ 	if (time_after_eq(jiffies, nfsd_next_expire)) {
+ 		expire_old(NFSD_FILE_CACHE,  5*HZ);
+ 		expire_old(NFSD_DIR_CACHE , 60*HZ);
+ 		nfsd_next_expire = jiffies + 5*HZ;
+ 	}
+}
+
+/* 
+ * Free cache after unlink/rmdir.
+ */
+void
+expire_by_dentry(struct dentry *dentry)
+{
+	struct fh_entry *fhe;
+
+	fhe = find_fhe(dentry, NFSD_FILE_CACHE, NULL);
+	if (fhe) {
+		expire_fhe(fhe, NFSD_FILE_CACHE);
+	}
+	fhe = find_fhe(dentry, NFSD_DIR_CACHE, NULL);
+	if (fhe) {
+		expire_fhe(fhe, NFSD_DIR_CACHE);
+	}
+}
+
 /*
  * The is the basic lookup mechanism for turning an NFS file handle 
  * into a dentry. There are several levels to the search:
@@ -997,15 +1025,8 @@
 		add_to_lookup_cache(dentry, fh);
 	}
 
-	/*
-	 * Perform any needed housekeeping ...
-	 * N.B. move this into one of the daemons ...
-	 */
-	if (time_after_eq(jiffies, nfsd_next_expire)) {
-		expire_old(NFSD_FILE_CACHE,  5*HZ);
-		expire_old(NFSD_DIR_CACHE , 60*HZ);
-		nfsd_next_expire = jiffies + 5*HZ;
-	}
+	expire_all();
+
 	return dentry;
 }
 
@@ -1133,7 +1154,7 @@
 #ifdef NFSD_PARANOIA
 if (error)
 printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, access, error);
+dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
 #endif
 out:
 	return error;
@@ -1260,11 +1281,8 @@
 		goto bad_addr;
 	if ((dent_addr & ~align_mask) != dent_addr)
 		goto bad_align;
-	/* XXX: Should test here, whether the address doesn't belong to
-	        a physical memory hole on sparc32/sparc64. Then it is not
-	        safe to dereference it. On the other side, the previous
-	        use of num_physpages instead of max_mapnr caused the same
-	        to happen, plus some valid addresses could get rejected. -jj */
+	if (!kern_addr_valid(dent_addr))
+		goto bad_addr;
 	/*
 	 * Looks safe enough to dereference ...
 	 */
diff -u --recursive --new-file v2.2.3/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.2.3/linux/fs/nfsd/nfssvc.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/nfsd/nfssvc.c	Sat Mar 20 12:28:14 1999
@@ -101,7 +101,7 @@
 nfsd(struct svc_rqst *rqstp)
 {
 	struct svc_serv	*serv = rqstp->rq_server;
-	int		oldumask, err;
+	int		oldumask, err, first = 0;
 
 	/* Lock module and set up kernel thread */
 	MOD_INC_USE_COUNT;
@@ -115,8 +115,10 @@
 
 	oldumask = current->fs->umask;		/* Set umask to 0.  */
 	current->fs->umask = 0;
-	if (!nfsd_active++)
+	if (!nfsd_active++) {
 		nfssvc_boot = xtime;		/* record boot time */
+		first = 1;
+	}
 	lockd_up();				/* start lockd */
 
 	/*
@@ -133,8 +135,14 @@
 		 * Find a socket with data available and call its
 		 * recvfrom routine.
 		 */
-		while ((err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
-			;
+		while ((err = svc_recv(serv, rqstp,
+		        first?5*HZ:MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) {
+			if (first && 1) {
+				exp_readlock();
+				expire_all();
+				exp_unlock();
+			}
+		}
 		if (err < 0)
 			break;
 
diff -u --recursive --new-file v2.2.3/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.2.3/linux/fs/nfsd/vfs.c	Tue Feb 23 15:21:34 1999
+++ linux/fs/nfsd/vfs.c	Sat Mar 20 12:28:14 1999
@@ -11,7 +11,7 @@
  * So if you notice code paths that apparently fail to dput() the
  * dentry, don't worry--they have been taken care of.
  *
- * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/config.h>
@@ -1139,8 +1139,11 @@
 		goto out;
 	}
 
+	expire_by_dentry(rdentry);
+
 	if (type != S_IFDIR) {
 		/* It's UNLINK */
+
 		err = fh_lock_parent(fhp, rdentry);
 		if (err)
 			goto out;
@@ -1155,6 +1158,7 @@
 	} else {
 		/* It's RMDIR */
 		/* See comments in fs/namei.c:do_rmdir */
+
 		rdentry->d_count++;
 		nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem);
 		if (!fhp->fh_pre_mtime)
diff -u --recursive --new-file v2.2.3/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.2.3/linux/fs/ntfs/fs.c	Mon Jan  4 15:08:17 1999
+++ linux/fs/ntfs/fs.c	Fri Mar 19 08:07:34 1999
@@ -505,6 +505,7 @@
 #endif
 	r->i_mode &= ~vol->umask;
 
+	insert_inode_hash(r);
 	d_instantiate(d,r);
 	return 0;
  fail:
@@ -567,6 +568,7 @@
 #endif
 	r->i_mode &= ~vol->umask;	
 	
+	insert_inode_hash(r);
 	d_instantiate(d, r);
 	error = 0;
  out:
diff -u --recursive --new-file v2.2.3/linux/fs/open.c linux/fs/open.c
--- v2.2.3/linux/fs/open.c	Tue Feb 23 15:21:34 1999
+++ linux/fs/open.c	Tue Mar  9 08:54:34 1999
@@ -751,12 +751,7 @@
  */
 asmlinkage int sys_creat(const char * pathname, int mode)
 {
-	int ret;
-
-	lock_kernel();
-	ret = sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
-	unlock_kernel();
-	return ret;
+	return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
 }
 
 #endif
diff -u --recursive --new-file v2.2.3/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.2.3/linux/fs/proc/array.c	Wed Mar 10 15:29:49 1999
+++ linux/fs/proc/array.c	Wed Mar 10 21:49:10 1999
@@ -550,7 +550,23 @@
 	    } while (count++ < 16);
 	}
 #elif defined(__powerpc__)
-	return (p->tss.wchan);
+	{
+		unsigned long ip, sp;
+		unsigned long stack_page = (unsigned long) p;
+		int count = 0;
+
+		sp = p->tss.ksp;
+		do {
+			sp = *(unsigned long *)sp;
+			if (sp < stack_page || sp >= stack_page + 8188)
+				return 0;
+			if (count > 0) {
+				ip = *(unsigned long *)(sp + 4);
+				if (ip < first_sched || ip >= last_sched)
+					return ip;
+			}
+		} while (count++ < 16);
+	}
 #elif defined (CONFIG_ARM)
 	{
 		unsigned long fp, lr;
diff -u --recursive --new-file v2.2.3/linux/fs/sysv/ialloc.c linux/fs/sysv/ialloc.c
--- v2.2.3/linux/fs/sysv/ialloc.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/sysv/ialloc.c	Sun Mar 21 07:11:37 1999
@@ -86,6 +86,7 @@
 		return;
 	}
 	raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1);
+	clear_inode(inode);
 	lock_super(sb);
 	if (*sb->sv_sb_fic_count < sb->sv_fic_size)
 		*sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)++) = ino;
@@ -97,7 +98,6 @@
 	mark_buffer_dirty(bh, 1);
 	unlock_super(sb);
 	brelse(bh);
-	clear_inode(inode);
 }
 
 struct inode * sysv_new_inode(const struct inode * dir)
diff -u --recursive --new-file v2.2.3/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.2.3/linux/fs/sysv/inode.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/sysv/inode.c	Sun Mar 21 07:11:37 1999
@@ -55,10 +55,8 @@
 }
 #endif
 
-void sysv_put_inode(struct inode *inode)
+static void sysv_delete_inode(struct inode *inode)
 {
-	if (inode->i_nlink)
-		return;
 	inode->i_size = 0;
 	sysv_truncate(inode);
 	sysv_free_inode(inode);
@@ -68,8 +66,8 @@
 static struct super_operations sysv_sops = {
 	sysv_read_inode,
 	sysv_write_inode,
-	sysv_put_inode,
-	NULL,			/* delete_inode */
+	NULL,			/* nothing special on put_inode() */
+	sysv_delete_inode,
 	sysv_notify_change,
 	sysv_put_super,
 	sysv_write_super,
diff -u --recursive --new-file v2.2.3/linux/fs/sysv/namei.c linux/fs/sysv/namei.c
--- v2.2.3/linux/fs/sysv/namei.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/sysv/namei.c	Sun Mar 21 07:11:37 1999
@@ -576,6 +576,7 @@
 	oldinode->i_nlink++;
 	oldinode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(oldinode);
+	inode->i_count++;
         d_instantiate(dentry, oldinode);
 	return 0;
 }
diff -u --recursive --new-file v2.2.3/linux/include/asm-alpha/cache.h linux/include/asm-alpha/cache.h
--- v2.2.3/linux/include/asm-alpha/cache.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/cache.h	Mon Mar 15 16:11:55 1999
@@ -8,5 +8,6 @@
    read-allocate, direct-mapped, physical. */
 #define L1_CACHE_BYTES     32
 #define L1_CACHE_ALIGN(x)  (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
+#define SMP_CACHE_BYTES    L1_CACHE_BYTES
 
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v2.2.3/linux/include/asm-alpha/pgtable.h	Thu Dec 31 10:29:02 1998
+++ linux/include/asm-alpha/pgtable.h	Wed Mar 10 18:11:51 1999
@@ -651,5 +651,6 @@
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)		(0)
+#define kern_addr_valid(addr)	(1)
 
 #endif /* _ALPHA_PGTABLE_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-alpha/semaphore-helper.h linux/include/asm-alpha/semaphore-helper.h
--- v2.2.3/linux/include/asm-alpha/semaphore-helper.h	Wed Mar 10 15:29:49 1999
+++ linux/include/asm-alpha/semaphore-helper.h	Fri Mar 19 08:06:56 1999
@@ -73,7 +73,10 @@
 	           ret = 1;
 	       }
 	       else if (pending) {
-	           tmp += 1;
+		   // Since -1 + 1 carries into the high word, we have
+		   // to be more careful adding 1 here.
+		   tmp = (tmp & 0xffffffff00000000)
+			 | ((tmp + 1) & 0x00000000ffffffff;
 	           ret = -EINTR;
 	       }
 	       else {
@@ -89,11 +92,13 @@
 	__asm__ __volatile__(
 		"1:	ldq_l	%1,%4\n"
 		"	lda	%0,0\n"
-		"	addq	%1,1,%2\n"
-		"	ldah	%3,0x8000(%1)\n"
 		"	cmovne	%5,%6,%0\n"
-		"	ldah	%3,0x8000(%3)\n"
+		"	addq	%1,1,%2\n"
+		"	and	%1,%7,%3\n"
+		"	andnot	%2,%7,%2\n"
 		"	cmovge	%1,1,%0\n"
+		"	or	%3,%2,%2\n"
+		"	addq	%1,%7,%3\n"
 		"	cmovne	%5,%2,%1\n"
 		"	cmovge	%2,%3,%1\n"
 		"	stq_c	%1,%4\n"
@@ -103,7 +108,8 @@
 		"3:	br	1b\n"
 		".previous"
 		: "=&r"(ret), "=&r"(tmp), "=&r"(tmp2), "=&r"(tmp3), "=m"(*sem)
-		: "r"(signal_pending(tsk)), "r"(-EINTR));
+		: "r"(signal_pending(tsk)), "r"(-EINTR),
+		  "r"(0xffffffff00000000));
 
 	return ret;
 }
diff -u --recursive --new-file v2.2.3/linux/include/asm-arm/cache.h linux/include/asm-arm/cache.h
--- v2.2.3/linux/include/asm-arm/cache.h	Tue Jan 20 16:39:42 1998
+++ linux/include/asm-arm/cache.h	Mon Mar 15 16:11:55 1999
@@ -6,5 +6,6 @@
 
 #define        L1_CACHE_BYTES  32
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
+#define        SMP_CACHE_BYTES L1_CACHE_BYTES
 
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-arm/pgtable.h linux/include/asm-arm/pgtable.h
--- v2.2.3/linux/include/asm-arm/pgtable.h	Wed Sep  9 14:51:12 1998
+++ linux/include/asm-arm/pgtable.h	Wed Mar 10 18:11:51 1999
@@ -11,5 +11,6 @@
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)			(0)
+#define kern_addr_valid(addr)		(1)
 
 #endif /* _ASMARM_PGTABLE_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-i386/cache.h linux/include/asm-i386/cache.h
--- v2.2.3/linux/include/asm-i386/cache.h	Sun Jan 26 02:07:46 1997
+++ linux/include/asm-i386/cache.h	Mon Mar 15 16:11:55 1999
@@ -13,4 +13,6 @@
 
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 
+#define        SMP_CACHE_BYTES L1_CACHE_BYTES
+
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.2.3/linux/include/asm-i386/pgtable.h	Fri Jan  1 12:58:21 1999
+++ linux/include/asm-i386/pgtable.h	Sun Mar 21 14:18:22 1999
@@ -594,5 +594,6 @@
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)		(0)
+#define kern_addr_valid(addr)	(1)
 
 #endif /* _I386_PAGE_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-m68k/cache.h linux/include/asm-m68k/cache.h
--- v2.2.3/linux/include/asm-m68k/cache.h	Wed Apr 23 19:01:27 1997
+++ linux/include/asm-m68k/cache.h	Mon Mar 15 16:11:55 1999
@@ -9,4 +9,6 @@
 
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 
+#define        SMP_CACHE_BYTES L1_CACHE_BYTES
+
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-m68k/pgtable.h linux/include/asm-m68k/pgtable.h
--- v2.2.3/linux/include/asm-m68k/pgtable.h	Wed Jan 20 23:14:06 1999
+++ linux/include/asm-m68k/pgtable.h	Wed Mar 10 18:11:51 1999
@@ -813,5 +813,6 @@
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)		(0)
+#define kern_addr_valid(addr)	(1)
 
 #endif /* _M68K_PGTABLE_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-mips/cache.h linux/include/asm-mips/cache.h
--- v2.2.3/linux/include/asm-mips/cache.h	Thu Jun 26 12:33:39 1997
+++ linux/include/asm-mips/cache.h	Mon Mar 15 16:11:55 1999
@@ -9,4 +9,6 @@
 
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 
+#define        SMP_CACHE_BYTES L1_CACHE_BYTES
+
 #endif /* __ASM_MIPS_CACHE_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-mips/pgtable.h linux/include/asm-mips/pgtable.h
--- v2.2.3/linux/include/asm-mips/pgtable.h	Fri Oct 23 22:01:23 1998
+++ linux/include/asm-mips/pgtable.h	Wed Mar 10 18:11:51 1999
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.15 1998/08/20 14:40:57 ralf Exp $
+/* $Id: pgtable.h,v 1.19 1998/10/26 19:59:39 davem Exp $
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -589,6 +589,7 @@
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)		(0)
+#define kern_addr_valid(addr)	(1)
 
 /* TLB operations. */
 extern inline void tlb_probe(void)
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/adb.h linux/include/asm-ppc/adb.h
--- v2.2.3/linux/include/asm-ppc/adb.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/adb.h	Wed Mar 10 21:30:32 1999
@@ -60,6 +60,9 @@
 		 void (*handler)(unsigned char *, int, struct pt_regs *, int));
 void adb_input(unsigned char *, int, struct pt_regs *, int);
 
+int adb_try_handler_change(int address, int new_id);
+int adb_get_infos(int address, int *original_address, int *handler_id);
+
 #endif /* __KERNEL__ */
 
 #endif /* __PPC_ADB_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/bootx.h linux/include/asm-ppc/bootx.h
--- v2.2.3/linux/include/asm-ppc/bootx.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/bootx.h	Wed Mar 10 21:30:32 1999
@@ -18,7 +18,14 @@
 #pragma options align=power
 #endif
 
-#define BOOT_INFO_VERSION		1
+/* On boostrap entry:
+ *
+ * r3 = 0x426f6f58	('BooX')
+ * r4 = pointer to boot_infos
+ * r5 = NULL
+ */
+
+#define BOOT_INFO_VERSION		2
 #define BOOT_INFO_COMPATIBLE_VERSION	1
 
 /* Here are the boot informations that are passed to the bootstrap
@@ -31,8 +38,12 @@
 	/* backward compatible down to version: */
 	unsigned long	compatible_version;
 	
+	/* NEW (vers. 2) this holds the current _logical_ base addr of
+	   the frame buffer (for use by early boot message) */
+	unsigned char*	logicalDisplayBase;
+
 	/* Set to 0 by current BootX */
-	unsigned long	unused[3];
+	unsigned long	unused[2];
 	
 	/* The device tree (internal addresses relative to the beginning of the tree,
 	 * device tree offset relative to the beginning of this structure). */
@@ -55,17 +66,18 @@
 	
 	/* Kernel command line arguments (offset from this structure) */
 	unsigned long	kernelParamsOffset;
-	
+		
 } boot_infos_t;
 
 /* (*) The format of the colormap is 256 * 3 * 2 bytes. Each color index is represented
  * by 3 short words containing a 16 bits (unsigned) color component.
  * Later versions may contain the gamma table for direct-color devices here.
  */
-#define BOOTX_COLORTABLE_SIZE	(256UL*3UL*2UL);
+#define BOOTX_COLORTABLE_SIZE	(256UL*3UL*2UL)
 
 #ifdef macintosh
 #pragma options align=reset
 #endif
 
 #endif
+
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/cache.h linux/include/asm-ppc/cache.h
--- v2.2.3/linux/include/asm-ppc/cache.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/cache.h	Fri Mar 19 10:50:51 1999
@@ -13,7 +13,11 @@
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 #define L1_CACHE_PAGES		8
 
+#define        SMP_CACHE_BYTES L1_CACHE_BYTES
+
 #if defined(__KERNEL__) && !defined(__ASSEMBLY__)
+extern void flush_dcache_range(unsigned long start, unsigned long stop);
+
 static inline unsigned long unlock_dcache(void)
 {
 #ifndef CONFIG_8xx	
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/dma.h linux/include/asm-ppc/dma.h
--- v2.2.3/linux/include/asm-ppc/dma.h	Fri Jan  8 22:36:24 1999
+++ linux/include/asm-ppc/dma.h	Fri Mar 19 10:50:09 1999
@@ -223,15 +223,47 @@
 /* enable/disable a specific DMA channel */
 static __inline__ void enable_dma(unsigned int dmanr)
 {
+	/*
+	 * The Radstone PPC2 and PPC2a boards have inverted DREQ
+	 * lines (active low) so each command needs to be logically
+	 * ORed with 0x40
+	 */
+	unsigned char ucDmaCmd=0x00;
+
+	if(_prep_type==_PREP_Radstone)
+	{
+		switch(ucSystemType)
+		{
+			case RS_SYS_TYPE_PPC2:
+			case RS_SYS_TYPE_PPC2a:
+			case RS_SYS_TYPE_PPC2ep:
+			{
+				/*
+				 * DREQ lines are active low
+				 */
+				ucDmaCmd=0x40;
+				break;
+			}
+
+			default:
+			{
+				/*
+				 * DREQ lines are active high
+				 */
+				break;
+			}
+		}
+	}
+
 	if (dmanr != 4)
 	{
 		dma_outb(0, DMA2_MASK_REG);  /* This may not be enabled */
-		dma_outb(0, DMA2_CMD_REG);  /* Enable group */
+		dma_outb(ucDmaCmd, DMA2_CMD_REG);  /* Enable group */
 	}
 	if (dmanr<=3)
 	{
 		dma_outb(dmanr,  DMA1_MASK_REG);
-		dma_outb(0, DMA1_CMD_REG);  /* Enable group */
+		dma_outb(ucDmaCmd, DMA1_CMD_REG);  /* Enable group */
 	} else
 	{
 		dma_outb(dmanr & 3,  DMA2_MASK_REG);
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/feature.h linux/include/asm-ppc/feature.h
--- v2.2.3/linux/include/asm-ppc/feature.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/feature.h	Wed Mar 10 21:30:32 1999
@@ -35,7 +35,6 @@
 	FEATURE_Mediabay_floppy_enable,
 	FEATURE_BMac_reset,
 	FEATURE_BMac_IO_enable,
-	FEATURE_Modem_PowerOn,
 	FEATURE_Modem_Reset,
 	FEATURE_last,
 };
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/ide.h linux/include/asm-ppc/ide.h
--- v2.2.3/linux/include/asm-ppc/ide.h	Fri Jan  8 22:36:24 1999
+++ linux/include/asm-ppc/ide.h	Wed Mar 10 21:30:32 1999
@@ -12,79 +12,85 @@
 #define __ASMPPC_IDE_H
 
 #include <linux/config.h>
+/*
+ * On APUS, nearly everything comes from the m68k file
+ * -- Cort
+ */
 #ifdef CONFIG_APUS
 #include <linux/hdreg.h>
-
 #define ide_init_hwif_ports m68k_ide_init_hwif_ports 
 #include <asm-m68k/ide.h>
 #undef ide_init_hwif_ports
-#undef insw
+#endif /* CONFIG_APUS */
+
+#ifndef MAX_HWIFS
+#define MAX_HWIFS	4
+#endif
+
+typedef unsigned int ide_ioreg_t;
+
+extern int pmac_ide_ports_known;
+extern ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
+extern int pmac_ide_irq[MAX_HWIFS];
+extern void pmac_ide_probe(void);
+
+extern int chrp_ide_ports_known;
+extern ide_ioreg_t chrp_ide_regbase[MAX_HWIFS];
+extern ide_ioreg_t chrp_idedma_regbase; /* one for both channels */
+extern unsigned int chrp_ide_irq;
+extern void chrp_ide_probe(void);
 
 void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void prep_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
+void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 void ide_insw(ide_ioreg_t port, void *buf, int ns);
 void ide_outsw(ide_ioreg_t port, void *buf, int ns);
-#define insw(port, buf, ns) 	do {			\
-	if ( _machine != _MACH_Pmac && _machine != _MACH_apus )	\
+
+#undef insw
+#define insw(port, buf, ns) 	do {				\
+	if ( _machine & (_MACH_chrp|_MACH_mbx) )		\
+		 ide_insw((port)+_IO_BASE, (buf), (ns));  	\
+	else if ( _machine & (_MACH_Pmac|_MACH_apus) )		\
+		 ide_insw((port), (buf), (ns));  		\
+	else							\
 		/* this must be the same as insw in io.h!! */	\
 		_insw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
-	else						\
-		ide_insw((port), (buf), (ns));		\
 } while (0)
+     
 #undef outsw
-#define outsw(port, buf, ns) 	do {			\
-	if ( _machine != _MACH_Pmac && _machine != _MACH_apus )	\
+#define outsw(port, buf, ns) 	do {				\
+	if ( _machine & (_MACH_chrp|_MACH_mbx) ) 		\
+		ide_outsw((port)+_IO_BASE, (buf), (ns)); 	\
+	else if ( _machine & (_MACH_Pmac|_MACH_apus) )		\
+		ide_outsw((port), (buf), (ns)); 		\
+	else							\
 		/* this must be the same as outsw in io.h!! */	\
 		_outsw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
-	else						\
-		ide_outsw((port), (buf), (ns));		\
 } while (0)
-#else /* CONFIG_APUS */
 
+#ifndef CONFIG_APUS
 #ifdef __KERNEL__
 
 #include <linux/hdreg.h>
 #include <linux/ioport.h>
-#include <asm/io.h>		/* so we can redefine insw/outsw */
-
-#ifndef MAX_HWIFS
-#define MAX_HWIFS	4
-#endif
+#include <asm/io.h>
 
 #undef	SUPPORT_SLOW_DATA_PORTS
 #define	SUPPORT_SLOW_DATA_PORTS	0
 #undef	SUPPORT_VLB_SYNC
 #define SUPPORT_VLB_SYNC	0
 
-
 #define ide__sti()	__sti()
 
-typedef unsigned int ide_ioreg_t;
-void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
-void prep_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
-void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
-void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
-void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
-void ide_insw(ide_ioreg_t port, void *buf, int ns);
-void ide_outsw(ide_ioreg_t port, void *buf, int ns);
-
-extern int pmac_ide_ports_known;
-extern ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
-extern int pmac_ide_irq[MAX_HWIFS];
-extern void pmac_ide_probe(void);
-
-extern int chrp_ide_ports_known;
-extern ide_ioreg_t chrp_ide_regbase[MAX_HWIFS];
-extern ide_ioreg_t chrp_idedma_regbase; /* one for both channels */
-extern unsigned int chrp_ide_irq;
-extern void chrp_ide_probe(void);
-
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
 	if ( _machine == _MACH_Pmac )
 		return 0;
 	else if ( _machine == _MACH_mbx )
-		/* hardcode IRQ 14 on the MBX */
-		return 14+16;		     
+		/* IRQ 14 when in legacy mode on MBX */
+		return 14;
         else if ( _machine == _MACH_chrp) {
                 if (chrp_ide_ports_known == 0) 
 			chrp_ide_probe();
@@ -149,7 +155,7 @@
    (Despite what it says in drivers/block/ide.h, they come up as little endian...)
    Changes to linux/hdreg.h may require changes here. */
 static __inline__ void ide_fix_driveid (struct hd_driveid *id) {
-	if (( _machine == _MACH_Pmac ) || (_machine == _MACH_chrp)|| (_machine == _MACH_mbx) ) {
+  if ( _machine & (_MACH_chrp|_MACH_mbx|_MACH_Pmac) ) { 	\
 		int i;
 		unsigned short *stringcast;
 		id->config         = __le16_to_cpu(id->config);
@@ -255,45 +261,17 @@
 	}
 }
 
-#undef insw
-#define insw(port, buf, ns) 	do {			\
-	if ( _machine == _MACH_chrp)  {\
-		 ide_insw((port)+_IO_BASE, (buf), (ns));  \
-	}\
-	else if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )			\
-		ide_insw((port)+((_machine==_MACH_mbx)? 0x80000000: 0), \
-			 (buf), (ns));		\
-	else						\
-		/* this must be the same as insw in io.h!! */	\
-		_insw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
-} while (0)
-#undef outsw
-/*	printk("port: %x buf: %p ns: %d\n",port,buf,ns); \ */
-#define outsw(port, buf, ns) 	do {			\
-	if ( _machine == _MACH_chrp) {\
-		ide_outsw((port)+_IO_BASE, (buf), (ns)); \
-	}\
-	else if ( (_machine == _MACH_Pmac) || (_machine == _MACH_mbx) )	 \
-		ide_outsw((port)+((_machine==_MACH_mbx)? 0x80000000: 0), \
-			   (buf), (ns));		\
-	else						\
-		/* this must be the same as outsw in io.h!! */	\
-		_outsw((unsigned short *)((port)+_IO_BASE), (buf), (ns)); \
-} while (0)
-
 #undef inb
 #define inb(port)	\
 	in_8((unsigned char *)((port) + \
-			       ((_machine==_MACH_Pmac)? 0: _IO_BASE) + \
-			       ((_machine==_MACH_mbx)? 0x80000000: 0)) )
+			       ((_machine==_MACH_Pmac)? 0: _IO_BASE) ) )
 #undef inb_p
 #define inb_p(port)	inb(port)
 
 #undef outb
 #define outb(val, port)	\
 	out_8((unsigned char *)((port) + \
-				((_machine==_MACH_Pmac)? 0: _IO_BASE) + \
-				((_machine==_MACH_mbx)? 0x80000000: 0)), (val) )
+				((_machine==_MACH_Pmac)? 0: _IO_BASE) ), (val) )
 #undef outb_p
 #define outb_p(val, port)	outb(val, port)
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/io.h linux/include/asm-ppc/io.h
--- v2.2.3/linux/include/asm-ppc/io.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-ppc/io.h	Wed Mar 10 21:30:32 1999
@@ -7,17 +7,9 @@
 
 #define KERNELBASE	0xc0000000
 
-/* from the Carolina Technical Spec -- Cort */
-#define IBM_ACORN 0x82A
 #define SIO_CONFIG_RA	0x398
 #define SIO_CONFIG_RD	0x399
 
-#define IBM_HDD_LED       0x808
-#define IBM_EQUIP_PRESENT 0x80c	
-#define IBM_L2_STATUS     0x80d
-#define IBM_L2_INVALIDATE 0x814
-#define IBM_SYS_CTL       0x81c
-
 #define SLOW_DOWN_IO
 
 #define PMAC_ISA_MEM_BASE 	0
@@ -27,11 +19,10 @@
 #define CHRP_PCI_DRAM_OFFSET 	0
 #define PREP_ISA_IO_BASE 	0x80000000
 #define PREP_ISA_MEM_BASE 	0xd0000000
-/*#define PREP_ISA_MEM_BASE 	0xc0000000*/
 #define PREP_PCI_DRAM_OFFSET 	0x80000000
 
 #ifdef CONFIG_MBX
-#define _IO_BASE        0
+#define _IO_BASE        0x80000000
 #define _ISA_MEM_BASE   0
 #define PCI_DRAM_OFFSET 0x80000000
 #else /* CONFIG_MBX8xx */
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/irq.h linux/include/asm-ppc/irq.h
--- v2.2.3/linux/include/asm-ppc/irq.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/irq.h	Wed Mar 10 21:30:32 1999
@@ -5,6 +5,9 @@
 
 #include <asm/processor.h>		/* for is_prep() */
 
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
 #ifndef CONFIG_8xx
 
 #ifdef CONFIG_APUS
@@ -31,9 +34,6 @@
 #define irq_to_openpic(n)	((n)-NUM_8259_INTERRUPTS)
 #define IRQ_8259_CASCADE	NUM_8259_INTERRUPTS
 
-extern void disable_irq(unsigned int);
-extern void enable_irq(unsigned int);
-
 #ifndef CONFIG_APUS
 /*
  * This gets called from serial.c, which is now used on
@@ -54,28 +54,26 @@
  * There are eight external interrupts (IRQs) that can be configured
  * as either level or edge sensitive. 
  * On the MBX implementation, there is also the possibility of an 8259
- * through the PCI and PCI-ISA bridges.  All 8259 interrupts appear
- * on the 8xx as IRQ3, but I may eventually add some of the 8259 code
- * back into this port to handle that controller.
+ * through the PCI and PCI-ISA bridges.
  */
-#define NR_IRQS	16
+#define NR_IRQS	(16+16) /* 8259 has 16, too -- Cort */
 
-#define	SIU_IRQ0	0	/* Highest priority */
-#define	SIU_LEVEL0	1
-#define	SIU_IRQ1	2
-#define	SIU_LEVEL1	3
-#define	SIU_IRQ2	4
-#define	SIU_LEVEL2	5
-#define	SIU_IRQ3	6
-#define	SIU_LEVEL3	7
-#define	SIU_IRQ4	8
-#define	SIU_LEVEL4	9
-#define	SIU_IRQ5	10
-#define	SIU_LEVEL5	11
-#define	SIU_IRQ6	12
-#define	SIU_LEVEL6	13
-#define	SIU_IRQ7	14
-#define	SIU_LEVEL7	15
+#define	SIU_IRQ0	(0+16)	/* Highest priority */
+#define	SIU_LEVEL0	(1+16)
+#define	SIU_IRQ1	(2+16)
+#define	SIU_LEVEL1	(3+16)
+#define	SIU_IRQ2	(4+16)
+#define	SIU_LEVEL2	(5+16)
+#define	SIU_IRQ3	(6+16)
+#define	SIU_LEVEL3	(7+16)
+#define	SIU_IRQ4	(8+16)
+#define	SIU_LEVEL4	(9+16)
+#define	SIU_IRQ5	(10+16)
+#define	SIU_LEVEL5	(11+16)
+#define	SIU_IRQ6	(12+16)
+#define	SIU_LEVEL6	(13+16)
+#define	SIU_IRQ7	(14+16)
+#define	SIU_LEVEL7	(15+16)
 
 /* The internal interrupts we can configure as we see fit.
  * My personal preference is CPM at level 2, which puts it above the
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/keyboard.h linux/include/asm-ppc/keyboard.h
--- v2.2.3/linux/include/asm-ppc/keyboard.h	Thu Aug  6 14:06:33 1998
+++ linux/include/asm-ppc/keyboard.h	Fri Mar 19 10:50:09 1999
@@ -46,7 +46,7 @@
 
 static inline int kbd_setkeycode(unsigned int scancode, unsigned int keycode)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		return pckbd_setkeycode(scancode,keycode);
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
@@ -69,7 +69,7 @@
 
 static inline int kbd_getkeycode(unsigned int x)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		return pckbd_getkeycode(x);
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
@@ -86,7 +86,7 @@
 
 static inline int kbd_pretranslate(unsigned char x,char y)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		return pckbd_pretranslate(x,y);
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
@@ -104,7 +104,7 @@
 static inline int kbd_translate(unsigned char keycode, unsigned char *keycodep,
 		     char raw_mode)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		return pckbd_translate(keycode,keycodep,raw_mode);
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
@@ -122,7 +122,7 @@
 
 static inline int kbd_unexpected_up(unsigned char keycode)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		return pckbd_unexpected_up(keycode);
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
@@ -140,7 +140,7 @@
 
 static inline void kbd_leds(unsigned char leds)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		pckbd_leds(leds);
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
@@ -157,7 +157,7 @@
 
 static inline void kbd_init_hw(void)
 {
-	if ( is_prep )
+	if ( is_prep || (_machine == _MACH_mbx) )
 		pckbd_init_hw();
 	else if ( is_chrp )
 #ifndef CONFIG_MAC_KEYBOARD
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/mmu.h linux/include/asm-ppc/mmu.h
--- v2.2.3/linux/include/asm-ppc/mmu.h	Thu Apr 23 20:21:37 1998
+++ linux/include/asm-ppc/mmu.h	Fri Mar 19 10:50:09 1999
@@ -88,25 +88,6 @@
 	P601_BATL batl;		/* Lower register */
 } P601_BAT;
 
-/* Block size masks */
-#define BL_128K	0x000
-#define BL_256K 0x001
-#define BL_512K 0x003
-#define BL_1M   0x007
-#define BL_2M   0x00F
-#define BL_4M   0x01F
-#define BL_8M   0x03F
-#define BL_16M  0x07F
-#define BL_32M  0x0FF
-#define BL_64M  0x1FF
-#define BL_128M 0x3FF
-#define BL_256M 0x7FF
-
-/* BAT Access Protection */
-#define BPP_XX	0x00		/* No access */
-#define BPP_RX	0x01		/* Read only */
-#define BPP_RW	0x02		/* Read/write */
-
 /*
  * Simulated two-level MMU.  This structure is used by the kernel
  * to keep track of MMU mappings and is used to update/maintain
@@ -135,6 +116,35 @@
 	pte	**pmap;		/* Two-level page-map structure */
 } MMU_context;
 
+/* invalidate a TLB entry */
+extern inline void _tlbie(unsigned long va)
+{
+	asm volatile ("tlbie %0" : : "r"(va));
+}
+
+extern void _tlbia(void);		/* invalidate all TLB entries */
+
+#endif /* __ASSEMBLY__ */
+
+/* Block size masks */
+#define BL_128K	0x000
+#define BL_256K 0x001
+#define BL_512K 0x003
+#define BL_1M   0x007
+#define BL_2M   0x00F
+#define BL_4M   0x01F
+#define BL_8M   0x03F
+#define BL_16M  0x07F
+#define BL_32M  0x0FF
+#define BL_64M  0x1FF
+#define BL_128M 0x3FF
+#define BL_256M 0x7FF
+
+/* BAT Access Protection */
+#define BPP_XX	0x00		/* No access */
+#define BPP_RX	0x01		/* Read only */
+#define BPP_RW	0x02		/* Read/write */
+
 /* Used to set up SDR1 register */
 #define HASH_TABLE_SIZE_64K	0x00010000
 #define HASH_TABLE_SIZE_128K	0x00020000
@@ -150,15 +160,6 @@
 #define HASH_TABLE_MASK_1M	0x00F   
 #define HASH_TABLE_MASK_2M	0x01F   
 #define HASH_TABLE_MASK_4M	0x03F   
-
-/* invalidate a TLB entry */
-extern inline void _tlbie(unsigned long va)
-{
-	asm volatile ("tlbie %0" : : "r"(va));
-}
-
-extern void _tlbia(void);		/* invalidate all TLB entries */
-#endif /* __ASSEMBLY__ */
 
 /* Control/status registers for the MPC8xx.
  * A write operation to these registers causes serialized access.
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/ohare.h linux/include/asm-ppc/ohare.h
--- v2.2.3/linux/include/asm-ppc/ohare.h	Thu Apr 23 20:21:37 1998
+++ linux/include/asm-ppc/ohare.h	Wed Mar 10 21:30:32 1999
@@ -22,8 +22,8 @@
 #define OH_SCC_ENABLE		0x200
 #define OH_MESH_ENABLE		0x400
 #define OH_FLOPPY_ENABLE	0x800
-#define OH_SCCA_IO		0x2000
-#define OH_SCCB_IO		0x4000
+#define OH_SCCA_IO		0x4000
+#define OH_SCCB_IO		0x8000
 #define OH_VIA_ENABLE		0x10000
 #define OH_IDECD_POWER		0x800000
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/page.h linux/include/asm-ppc/page.h
--- v2.2.3/linux/include/asm-ppc/page.h	Thu Aug  6 14:06:33 1998
+++ linux/include/asm-ppc/page.h	Wed Mar 10 21:30:32 1999
@@ -63,9 +63,9 @@
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
-
-#define clear_page(page)        memset((void *)(page), 0, PAGE_SIZE)
+extern void clear_page(unsigned long page);
 #define copy_page(to,from)	memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+
 /* map phys->virtual and virtual->phys for RAM pages */
 #ifdef CONFIG_APUS
 #include <asm/amigappc.h>
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/pgtable.h linux/include/asm-ppc/pgtable.h
--- v2.2.3/linux/include/asm-ppc/pgtable.h	Fri Jan  8 22:36:24 1999
+++ linux/include/asm-ppc/pgtable.h	Wed Mar 10 21:30:32 1999
@@ -359,47 +359,46 @@
 	return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
 }
 
-
 /*
  * This is handled very differently on the PPC since out page tables
  * are all 0's and I want to be able to use these zero'd pages elsewhere
  * as well - it gives us quite a speedup.
  *
- * Note that the SMP/UP versions are the same since we don't need a
- * per cpu list of zero pages since we do the zero-ing with the cache
+ * Note that the SMP/UP versions are the same but we don't need a
+ * per cpu list of zero pages because we do the zero-ing with the cache
  * off and the access routines are lock-free but the pgt cache stuff
- * _IS_ per-cpu since it isn't done with any lock-free access routines
+ * is per-cpu since it isn't done with any lock-free access routines
  * (although I think we need arch-specific routines so I can do lock-free).
  *
  * I need to generalize this so we can use it for other arch's as well.
  * -- Cort
  */
+#ifdef __SMP__
+#define quicklists	cpu_data[smp_processor_id()]
+#else
 extern struct pgtable_cache_struct {
 	unsigned long *pgd_cache;
 	unsigned long *pte_cache;
 	unsigned long pgtable_cache_sz;
-	unsigned long *zero_cache;    /* head linked list of pre-zero'd pages */
-  	unsigned long zero_sz;	      /* # currently pre-zero'd pages */
-	unsigned long zeropage_hits;  /* # zero'd pages request that we've done */
-	unsigned long zeropage_calls; /* # zero'd pages request that've been made */
-  	unsigned long zerototal;      /* # pages zero'd over time */
 } quicklists;
+#endif
 
-#ifdef __SMP__
-/*#warning Tell Cort to do the pgt cache for SMP*/
-#define pgd_quicklist (quicklists.pgd_cache)
-#define pmd_quicklist ((unsigned long *)0)
-#define pte_quicklist (quicklists.pte_cache)
-#define pgtable_cache_size (quicklists.pgtable_cache_sz)
-#else /* __SMP__ */
-#define pgd_quicklist (quicklists.pgd_cache)
-#define pmd_quicklist ((unsigned long *)0)
-#define pte_quicklist (quicklists.pte_cache)
-#define pgtable_cache_size (quicklists.pgtable_cache_sz)
-#endif /* __SMP__ */
-
-#define zero_quicklist (quicklists.zero_cache)
-#define zero_cache_sz  (quicklists.zero_sz)
+#define pgd_quicklist 		(quicklists.pgd_cache)
+#define pmd_quicklist 		((unsigned long *)0)
+#define pte_quicklist 		(quicklists.pte_cache)
+#define pgtable_cache_size 	(quicklists.pgtable_cache_sz)
+
+extern unsigned long *zero_cache;    /* head linked list of pre-zero'd pages */
+extern unsigned long zero_sz;	     /* # currently pre-zero'd pages */
+extern unsigned long zeropage_hits;  /* # zero'd pages request that we've done */
+extern unsigned long zeropage_calls; /* # zero'd pages request that've been made */
+extern unsigned long zerototal;      /* # pages zero'd over time */
+
+#define zero_quicklist     	(zero_cache)
+#define zero_cache_sz  	 	(zero_sz)
+#define zero_cache_calls 	(zeropage_calls)
+#define zero_cache_hits  	(zeropage_hits)
+#define zero_cache_total 	(zerototal)
 
 /* return a pre-zero'd page from the list, return NULL if none available -- Cort */
 extern unsigned long get_zero_page_fast(void);
@@ -410,8 +409,8 @@
 
 	if ( (ret = (pgd_t *)get_zero_page_fast()) == NULL )
 	{
-		ret = (pgd_t *)__get_free_page(GFP_KERNEL);
-		memset (ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+		if ( (ret = (pgd_t *)__get_free_page(GFP_KERNEL)) != NULL )
+			memset (ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
 	}
 	if (ret) {
 		init = pgd_offset(&init_mm, 0);
@@ -553,7 +552,7 @@
 	/* To pgd_alloc/pgd_free, one holds master kernel lock and so does our callee, so we can
 	   modify pgd caches of other CPUs as well. -jj */
 	for (i = 0; i < NR_CPUS; i++)
-		for (pgd = (pgd_t *)cpu_data[i].pgd_quick; pgd; pgd = (pgd_t *)*(unsigned long *)pgd)
+		for (pgd = (pgd_t *)cpu_data[i].pgd_cache; pgd; pgd = (pgd_t *)*(unsigned long *)pgd)
 			pgd[address >> PGDIR_SHIFT] = entry;
 #endif
 }
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/pmu.h linux/include/asm-ppc/pmu.h
--- v2.2.3/linux/include/asm-ppc/pmu.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/pmu.h	Wed Mar 10 21:30:32 1999
@@ -9,7 +9,7 @@
 /*
  * PMU commands
  */
-#define PMU_BACKLIGHT_CTRL	0x11	/* control backlight */
+#define PMU_POWER_CTRL		0x11	/* control power of some devices */
 #define PMU_ADB_CMD		0x20	/* send ADB packet */
 #define PMU_ADB_POLL_OFF	0x21	/* disable ADB auto-poll */
 #define PMU_WRITE_NVRAM		0x33	/* write non-volatile RAM */
@@ -29,6 +29,12 @@
 #define PMU_GET_BRIGHTBUTTON	0xd9	/* report brightness up/down pos */
 #define PMU_GET_COVER		0xdc	/* report cover open/closed */
 
+/* Bits to use with the PMU_POWER_CTRL command */
+#define PMU_POW_ON		0x80	/* OR this to power ON the device */
+#define PMU_POW_OFF		0x00	/* leave bit 7 to 0 to power it OFF */
+#define PMU_POW_BACKLIGHT	0x01	/* backlight power */
+#define PMU_POW_IRLED		0x04	/* IR led power (on wallstreet) ??? */
+
 /* Bits in PMU interrupt and interrupt mask bytes */
 #define PMU_INT_ADB_AUTO	0x04	/* ADB autopoll, when PMU_INT_ADB */
 #define PMU_INT_PCEJECT		0x04	/* PC-card eject buttons */
@@ -36,13 +42,26 @@
 #define PMU_INT_ADB		0x10	/* ADB autopoll or reply data */
 #define PMU_INT_TICK		0x80	/* 1-second tick interrupt */
 
+/* Kind of PMU (model) */
+enum {
+  PMU_UNKNOWN,
+  PMU_OHARE_BASED,
+  PMU_HEATHROW_BASED
+};
 
 /*
  * Ioctl commands for the /dev/pmu device
  */
 #include <linux/ioctl.h>
 
+/* no param */
 #define PMU_IOC_SLEEP		_IO('B', 0)
+/* out param: u32*	backlight value: 0 to 31 */
+#define PMU_IOC_GET_BACKLIGHT	_IOR('B', 1, sizeof(__u32*))
+/* in param: u32	backlight value: 0 to 31 */
+#define PMU_IOC_SET_BACKLIGHT	_IOW('B', 2, sizeof(__u32))
+/* out param: u32*	backlight value: 0 to 31 */
+#define PMU_IOC_GET_MODEL	_IOR('B', 3, sizeof(__u32*))
 
 #ifdef __KERNEL__
 void find_via_pmu(void);
@@ -53,9 +72,14 @@
 void pmu_poll(void);
 
 void pmu_enable_backlight(int on);
+void pmu_set_brightness(int level);
+
+void pmu_enable_irled(int on);
 
 void pmu_restart(void);
 void pmu_shutdown(void);
+
+int pmu_get_model(void);
 
 /*
  * Stuff for putting the powerbook to sleep and waking it again.
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/posix_types.h linux/include/asm-ppc/posix_types.h
--- v2.2.3/linux/include/asm-ppc/posix_types.h	Thu Aug  6 14:06:33 1998
+++ linux/include/asm-ppc/posix_types.h	Fri Mar 19 10:50:09 1999
@@ -43,7 +43,7 @@
 
 #else /* __GNUC__ */
 
-#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ <= 2)
 /* With GNU C, use inline functions instead so args are evaluated only once: */
 
 #undef __FD_SET
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- v2.2.3/linux/include/asm-ppc/processor.h	Tue Jan 19 11:32:53 1999
+++ linux/include/asm-ppc/processor.h	Fri Mar 19 10:50:09 1999
@@ -60,21 +60,36 @@
 #define FPSCR_FEX       (1<<30)
 
 #define _MACH_prep     1
-#define _MACH_Pmac     2 /* pmac or pmac clone (non-chrp) */
-#define _MACH_chrp     4 /* chrp machine */
-#define _MACH_mbx      8 /* Motorola MBX board */
-#define _MACH_apus    16 /* amiga with phase5 powerup */
-#define _MACH_fads    32 /* Motorola FADS board */
+#define _MACH_Pmac     2  /* pmac or pmac clone (non-chrp) */
+#define _MACH_chrp     4  /* chrp machine */
+#define _MACH_mbx      8  /* Motorola MBX board */
+#define _MACH_apus    16  /* amiga with phase5 powerup */
+#define _MACH_fads    32  /* Motorola FADS board */
+#define _MACH_rpxlite 64  /* RPCG RPX-Lite 8xx board */
+#define _MACH_bseip   128 /* Bright Star Engineering ip-Engine */
+#define _MACH_yk      256 /* Motorola Yellowknife */
 
 /* see residual.h for these */
 #define _PREP_Motorola 0x01  /* motorola prep */
 #define _PREP_Firm     0x02  /* firmworks prep */
 #define _PREP_IBM      0x00  /* ibm prep */
 #define _PREP_Bull     0x03  /* bull prep */
+#define _PREP_Radstone 0x04  /* Radstone Technology PLC prep */
+
+/*
+ * Radstone board types
+ */
+#define RS_SYS_TYPE_PPC1   0
+#define RS_SYS_TYPE_PPC2   1
+#define RS_SYS_TYPE_PPC1a  2
+#define RS_SYS_TYPE_PPC2a  3
+#define RS_SYS_TYPE_PPC4   4
+#define RS_SYS_TYPE_PPC4a  5
+#define RS_SYS_TYPE_PPC2ep 6
 
 /* these are arbitrary */
 #define _CHRP_Motorola 0x04  /* motorola chrp, the cobra */
-#define _CHRP_IBM     0x05   /* IBM chrp, the longtrail and longtrail 2 */
+#define _CHRP_IBM      0x05  /* IBM chrp, the longtrail and longtrail 2 */
 
 #define _GLOBAL(n)\
 	.globl n;\
@@ -224,6 +239,13 @@
 
 /* what kind of prep workstation we are */
 extern int _prep_type;
+/*
+ * This is used to identify the board type from a given PReP board
+ * vendor. Board revision is also made available.
+ */
+extern unsigned char ucSystemType;
+extern unsigned char ucBoardRev;
+extern unsigned char ucBoardRevMaj, ucBoardRevMin;
 
 struct task_struct;
 void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp);
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/semaphore-helper.h linux/include/asm-ppc/semaphore-helper.h
--- v2.2.3/linux/include/asm-ppc/semaphore-helper.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/semaphore-helper.h	Wed Mar 10 21:30:32 1999
@@ -0,0 +1,107 @@
+#ifndef _PPC_SEMAPHORE_HELPER_H
+#define _PPC_SEMAPHORE_HELPER_H
+
+/*
+ * SMP- and interrupt-safe semaphores..
+ *
+ * (C) Copyright 1996 Linus Torvalds
+ * Adapted for PowerPC by Gary Thomas and Paul Mackerras
+ */
+
+#include <asm/atomic.h>
+
+/*
+ * These two (wake_one_more and waking_non_zero) _must_ execute
+ * atomically wrt each other.
+ *
+ * This is trivially done with load with reservation and
+ * store conditional on the ppc.
+ */
+
+static inline void wake_one_more(struct semaphore * sem)
+{
+	atomic_inc(&sem->waking);
+}
+
+static inline int waking_non_zero(struct semaphore *sem)
+{
+	int ret, tmp;
+
+	/* Atomic decrement sem->waking iff it is > 0 */
+	__asm__ __volatile__(
+		"1:	lwarx %1,0,%2\n"	/* tmp = sem->waking */
+		"	cmpwi 0,%1,0\n"		/* test tmp */
+		"	addic %1,%1,-1\n"	/* --tmp */
+		"	ble- 2f\n"		/* exit if tmp was <= 0 */
+		"	stwcx. %1,0,%2\n"	/* update sem->waking */
+		"	bne- 1b\n"		/* try again if update failed*/
+		"	li %0,1\n"		/* ret = 1 */
+		"2:"
+		: "=r" (ret), "=&r" (tmp)
+		: "r" (&sem->waking), "0" (0)
+		: "cr0", "memory");
+
+	return ret;
+}
+/*
+ * waking_non_zero_interruptible:
+ *	1	got the lock
+ *	0	go to sleep
+ *	-EINTR	interrupted
+ */
+static inline int waking_non_zero_interruptible(struct semaphore *sem,
+						struct task_struct *tsk)
+{
+	int ret, tmp;
+
+	/* Atomic decrement sem->waking iff it is > 0 */
+	__asm__ __volatile__(
+		"1:	lwarx %1,0,%2\n"	/* tmp = sem->waking */
+		"	cmpwi 0,%1,0\n"		/* test tmp */
+		"	addic %1,%1,-1\n"	/* --tmp */
+		"	ble- 2f\n"		/* exit if tmp was <= 0 */
+		"	stwcx. %1,0,%2\n"	/* update sem->waking */
+		"	bne- 1b\n"		/* try again if update failed*/
+		"	li %0,1\n"		/* ret = 1 */
+		"2:"
+		: "=r" (ret), "=&r" (tmp)
+		: "r" (&sem->waking), "0" (0)
+		: "cr0", "memory");
+
+	if (ret == 0 && signal_pending(tsk)) {
+		atomic_inc(&sem->count);
+		ret = -EINTR;
+	}
+	return ret;
+}
+
+/*
+ * waking_non_zero_trylock:
+ *	1	failed to lock
+ *	0	got the lock
+ */
+static inline int waking_non_zero_trylock(struct semaphore *sem)
+{
+	int ret, tmp;
+
+	/* Atomic decrement sem->waking iff it is > 0 */
+	__asm__ __volatile__(
+		"1:	lwarx %1,0,%2\n"	/* tmp = sem->waking */
+		"	cmpwi 0,%1,0\n"		/* test tmp */
+		"	addic %1,%1,-1\n"	/* --tmp */
+		"	ble- 2f\n"		/* exit if tmp was <= 0 */
+		"	stwcx. %1,0,%2\n"	/* update sem->waking */
+		"	bne- 1b\n"		/* try again if update failed*/
+		"	li %0,0\n"		/* ret = 0 */
+		"2:"
+		: "=r" (ret), "=&r" (tmp)
+		: "r" (&sem->waking), "0" (1)
+		: "cr0", "memory");
+
+	if (ret)
+		atomic_inc(&sem->count);
+
+	return ret;
+}
+
+#endif /* _PPC_SEMAPHORE_HELPER_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/semaphore.h linux/include/asm-ppc/semaphore.h
--- v2.2.3/linux/include/asm-ppc/semaphore.h	Tue Jan 19 11:32:53 1999
+++ linux/include/asm-ppc/semaphore.h	Wed Mar 10 21:30:32 1999
@@ -13,66 +13,54 @@
 struct semaphore {
 	atomic_t count;
 	atomic_t waking;
-	struct wait_queue * wait;
+	struct wait_queue *wait;
 };
 
-#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
-#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
+#define sema_init(sem, val)	atomic_set(&((sem)->count), (val))
+
+#define MUTEX		((struct semaphore) \
+			 { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
+#define MUTEX_LOCKED	((struct semaphore) \
+			 { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
+extern int  __down_trylock(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
-#define sema_init(sem, val)	atomic_set(&((sem)->count), (val))
-
-/*
- * These two _must_ execute atomically wrt each other.
- *
- * This is trivially done with load_locked/store_cond,
- * i.e. load with reservation and store conditional on the ppc.
- */
-
-static inline void wake_one_more(struct semaphore * sem)
+extern inline void down(struct semaphore * sem)
 {
-	atomic_inc(&sem->waking);
+	if (atomic_dec_return(&sem->count) >= 0)
+		wmb();
+	else
+		__down(sem);
 }
 
-static inline int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
+extern inline int down_interruptible(struct semaphore * sem)
 {
-	int ret, tmp;
-
-	__asm__ __volatile__(
-		"1:	lwarx %1,0,%2\n"
-		"	cmpwi 0,%1,0\n"
-		"	addic %1,%1,-1\n"
-		"	ble- 2f\n"
-		"	stwcx. %1,0,%2\n"
-		"	bne- 1b\n"
-		"	li %0,1\n"
-		"2:"
-		: "=r" (ret), "=&r" (tmp)
-		: "r" (&sem->waking), "0" (0)
-		: "cr0", "memory");
+	int ret = 0;
 
+	if (atomic_dec_return(&sem->count) >= 0)
+		wmb();
+	else
+		ret = __down_interruptible(sem);
 	return ret;
 }
 
-extern inline void down(struct semaphore * sem)
-{
-	if (atomic_dec_return(&sem->count) < 0)
-		__down(sem);
-}
-
-extern inline int down_interruptible(struct semaphore * sem)
+extern inline int down_trylock(struct semaphore * sem)
 {
 	int ret = 0;
-	if (atomic_dec_return(&sem->count) < 0)
-		ret = __down_interruptible(sem);
+
+	if (atomic_dec_return(&sem->count) >= 0)
+		wmb();
+	else
+		ret = __down_trylock(sem);
 	return ret;
 }
 
 extern inline void up(struct semaphore * sem)
 {
+	mb();
 	if (atomic_inc_return(&sem->count) <= 0)
 		__up(sem);
 }	
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/smp.h linux/include/asm-ppc/smp.h
--- v2.2.3/linux/include/asm-ppc/smp.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/smp.h	Wed Mar 10 21:30:32 1999
@@ -6,13 +6,22 @@
 #ifndef _PPC_SMP_H
 #define _PPC_SMP_H
 
-#include <linux/kernel.h> /* for panic */
-#include <linux/tasks.h> /* for NR_CPUS */
+#include <linux/kernel.h>
+#include <linux/tasks.h>
 
 #ifdef __SMP__
 
 #ifndef __ASSEMBLY__
 
+struct cpuinfo_PPC {
+	unsigned long loops_per_sec;
+	unsigned long pvr;
+	unsigned long *pgd_cache;
+	unsigned long *pte_cache;
+	unsigned long pgtable_cache_sz;
+};
+extern struct cpuinfo_PPC cpu_data[NR_CPUS];
+
 extern int first_cpu_booted;
 extern unsigned long smp_proc_in_lock[NR_CPUS];
 
@@ -20,7 +29,7 @@
 extern void smp_store_cpu_info(int id);
 
 #define NO_PROC_ID		0xFF            /* No processor magic marker */
-#define PROC_CHANGE_PENALTY	2000
+#define PROC_CHANGE_PENALTY	20
 
 /* 1 to 1 mapping on PPC -- Cort */
 #define cpu_logical_map(cpu) (cpu)
@@ -29,17 +38,6 @@
 
 #define hard_smp_processor_id() (0)
 #define smp_processor_id() (current->processor)
-
-/* per processor PPC parameters we need. */
-struct cpuinfo_PPC {
-	unsigned long loops_per_sec;
-	unsigned long pvr;
-	unsigned long *pgd_quick;
-	unsigned long *pte_quick;
-	unsigned long pgtable_cache_sz;
-};
-
-extern struct cpuinfo_PPC cpu_data[NR_CPUS];
 
 struct klock_info_struct {
 	unsigned long kernel_flag;
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/softirq.h linux/include/asm-ppc/softirq.h
--- v2.2.3/linux/include/asm-ppc/softirq.h	Mon Dec 28 15:00:53 1998
+++ linux/include/asm-ppc/softirq.h	Wed Mar 10 21:30:32 1999
@@ -18,8 +18,9 @@
 
 extern inline void remove_bh(int nr)
 {
-	bh_base[nr] = NULL;
 	bh_mask &= ~(1 << nr);
+	wmb();
+	bh_base[nr] = NULL;
 }
 
 extern inline void mark_bh(int nr)
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/spinlock.h linux/include/asm-ppc/spinlock.h
--- v2.2.3/linux/include/asm-ppc/spinlock.h	Thu Dec 31 10:29:02 1998
+++ linux/include/asm-ppc/spinlock.h	Wed Mar 10 21:30:32 1999
@@ -102,6 +102,8 @@
 #define spin_unlock_irqrestore(lock, flags) \
 	do { spin_unlock(lock); __restore_flags(flags); } while (0)
 
+extern unsigned long __spin_trylock(volatile unsigned long *lock);
+
 /*
  * Read-write spinlocks, allowing multiple readers
  * but only one writer.
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.2.3/linux/include/asm-ppc/system.h	Thu Dec 31 10:29:02 1998
+++ linux/include/asm-ppc/system.h	Wed Mar 10 21:30:32 1999
@@ -9,32 +9,33 @@
  * Memory barrier.
  * The sync instruction guarantees that all memory accesses initiated
  * by this processor have been performed (with respect to all other
- * mechanisms that access memory).
+ * mechanisms that access memory).  The eieio instruction is a barrier
+ * providing an ordering (separately) for (a) cacheable stores and (b)
+ * loads and stores to non-cacheable memory (e.g. I/O devices).
+ *
+ * mb() prevents loads and stores being reordered across this point.
+ * rmb() prevents loads being reordered across this point.
+ * wmb() prevents stores being reordered across this point.
+ *
+ * We can use the eieio instruction for wmb, but since it doesn't
+ * give any ordering guarantees about loads, we have to use the
+ * stronger but slower sync instruction for mb and rmb.
  */
 #define mb()  __asm__ __volatile__ ("sync" : : : "memory")
 #define rmb()  __asm__ __volatile__ ("sync" : : : "memory")
-#define wmb()  __asm__ __volatile__ ("sync" : : : "memory")
+#define wmb()  __asm__ __volatile__ ("eieio" : : : "memory")
 
 #define __save_flags(flags)	({\
 	__asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); })
 #define __save_and_cli(flags)	({__save_flags(flags);__cli();})
 
+/* Data cache block flush - write out the cache line containing the
+   specified address and then invalidate it in the cache. */
 extern __inline__ void dcbf(void *line)
 {
-	asm("dcbf %0,%1\n\t"
-	    "sync \n\t"
-	    "isync \n\t"
-	    :: "r" (line), "r" (0));
+	asm("dcbf %0,%1; sync" : : "r" (line), "r" (0));
 }
 
-extern __inline__ void dcbi(void *line)
-{
-	asm("dcbi %0,%1\n\t"
-	    "sync \n\t"
-	    "isync \n\t"
-	    :: "r" (line), "r" (0));
-}
-     
 extern __inline__ void __restore_flags(unsigned long flags)
 {
         extern atomic_t n_lost_interrupts;
@@ -54,6 +55,7 @@
 extern int _disable_interrupts(void);
 extern void _enable_interrupts(int);
 
+extern void instruction_dump(unsigned long *);
 extern void print_backtrace(unsigned long *);
 extern void show_regs(struct pt_regs * regs);
 extern void flush_instruction_cache(void);
@@ -107,8 +109,8 @@
 
 #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
-extern void *xchg_u64(void *ptr, unsigned long val);
-extern void *xchg_u32(void *m, unsigned long val);
+extern unsigned long xchg_u64(void *ptr, unsigned long val);
+extern unsigned long xchg_u32(void *ptr, unsigned long val);
 
 /*
  * This function doesn't exist, so you'll get a linker error
@@ -126,10 +128,10 @@
 static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
 {
 	switch (size) {
-		case 4:
-			return (unsigned long )xchg_u32(ptr, x);
-		case 8:
-			return (unsigned long )xchg_u64(ptr, x);
+	case 4:
+		return (unsigned long )xchg_u32(ptr, x);
+	case 8:
+		return (unsigned long )xchg_u64(ptr, x);
 	}
 	__xchg_called_with_bad_pointer();
 	return x;
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/termbits.h linux/include/asm-ppc/termbits.h
--- v2.2.3/linux/include/asm-ppc/termbits.h	Fri Jan  8 22:36:24 1999
+++ linux/include/asm-ppc/termbits.h	Wed Mar 10 21:30:32 1999
@@ -121,7 +121,7 @@
 #define  B38400	0000017
 #define EXTA B19200
 #define EXTB B38400
-#define CBAUDEX 0000020
+#define CBAUDEX 0000000
 #define  B57600   00020
 #define  B115200  00021
 #define  B230400  00022
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/types.h linux/include/asm-ppc/types.h
--- v2.2.3/linux/include/asm-ppc/types.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/types.h	Wed Mar 10 21:30:32 1999
@@ -6,6 +6,8 @@
  * header files exported to user space
  */
 
+typedef unsigned short umode_t;
+
 typedef __signed__ char __s8;
 typedef unsigned char __u8;
 
@@ -24,8 +26,6 @@
  * These aren't exported outside the kernel to avoid name space clashes
  */
 #ifdef __KERNEL__
-
-typedef unsigned short umode_t;
 
 typedef signed char s8;
 typedef unsigned char u8;
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/uaccess.h linux/include/asm-ppc/uaccess.h
--- v2.2.3/linux/include/asm-ppc/uaccess.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/uaccess.h	Fri Mar 19 10:50:09 1999
@@ -27,7 +27,7 @@
 #define segment_eq(a,b)	((a).seg == (b).seg)
 
 #define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
-#define __user_ok(addr,size) (((size) <= 0x80000000)&&((addr) <= 0x80000000-(size)))
+#define __user_ok(addr,size) (((size) <= TASK_SIZE)&&((addr) <= TASK_SIZE-(size)))
 #define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size)))
 #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
 
@@ -223,6 +223,10 @@
 		return __copy_tofrom_user(to, from, n);
 	return n;
 }
+
+#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; })
+
+#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n)) return retval; })
 
 #define __copy_from_user(to, from, size) \
 	__copy_tofrom_user((to), (from), (size))
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/unistd.h linux/include/asm-ppc/unistd.h
--- v2.2.3/linux/include/asm-ppc/unistd.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-ppc/unistd.h	Wed Mar 10 21:30:32 1999
@@ -193,6 +193,7 @@
 #define __NR_sendfile		186
 #define __NR_getpmsg		187	/* some people actually want streams */
 #define __NR_putpmsg		188	/* some people actually want streams */
+#define __NR_vfork		189
 
 #define __NR(n)	#n
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-ppc/vga.h linux/include/asm-ppc/vga.h
--- v2.2.3/linux/include/asm-ppc/vga.h	Wed Mar 10 15:29:49 1999
+++ linux/include/asm-ppc/vga.h	Fri Mar 19 10:50:09 1999
@@ -8,17 +8,38 @@
 #define _LINUX_ASM_VGA_H_
 
 #include <asm/io.h>
+#include <asm/processor.h>
+
+#include <linux/config.h>
+#include <linux/console.h>
 
 #define VT_BUF_HAVE_RW
 
 extern inline void scr_writew(u16 val, u16 *addr)
 {
-	st_le16(addr, val);
+	/* If using vgacon (not fbcon) byteswap the writes.
+	 * If non-vgacon assume fbcon and don't byteswap
+	 * just like include/linux/vt_buffer.h.
+	 * XXX: this is a performance loss so get rid of it
+	 *      as soon as fbcon works on prep.
+	 * -- Cort
+	 */
+#ifdef CONFIG_FB
+	if ( conswitchp != &vga_con )
+		(*(addr) = (val));
+	else
+#endif /* CONFIG_FB */
+		st_le16(addr, val);
 }
 
 extern inline u16 scr_readw(const u16 *addr)
 {
-	return ld_le16(addr);
+#ifdef CONFIG_FB
+	if ( conswitchp != &vga_con )
+		return (*(addr));
+	else
+#endif /* CONFIG_FB */
+		return ld_le16((unsigned short *)addr);
 }
 
 #define VT_BUF_HAVE_MEMCPYF
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/asm_offsets.h linux/include/asm-sparc/asm_offsets.h
--- v2.2.3/linux/include/asm-sparc/asm_offsets.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc/asm_offsets.h	Wed Mar 10 16:53:37 1999
@@ -2,7 +2,9 @@
 #ifndef __ASM_OFFSETS_H__
 #define __ASM_OFFSETS_H__
 
-#ifndef __SMP__
+#include <linux/config.h>
+
+#ifndef CONFIG_SMP
 
 #define AOFF_task_state	0x00000000
 #define ASIZ_task_state	0x00000004
@@ -20,217 +22,219 @@
 #define ASIZ_task_counter	0x00000004
 #define AOFF_task_priority	0x0000001c
 #define ASIZ_task_priority	0x00000004
-#define AOFF_task_has_cpu	0x00000020
+#define AOFF_task_avg_slice	0x00000020
+#define ASIZ_task_avg_slice	0x00000004
+#define AOFF_task_has_cpu	0x00000024
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000024
+#define AOFF_task_processor	0x00000028
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000028
+#define AOFF_task_last_processor	0x0000002c
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000002c
+#define AOFF_task_lock_depth	0x00000030
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000030
+#define AOFF_task_next_task	0x00000034
 #define ASIZ_task_next_task	0x00000004
-#define AOFF_task_prev_task	0x00000034
+#define AOFF_task_prev_task	0x00000038
 #define ASIZ_task_prev_task	0x00000004
-#define AOFF_task_next_run	0x00000038
+#define AOFF_task_next_run	0x0000003c
 #define ASIZ_task_next_run	0x00000004
-#define AOFF_task_prev_run	0x0000003c
+#define AOFF_task_prev_run	0x00000040
 #define ASIZ_task_prev_run	0x00000004
-#define AOFF_task_binfmt	0x00000040
+#define AOFF_task_binfmt	0x00000044
 #define ASIZ_task_binfmt	0x00000004
-#define AOFF_task_exit_code	0x00000044
+#define AOFF_task_exit_code	0x00000048
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x00000048
+#define AOFF_task_exit_signal	0x0000004c
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x0000004c
+#define AOFF_task_pdeath_signal	0x00000050
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000050
+#define AOFF_task_personality	0x00000054
 #define ASIZ_task_personality	0x00000004
-#define AOFF_task_pid	0x00000058
+#define AOFF_task_pid	0x0000005c
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x0000005c
+#define AOFF_task_pgrp	0x00000060
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x00000060
+#define AOFF_task_tty_old_pgrp	0x00000064
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x00000064
+#define AOFF_task_session	0x00000068
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x00000068
+#define AOFF_task_leader	0x0000006c
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x0000006c
+#define AOFF_task_p_opptr	0x00000070
 #define ASIZ_task_p_opptr	0x00000004
-#define AOFF_task_p_pptr	0x00000070
+#define AOFF_task_p_pptr	0x00000074
 #define ASIZ_task_p_pptr	0x00000004
-#define AOFF_task_p_cptr	0x00000074
+#define AOFF_task_p_cptr	0x00000078
 #define ASIZ_task_p_cptr	0x00000004
-#define AOFF_task_p_ysptr	0x00000078
+#define AOFF_task_p_ysptr	0x0000007c
 #define ASIZ_task_p_ysptr	0x00000004
-#define AOFF_task_p_osptr	0x0000007c
+#define AOFF_task_p_osptr	0x00000080
 #define ASIZ_task_p_osptr	0x00000004
-#define AOFF_task_pidhash_next	0x00000080
+#define AOFF_task_pidhash_next	0x00000084
 #define ASIZ_task_pidhash_next	0x00000004
-#define AOFF_task_pidhash_pprev	0x00000084
+#define AOFF_task_pidhash_pprev	0x00000088
 #define ASIZ_task_pidhash_pprev	0x00000004
-#define AOFF_task_tarray_ptr	0x00000088
+#define AOFF_task_tarray_ptr	0x0000008c
 #define ASIZ_task_tarray_ptr	0x00000004
-#define AOFF_task_wait_chldexit	0x0000008c
+#define AOFF_task_wait_chldexit	0x00000090
 #define ASIZ_task_wait_chldexit	0x00000004
-#define AOFF_task_policy	0x00000090
+#define AOFF_task_vfork_sem	0x00000094
+#define ASIZ_task_vfork_sem	0x00000004
+#define AOFF_task_policy	0x00000098
 #define ASIZ_task_policy	0x00000004
-#define AOFF_task_rt_priority	0x00000094
+#define AOFF_task_rt_priority	0x0000009c
 #define ASIZ_task_rt_priority	0x00000004
-#define AOFF_task_it_real_value	0x00000098
+#define AOFF_task_it_real_value	0x000000a0
 #define ASIZ_task_it_real_value	0x00000004
-#define AOFF_task_it_prof_value	0x0000009c
+#define AOFF_task_it_prof_value	0x000000a4
 #define ASIZ_task_it_prof_value	0x00000004
-#define AOFF_task_it_virt_value	0x000000a0
+#define AOFF_task_it_virt_value	0x000000a8
 #define ASIZ_task_it_virt_value	0x00000004
-#define AOFF_task_it_real_incr	0x000000a4
+#define AOFF_task_it_real_incr	0x000000ac
 #define ASIZ_task_it_real_incr	0x00000004
-#define AOFF_task_it_prof_incr	0x000000a8
+#define AOFF_task_it_prof_incr	0x000000b0
 #define ASIZ_task_it_prof_incr	0x00000004
-#define AOFF_task_it_virt_incr	0x000000ac
+#define AOFF_task_it_virt_incr	0x000000b4
 #define ASIZ_task_it_virt_incr	0x00000004
-#define AOFF_task_real_timer	0x000000b0
+#define AOFF_task_real_timer	0x000000b8
 #define ASIZ_task_real_timer	0x00000014
-#define AOFF_task_times	0x000000c4
+#define AOFF_task_times	0x000000cc
 #define ASIZ_task_times	0x00000010
-#define AOFF_task_start_time	0x000000d4
+#define AOFF_task_start_time	0x000000dc
 #define ASIZ_task_start_time	0x00000004
-#define AOFF_task_per_cpu_utime	0x000000d8
+#define AOFF_task_per_cpu_utime	0x000000e0
 #define ASIZ_task_per_cpu_utime	0x00000004
-#define AOFF_task_min_flt	0x000000e0
+#define AOFF_task_min_flt	0x000000e8
 #define ASIZ_task_min_flt	0x00000004
-#define AOFF_task_maj_flt	0x000000e4
+#define AOFF_task_maj_flt	0x000000ec
 #define ASIZ_task_maj_flt	0x00000004
-#define AOFF_task_nswap	0x000000e8
+#define AOFF_task_nswap	0x000000f0
 #define ASIZ_task_nswap	0x00000004
-#define AOFF_task_cmin_flt	0x000000ec
+#define AOFF_task_cmin_flt	0x000000f4
 #define ASIZ_task_cmin_flt	0x00000004
-#define AOFF_task_cmaj_flt	0x000000f0
+#define AOFF_task_cmaj_flt	0x000000f8
 #define ASIZ_task_cmaj_flt	0x00000004
-#define AOFF_task_cnswap	0x000000f4
+#define AOFF_task_cnswap	0x000000fc
 #define ASIZ_task_cnswap	0x00000004
-#define AOFF_task_swap_address	0x000000fc
-#define ASIZ_task_swap_address	0x00000004
-#define AOFF_task_old_maj_flt	0x00000100
-#define ASIZ_task_old_maj_flt	0x00000004
-#define AOFF_task_dec_flt	0x00000104
-#define ASIZ_task_dec_flt	0x00000004
-#define AOFF_task_swap_cnt	0x00000108
-#define ASIZ_task_swap_cnt	0x00000004
-#define AOFF_task_uid	0x0000010c
+#define AOFF_task_uid	0x00000102
 #define ASIZ_task_uid	0x00000002
-#define AOFF_task_euid	0x0000010e
+#define AOFF_task_euid	0x00000104
 #define ASIZ_task_euid	0x00000002
-#define AOFF_task_suid	0x00000110
+#define AOFF_task_suid	0x00000106
 #define ASIZ_task_suid	0x00000002
-#define AOFF_task_fsuid	0x00000112
+#define AOFF_task_fsuid	0x00000108
 #define ASIZ_task_fsuid	0x00000002
-#define AOFF_task_gid	0x00000114
+#define AOFF_task_gid	0x0000010a
 #define ASIZ_task_gid	0x00000002
-#define AOFF_task_egid	0x00000116
+#define AOFF_task_egid	0x0000010c
 #define ASIZ_task_egid	0x00000002
-#define AOFF_task_sgid	0x00000118
+#define AOFF_task_sgid	0x0000010e
 #define ASIZ_task_sgid	0x00000002
-#define AOFF_task_fsgid	0x0000011a
+#define AOFF_task_fsgid	0x00000110
 #define ASIZ_task_fsgid	0x00000002
-#define AOFF_task_ngroups	0x0000011c
+#define AOFF_task_ngroups	0x00000114
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x00000120
+#define AOFF_task_groups	0x00000118
 #define ASIZ_task_groups	0x00000040
-#define AOFF_task_cap_effective	0x00000160
+#define AOFF_task_cap_effective	0x00000158
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x00000164
+#define AOFF_task_cap_inheritable	0x0000015c
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000168
+#define AOFF_task_cap_permitted	0x00000160
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x0000016c
+#define AOFF_task_user	0x00000164
 #define ASIZ_task_user	0x00000004
-#define AOFF_task_rlim	0x00000170
+#define AOFF_task_rlim	0x00000168
 #define ASIZ_task_rlim	0x00000050
-#define AOFF_task_used_math	0x000001c0
+#define AOFF_task_used_math	0x000001b8
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x000001c2
+#define AOFF_task_comm	0x000001ba
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x000001d4
+#define AOFF_task_link_count	0x000001cc
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x000001d8
+#define AOFF_task_tty	0x000001d0
 #define ASIZ_task_tty	0x00000004
-#define AOFF_task_semundo	0x000001dc
+#define AOFF_task_semundo	0x000001d4
 #define ASIZ_task_semundo	0x00000004
-#define AOFF_task_semsleeping	0x000001e0
+#define AOFF_task_semsleeping	0x000001d8
 #define ASIZ_task_semsleeping	0x00000004
-#define AOFF_task_tss	0x000001e8
+#define AOFF_task_tss	0x000001e0
 #define ASIZ_task_tss	0x00000388
-#define AOFF_task_fs	0x00000570
+#define AOFF_task_fs	0x00000568
 #define ASIZ_task_fs	0x00000004
-#define AOFF_task_files	0x00000574
+#define AOFF_task_files	0x0000056c
 #define ASIZ_task_files	0x00000004
-#define AOFF_task_mm	0x00000578
+#define AOFF_task_mm	0x00000570
 #define ASIZ_task_mm	0x00000004
-#define AOFF_task_sigmask_lock	0x0000057c
+#define AOFF_task_sigmask_lock	0x00000574
 #define ASIZ_task_sigmask_lock	0x00000001
-#define AOFF_task_sig	0x00000580
+#define AOFF_task_sig	0x00000578
 #define ASIZ_task_sig	0x00000004
-#define AOFF_task_signal	0x00000584
+#define AOFF_task_signal	0x0000057c
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x0000058c
+#define AOFF_task_blocked	0x00000584
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000594
+#define AOFF_task_sigqueue	0x0000058c
 #define ASIZ_task_sigqueue	0x00000004
-#define AOFF_task_sigqueue_tail	0x00000598
+#define AOFF_task_sigqueue_tail	0x00000590
 #define ASIZ_task_sigqueue_tail	0x00000004
-#define AOFF_task_sas_ss_sp	0x0000059c
+#define AOFF_task_sas_ss_sp	0x00000594
 #define ASIZ_task_sas_ss_sp	0x00000004
-#define AOFF_task_sas_ss_size	0x000005a0
+#define AOFF_task_sas_ss_size	0x00000598
 #define ASIZ_task_sas_ss_size	0x00000004
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
-#define AOFF_mm_mmap_cache	0x00000004
+#define AOFF_mm_mmap_avl	0x00000004
+#define ASIZ_mm_mmap_avl	0x00000004
+#define AOFF_mm_mmap_cache	0x00000008
 #define ASIZ_mm_mmap_cache	0x00000004
-#define AOFF_mm_pgd	0x00000008
+#define AOFF_mm_pgd	0x0000000c
 #define ASIZ_mm_pgd	0x00000004
-#define AOFF_mm_count	0x0000000c
+#define AOFF_mm_count	0x00000010
 #define ASIZ_mm_count	0x00000004
-#define AOFF_mm_map_count	0x00000010
+#define AOFF_mm_map_count	0x00000014
 #define ASIZ_mm_map_count	0x00000004
-#define AOFF_mm_mmap_sem	0x00000014
+#define AOFF_mm_mmap_sem	0x00000018
 #define ASIZ_mm_mmap_sem	0x0000000c
-#define AOFF_mm_context	0x00000020
+#define AOFF_mm_context	0x00000024
 #define ASIZ_mm_context	0x00000004
-#define AOFF_mm_start_code	0x00000024
+#define AOFF_mm_start_code	0x00000028
 #define ASIZ_mm_start_code	0x00000004
-#define AOFF_mm_end_code	0x00000028
+#define AOFF_mm_end_code	0x0000002c
 #define ASIZ_mm_end_code	0x00000004
-#define AOFF_mm_start_data	0x0000002c
+#define AOFF_mm_start_data	0x00000030
 #define ASIZ_mm_start_data	0x00000004
-#define AOFF_mm_end_data	0x00000030
+#define AOFF_mm_end_data	0x00000034
 #define ASIZ_mm_end_data	0x00000004
-#define AOFF_mm_start_brk	0x00000034
+#define AOFF_mm_start_brk	0x00000038
 #define ASIZ_mm_start_brk	0x00000004
-#define AOFF_mm_brk	0x00000038
+#define AOFF_mm_brk	0x0000003c
 #define ASIZ_mm_brk	0x00000004
-#define AOFF_mm_start_stack	0x0000003c
+#define AOFF_mm_start_stack	0x00000040
 #define ASIZ_mm_start_stack	0x00000004
-#define AOFF_mm_arg_start	0x00000040
+#define AOFF_mm_arg_start	0x00000044
 #define ASIZ_mm_arg_start	0x00000004
-#define AOFF_mm_arg_end	0x00000044
+#define AOFF_mm_arg_end	0x00000048
 #define ASIZ_mm_arg_end	0x00000004
-#define AOFF_mm_env_start	0x00000048
+#define AOFF_mm_env_start	0x0000004c
 #define ASIZ_mm_env_start	0x00000004
-#define AOFF_mm_env_end	0x0000004c
+#define AOFF_mm_env_end	0x00000050
 #define ASIZ_mm_env_end	0x00000004
-#define AOFF_mm_rss	0x00000050
+#define AOFF_mm_rss	0x00000054
 #define ASIZ_mm_rss	0x00000004
-#define AOFF_mm_total_vm	0x00000054
+#define AOFF_mm_total_vm	0x00000058
 #define ASIZ_mm_total_vm	0x00000004
-#define AOFF_mm_locked_vm	0x00000058
+#define AOFF_mm_locked_vm	0x0000005c
 #define ASIZ_mm_locked_vm	0x00000004
-#define AOFF_mm_def_flags	0x0000005c
+#define AOFF_mm_def_flags	0x00000060
 #define ASIZ_mm_def_flags	0x00000004
-#define AOFF_mm_cpu_vm_mask	0x00000060
+#define AOFF_mm_cpu_vm_mask	0x00000064
 #define ASIZ_mm_cpu_vm_mask	0x00000004
-#define AOFF_mm_segments	0x00000064
+#define AOFF_mm_swap_cnt	0x00000068
+#define ASIZ_mm_swap_cnt	0x00000004
+#define AOFF_mm_swap_address	0x0000006c
+#define ASIZ_mm_swap_address	0x00000004
+#define AOFF_mm_segments	0x00000070
 #define ASIZ_mm_segments	0x00000004
 #define AOFF_thread_uwinmask	0x00000000
 #define ASIZ_thread_uwinmask	0x00000004
@@ -275,7 +279,7 @@
 #define AOFF_thread_new_signal	0x00000380
 #define ASIZ_thread_new_signal	0x00000004
 
-#else /* __SMP__ */
+#else /* CONFIG_SMP */
 
 #define AOFF_task_state	0x00000000
 #define ASIZ_task_state	0x00000004
@@ -293,217 +297,219 @@
 #define ASIZ_task_counter	0x00000004
 #define AOFF_task_priority	0x0000001c
 #define ASIZ_task_priority	0x00000004
-#define AOFF_task_has_cpu	0x00000020
+#define AOFF_task_avg_slice	0x00000020
+#define ASIZ_task_avg_slice	0x00000004
+#define AOFF_task_has_cpu	0x00000024
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000024
+#define AOFF_task_processor	0x00000028
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000028
+#define AOFF_task_last_processor	0x0000002c
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000002c
+#define AOFF_task_lock_depth	0x00000030
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000030
+#define AOFF_task_next_task	0x00000034
 #define ASIZ_task_next_task	0x00000004
-#define AOFF_task_prev_task	0x00000034
+#define AOFF_task_prev_task	0x00000038
 #define ASIZ_task_prev_task	0x00000004
-#define AOFF_task_next_run	0x00000038
+#define AOFF_task_next_run	0x0000003c
 #define ASIZ_task_next_run	0x00000004
-#define AOFF_task_prev_run	0x0000003c
+#define AOFF_task_prev_run	0x00000040
 #define ASIZ_task_prev_run	0x00000004
-#define AOFF_task_binfmt	0x00000040
+#define AOFF_task_binfmt	0x00000044
 #define ASIZ_task_binfmt	0x00000004
-#define AOFF_task_exit_code	0x00000044
+#define AOFF_task_exit_code	0x00000048
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x00000048
+#define AOFF_task_exit_signal	0x0000004c
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x0000004c
+#define AOFF_task_pdeath_signal	0x00000050
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000050
+#define AOFF_task_personality	0x00000054
 #define ASIZ_task_personality	0x00000004
-#define AOFF_task_pid	0x00000058
+#define AOFF_task_pid	0x0000005c
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x0000005c
+#define AOFF_task_pgrp	0x00000060
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x00000060
+#define AOFF_task_tty_old_pgrp	0x00000064
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x00000064
+#define AOFF_task_session	0x00000068
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x00000068
+#define AOFF_task_leader	0x0000006c
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x0000006c
+#define AOFF_task_p_opptr	0x00000070
 #define ASIZ_task_p_opptr	0x00000004
-#define AOFF_task_p_pptr	0x00000070
+#define AOFF_task_p_pptr	0x00000074
 #define ASIZ_task_p_pptr	0x00000004
-#define AOFF_task_p_cptr	0x00000074
+#define AOFF_task_p_cptr	0x00000078
 #define ASIZ_task_p_cptr	0x00000004
-#define AOFF_task_p_ysptr	0x00000078
+#define AOFF_task_p_ysptr	0x0000007c
 #define ASIZ_task_p_ysptr	0x00000004
-#define AOFF_task_p_osptr	0x0000007c
+#define AOFF_task_p_osptr	0x00000080
 #define ASIZ_task_p_osptr	0x00000004
-#define AOFF_task_pidhash_next	0x00000080
+#define AOFF_task_pidhash_next	0x00000084
 #define ASIZ_task_pidhash_next	0x00000004
-#define AOFF_task_pidhash_pprev	0x00000084
+#define AOFF_task_pidhash_pprev	0x00000088
 #define ASIZ_task_pidhash_pprev	0x00000004
-#define AOFF_task_tarray_ptr	0x00000088
+#define AOFF_task_tarray_ptr	0x0000008c
 #define ASIZ_task_tarray_ptr	0x00000004
-#define AOFF_task_wait_chldexit	0x0000008c
+#define AOFF_task_wait_chldexit	0x00000090
 #define ASIZ_task_wait_chldexit	0x00000004
-#define AOFF_task_policy	0x00000090
+#define AOFF_task_vfork_sem	0x00000094
+#define ASIZ_task_vfork_sem	0x00000004
+#define AOFF_task_policy	0x00000098
 #define ASIZ_task_policy	0x00000004
-#define AOFF_task_rt_priority	0x00000094
+#define AOFF_task_rt_priority	0x0000009c
 #define ASIZ_task_rt_priority	0x00000004
-#define AOFF_task_it_real_value	0x00000098
+#define AOFF_task_it_real_value	0x000000a0
 #define ASIZ_task_it_real_value	0x00000004
-#define AOFF_task_it_prof_value	0x0000009c
+#define AOFF_task_it_prof_value	0x000000a4
 #define ASIZ_task_it_prof_value	0x00000004
-#define AOFF_task_it_virt_value	0x000000a0
+#define AOFF_task_it_virt_value	0x000000a8
 #define ASIZ_task_it_virt_value	0x00000004
-#define AOFF_task_it_real_incr	0x000000a4
+#define AOFF_task_it_real_incr	0x000000ac
 #define ASIZ_task_it_real_incr	0x00000004
-#define AOFF_task_it_prof_incr	0x000000a8
+#define AOFF_task_it_prof_incr	0x000000b0
 #define ASIZ_task_it_prof_incr	0x00000004
-#define AOFF_task_it_virt_incr	0x000000ac
+#define AOFF_task_it_virt_incr	0x000000b4
 #define ASIZ_task_it_virt_incr	0x00000004
-#define AOFF_task_real_timer	0x000000b0
+#define AOFF_task_real_timer	0x000000b8
 #define ASIZ_task_real_timer	0x00000014
-#define AOFF_task_times	0x000000c4
+#define AOFF_task_times	0x000000cc
 #define ASIZ_task_times	0x00000010
-#define AOFF_task_start_time	0x000000d4
+#define AOFF_task_start_time	0x000000dc
 #define ASIZ_task_start_time	0x00000004
-#define AOFF_task_per_cpu_utime	0x000000d8
+#define AOFF_task_per_cpu_utime	0x000000e0
 #define ASIZ_task_per_cpu_utime	0x00000080
-#define AOFF_task_min_flt	0x000001d8
+#define AOFF_task_min_flt	0x000001e0
 #define ASIZ_task_min_flt	0x00000004
-#define AOFF_task_maj_flt	0x000001dc
+#define AOFF_task_maj_flt	0x000001e4
 #define ASIZ_task_maj_flt	0x00000004
-#define AOFF_task_nswap	0x000001e0
+#define AOFF_task_nswap	0x000001e8
 #define ASIZ_task_nswap	0x00000004
-#define AOFF_task_cmin_flt	0x000001e4
+#define AOFF_task_cmin_flt	0x000001ec
 #define ASIZ_task_cmin_flt	0x00000004
-#define AOFF_task_cmaj_flt	0x000001e8
+#define AOFF_task_cmaj_flt	0x000001f0
 #define ASIZ_task_cmaj_flt	0x00000004
-#define AOFF_task_cnswap	0x000001ec
+#define AOFF_task_cnswap	0x000001f4
 #define ASIZ_task_cnswap	0x00000004
-#define AOFF_task_swap_address	0x000001f4
-#define ASIZ_task_swap_address	0x00000004
-#define AOFF_task_old_maj_flt	0x000001f8
-#define ASIZ_task_old_maj_flt	0x00000004
-#define AOFF_task_dec_flt	0x000001fc
-#define ASIZ_task_dec_flt	0x00000004
-#define AOFF_task_swap_cnt	0x00000200
-#define ASIZ_task_swap_cnt	0x00000004
-#define AOFF_task_uid	0x00000204
+#define AOFF_task_uid	0x000001fa
 #define ASIZ_task_uid	0x00000002
-#define AOFF_task_euid	0x00000206
+#define AOFF_task_euid	0x000001fc
 #define ASIZ_task_euid	0x00000002
-#define AOFF_task_suid	0x00000208
+#define AOFF_task_suid	0x000001fe
 #define ASIZ_task_suid	0x00000002
-#define AOFF_task_fsuid	0x0000020a
+#define AOFF_task_fsuid	0x00000200
 #define ASIZ_task_fsuid	0x00000002
-#define AOFF_task_gid	0x0000020c
+#define AOFF_task_gid	0x00000202
 #define ASIZ_task_gid	0x00000002
-#define AOFF_task_egid	0x0000020e
+#define AOFF_task_egid	0x00000204
 #define ASIZ_task_egid	0x00000002
-#define AOFF_task_sgid	0x00000210
+#define AOFF_task_sgid	0x00000206
 #define ASIZ_task_sgid	0x00000002
-#define AOFF_task_fsgid	0x00000212
+#define AOFF_task_fsgid	0x00000208
 #define ASIZ_task_fsgid	0x00000002
-#define AOFF_task_ngroups	0x00000214
+#define AOFF_task_ngroups	0x0000020c
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x00000218
+#define AOFF_task_groups	0x00000210
 #define ASIZ_task_groups	0x00000040
-#define AOFF_task_cap_effective	0x00000258
+#define AOFF_task_cap_effective	0x00000250
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x0000025c
+#define AOFF_task_cap_inheritable	0x00000254
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000260
+#define AOFF_task_cap_permitted	0x00000258
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x00000264
+#define AOFF_task_user	0x0000025c
 #define ASIZ_task_user	0x00000004
-#define AOFF_task_rlim	0x00000268
+#define AOFF_task_rlim	0x00000260
 #define ASIZ_task_rlim	0x00000050
-#define AOFF_task_used_math	0x000002b8
+#define AOFF_task_used_math	0x000002b0
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x000002ba
+#define AOFF_task_comm	0x000002b2
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x000002cc
+#define AOFF_task_link_count	0x000002c4
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x000002d0
+#define AOFF_task_tty	0x000002c8
 #define ASIZ_task_tty	0x00000004
-#define AOFF_task_semundo	0x000002d4
+#define AOFF_task_semundo	0x000002cc
 #define ASIZ_task_semundo	0x00000004
-#define AOFF_task_semsleeping	0x000002d8
+#define AOFF_task_semsleeping	0x000002d0
 #define ASIZ_task_semsleeping	0x00000004
-#define AOFF_task_tss	0x000002e0
+#define AOFF_task_tss	0x000002d8
 #define ASIZ_task_tss	0x00000388
-#define AOFF_task_fs	0x00000668
+#define AOFF_task_fs	0x00000660
 #define ASIZ_task_fs	0x00000004
-#define AOFF_task_files	0x0000066c
+#define AOFF_task_files	0x00000664
 #define ASIZ_task_files	0x00000004
-#define AOFF_task_mm	0x00000670
+#define AOFF_task_mm	0x00000668
 #define ASIZ_task_mm	0x00000004
-#define AOFF_task_sigmask_lock	0x00000674
+#define AOFF_task_sigmask_lock	0x0000066c
 #define ASIZ_task_sigmask_lock	0x00000008
-#define AOFF_task_sig	0x0000067c
+#define AOFF_task_sig	0x00000674
 #define ASIZ_task_sig	0x00000004
-#define AOFF_task_signal	0x00000680
+#define AOFF_task_signal	0x00000678
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000688
+#define AOFF_task_blocked	0x00000680
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000690
+#define AOFF_task_sigqueue	0x00000688
 #define ASIZ_task_sigqueue	0x00000004
-#define AOFF_task_sigqueue_tail	0x00000694
+#define AOFF_task_sigqueue_tail	0x0000068c
 #define ASIZ_task_sigqueue_tail	0x00000004
-#define AOFF_task_sas_ss_sp	0x00000698
+#define AOFF_task_sas_ss_sp	0x00000690
 #define ASIZ_task_sas_ss_sp	0x00000004
-#define AOFF_task_sas_ss_size	0x0000069c
+#define AOFF_task_sas_ss_size	0x00000694
 #define ASIZ_task_sas_ss_size	0x00000004
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000004
-#define AOFF_mm_mmap_cache	0x00000004
+#define AOFF_mm_mmap_avl	0x00000004
+#define ASIZ_mm_mmap_avl	0x00000004
+#define AOFF_mm_mmap_cache	0x00000008
 #define ASIZ_mm_mmap_cache	0x00000004
-#define AOFF_mm_pgd	0x00000008
+#define AOFF_mm_pgd	0x0000000c
 #define ASIZ_mm_pgd	0x00000004
-#define AOFF_mm_count	0x0000000c
+#define AOFF_mm_count	0x00000010
 #define ASIZ_mm_count	0x00000004
-#define AOFF_mm_map_count	0x00000010
+#define AOFF_mm_map_count	0x00000014
 #define ASIZ_mm_map_count	0x00000004
-#define AOFF_mm_mmap_sem	0x00000014
+#define AOFF_mm_mmap_sem	0x00000018
 #define ASIZ_mm_mmap_sem	0x0000000c
-#define AOFF_mm_context	0x00000020
+#define AOFF_mm_context	0x00000024
 #define ASIZ_mm_context	0x00000004
-#define AOFF_mm_start_code	0x00000024
+#define AOFF_mm_start_code	0x00000028
 #define ASIZ_mm_start_code	0x00000004
-#define AOFF_mm_end_code	0x00000028
+#define AOFF_mm_end_code	0x0000002c
 #define ASIZ_mm_end_code	0x00000004
-#define AOFF_mm_start_data	0x0000002c
+#define AOFF_mm_start_data	0x00000030
 #define ASIZ_mm_start_data	0x00000004
-#define AOFF_mm_end_data	0x00000030
+#define AOFF_mm_end_data	0x00000034
 #define ASIZ_mm_end_data	0x00000004
-#define AOFF_mm_start_brk	0x00000034
+#define AOFF_mm_start_brk	0x00000038
 #define ASIZ_mm_start_brk	0x00000004
-#define AOFF_mm_brk	0x00000038
+#define AOFF_mm_brk	0x0000003c
 #define ASIZ_mm_brk	0x00000004
-#define AOFF_mm_start_stack	0x0000003c
+#define AOFF_mm_start_stack	0x00000040
 #define ASIZ_mm_start_stack	0x00000004
-#define AOFF_mm_arg_start	0x00000040
+#define AOFF_mm_arg_start	0x00000044
 #define ASIZ_mm_arg_start	0x00000004
-#define AOFF_mm_arg_end	0x00000044
+#define AOFF_mm_arg_end	0x00000048
 #define ASIZ_mm_arg_end	0x00000004
-#define AOFF_mm_env_start	0x00000048
+#define AOFF_mm_env_start	0x0000004c
 #define ASIZ_mm_env_start	0x00000004
-#define AOFF_mm_env_end	0x0000004c
+#define AOFF_mm_env_end	0x00000050
 #define ASIZ_mm_env_end	0x00000004
-#define AOFF_mm_rss	0x00000050
+#define AOFF_mm_rss	0x00000054
 #define ASIZ_mm_rss	0x00000004
-#define AOFF_mm_total_vm	0x00000054
+#define AOFF_mm_total_vm	0x00000058
 #define ASIZ_mm_total_vm	0x00000004
-#define AOFF_mm_locked_vm	0x00000058
+#define AOFF_mm_locked_vm	0x0000005c
 #define ASIZ_mm_locked_vm	0x00000004
-#define AOFF_mm_def_flags	0x0000005c
+#define AOFF_mm_def_flags	0x00000060
 #define ASIZ_mm_def_flags	0x00000004
-#define AOFF_mm_cpu_vm_mask	0x00000060
+#define AOFF_mm_cpu_vm_mask	0x00000064
 #define ASIZ_mm_cpu_vm_mask	0x00000004
-#define AOFF_mm_segments	0x00000064
+#define AOFF_mm_swap_cnt	0x00000068
+#define ASIZ_mm_swap_cnt	0x00000004
+#define AOFF_mm_swap_address	0x0000006c
+#define ASIZ_mm_swap_address	0x00000004
+#define AOFF_mm_segments	0x00000070
 #define ASIZ_mm_segments	0x00000004
 #define AOFF_thread_uwinmask	0x00000000
 #define ASIZ_thread_uwinmask	0x00000004
@@ -548,6 +554,6 @@
 #define AOFF_thread_new_signal	0x00000380
 #define ASIZ_thread_new_signal	0x00000004
 
-#endif /* __SMP__ */
+#endif /* CONFIG_SMP */
 
 #endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/atomic.h linux/include/asm-sparc/atomic.h
--- v2.2.3/linux/include/asm-sparc/atomic.h	Wed Apr 23 19:01:28 1997
+++ linux/include/asm-sparc/atomic.h	Wed Mar 10 16:53:37 1999
@@ -9,16 +9,22 @@
 #ifdef __SMP__
 /* This is a temporary measure. -DaveM */
 typedef struct { volatile int counter; } atomic_t;
+#define ATOMIC_INIT(i)	{ (i << 8) }
 #else
 typedef struct { int counter; } atomic_t;
+#define ATOMIC_INIT(i)  { (i) }
 #endif
 
-#define ATOMIC_INIT(i)	{ (i << 8) }
-
 #ifdef __KERNEL__
 #include <asm/system.h>
 #include <asm/psr.h>
 
+#ifndef __SMP__
+
+#define atomic_read(v)          ((v)->counter)
+#define atomic_set(v, i)        (((v)->counter) = i)
+
+#else
 /* We do the bulk of the actual work out of line in two common
  * routines in assembler, see arch/sparc/lib/atomic.S for the
  * "fun" details.
@@ -43,6 +49,7 @@
 	return val;
 }
 #define atomic_set(v, i)	(((v)->counter) = ((i) << 8))
+#endif
 
 /* Make sure gcc doesn't try to be clever and move things around
  * on us. We need to use _exactly_ the address the user gave us,
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/cache.h linux/include/asm-sparc/cache.h
--- v2.2.3/linux/include/asm-sparc/cache.h	Thu May 15 16:48:04 1997
+++ linux/include/asm-sparc/cache.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: cache.h,v 1.7 1997/05/06 09:31:46 davem Exp $
+/* $Id: cache.h,v 1.8 1999/03/11 00:14:45 davem Exp $
  * cache.h:  Cache specific code for the Sparc.  These include flushing
  *           and direct tag/data line access.
  *
@@ -12,6 +12,8 @@
 
 #define L1_CACHE_BYTES 32
 #define L1_CACHE_ALIGN(x) ((((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)))
+
+#define SMP_CACHE_BYTES 32
 
 /* Direct access to the instruction cache is provided through and
  * alternate address space.  The IDC bit must be off in the ICCR on
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/checksum.h linux/include/asm-sparc/checksum.h
--- v2.2.3/linux/include/asm-sparc/checksum.h	Sat May  2 14:19:54 1998
+++ linux/include/asm-sparc/checksum.h	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: checksum.h,v 1.28 1998/04/17 02:37:25 davem Exp $ */
+/* $Id: checksum.h,v 1.29 1999/03/21 05:22:07 davem Exp $ */
 #ifndef __SPARC_CHECKSUM_H
 #define __SPARC_CHECKSUM_H
 
@@ -117,7 +117,10 @@
 		return ret;
 	}
 }
-  
+
+#define HAVE_CSUM_COPY_USER
+#define csum_and_copy_to_user csum_partial_copy_to_user
+
 /* ihl is always 5 or greater, almost always is 5, and iph is word aligned
  * the majority of the time.
  */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/dma.h linux/include/asm-sparc/dma.h
--- v2.2.3/linux/include/asm-sparc/dma.h	Sun Nov  8 14:03:08 1998
+++ linux/include/asm-sparc/dma.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.28 1998/10/26 20:03:09 davem Exp $
+/* $Id: dma.h,v 1.29 1998/11/29 15:49:44 davem Exp $
  * include/asm-sparc/dma.h
  *
  * Copyright 1995 (C) David S. Miller (davem@caip.rutgers.edu)
@@ -127,6 +127,7 @@
 #define DMA_DSBL_RD_DRN  0x00001000        /* No EC drain on slave reads */
 #define DMA_BCNT_ENAB    0x00002000        /* If on, use the byte counter */
 #define DMA_TERM_CNTR    0x00004000        /* Terminal counter */
+#define DMA_SCSI_SBUS64  0x00008000        /* HME: Enable 64-bit SBUS mode. */
 #define DMA_CSR_DISAB    0x00010000        /* No FIFO drains during csr */
 #define DMA_SCSI_DISAB   0x00020000        /* No FIFO drains during reg */
 #define DMA_DSBL_WR_INV  0x00020000        /* No EC inval. on slave writes */
@@ -147,6 +148,7 @@
 #define DMA_PARITY_OFF   0x02000000        /* HME: disable parity checking */
 #define DMA_LOADED_ADDR  0x04000000        /* Address has been loaded */
 #define DMA_LOADED_NADDR 0x08000000        /* Next address has been loaded */
+#define DMA_RESET_FAS366 0x08000000        /* HME: Assert RESET to FAS366 */
 
 /* Values describing the burst-size property from the PROM */
 #define DMA_BURST1       0x01
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/ethtool.h linux/include/asm-sparc/ethtool.h
--- v2.2.3/linux/include/asm-sparc/ethtool.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/ethtool.h	Wed Mar 10 16:53:37 1999
@@ -0,0 +1,79 @@
+/* $Id: ethtool.h,v 1.1 1998/12/19 15:09:38 davem Exp $
+ * ethtool.h: Defines for SparcLinux ethtool.
+ *
+ * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ */
+
+#ifndef _SPARC_ETHTOOL_H
+#define _SPARC_ETHTOOL_H
+
+/* We do things like this so it does not matter what kernel
+ * headers you have on your system etc.
+ */
+#undef SIOCETHTOOL
+#define SIOCETHTOOL	(SIOCDEVPRIVATE + 0x0f)
+
+/* This should work for both 32 and 64 bit userland. */
+struct ethtool_cmd {
+	u32	cmd;
+	u32	supported;
+	u16	speed;
+	u8	duplex;
+	u8	port;
+	u8	phy_address;
+	u8	transceiver;
+	u8	autoneg;
+};
+
+/* CMDs currently supported */
+#define SPARC_ETH_GSET		0x00000001	/* Get settings, non-privileged. */
+#define SPARC_ETH_SSET		0x00000002	/* Set settings, privileged. */
+
+/* Indicates what features are supported by the interface. */
+#define SUPPORTED_10baseT_Half		0x00000001
+#define SUPPORTED_10baseT_Full		0x00000002
+#define SUPPORTED_100baseT_Half		0x00000004
+#define SUPPORTED_100baseT_Full		0x00000008
+#define SUPPORTED_1000baseT_Half	0x00000010
+#define SUPPORTED_1000baseT_Full	0x00000020
+#define SUPPORTED_Autoneg		0x00000040
+#define SUPPORTED_TP			0x00000080
+#define SUPPORTED_AUI			0x00000100
+#define SUPPORTED_MII			0x00000200
+#define SUPPORTED_FIBRE			0x00000400
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things.  When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was foced up into this mode or autonegotiated.
+ */
+
+/* The forced speec, 10Mb, 100Mb, gigabit. */
+#define SPEED_10		10
+#define SPEED_100		100
+#define SPEED_1000		1000
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF		0x00
+#define DUPLEX_FULL		0x01
+
+/* Which connector port. */
+#define PORT_TP			0x00
+#define PORT_AUI		0x01
+#define PORT_MII		0x02
+#define PORT_FIBRE		0x03
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL		0x00
+#define XCVR_EXTERNAL		0x01
+#define XCVR_DUMMY1		0x02
+#define XCVR_DUMMY2		0x03
+#define XCVR_DUMMY3		0x04
+
+/* Enable or disable autonegotiation.  If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE		0x00
+#define AUTONEG_ENABLE		0x01
+
+#endif /* _SPARC_ETHTOOL_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/floppy.h linux/include/asm-sparc/floppy.h
--- v2.2.3/linux/include/asm-sparc/floppy.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-sparc/floppy.h	Sun Mar 21 07:23:38 1999
@@ -99,6 +99,7 @@
 /* Routines unique to each controller type on a Sun. */
 static unsigned char sun_82072_fd_inb(int port)
 {
+	udelay(5);
 	switch(port & 7) {
 	default:
 		printk("floppy: Asked to read unknown port %d\n", port);
@@ -115,6 +116,7 @@
 
 static void sun_82072_fd_outb(unsigned char value, int port)
 {
+	udelay(5);
 	switch(port & 7) {
 	default:
 		printk("floppy: Asked to write to unknown port %d\n", port);
@@ -150,6 +152,7 @@
 
 static unsigned char sun_82077_fd_inb(int port)
 {
+	udelay(5);
 	switch(port & 7) {
 	default:
 		printk("floppy: Asked to read unknown port %d\n", port);
@@ -167,6 +170,7 @@
 
 static void sun_82077_fd_outb(unsigned char value, int port)
 {
+	udelay(5);
 	switch(port & 7) {
 	default:
 		printk("floppy: Asked to write to unknown port %d\n", port);
@@ -337,6 +341,7 @@
 	}
 
 	/* Success... */
+	allowed_drive_mask = 0x01;
 	return (int) sun_fdc;
 
 no_sun_fdc:
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/init.h linux/include/asm-sparc/init.h
--- v2.2.3/linux/include/asm-sparc/init.h	Wed Apr 23 19:01:28 1997
+++ linux/include/asm-sparc/init.h	Wed Mar 10 16:53:37 1999
@@ -7,6 +7,8 @@
 #define __initfunc(__arginit) \
 	__arginit __init; \
 	__arginit
+#define __cacheline_aligned __attribute__ \
+			((__section__ (".data.cacheline_aligned")))
 /* For assembly routines */
 #define __INIT		.section	".text.init",#alloc,#execinstr
 #define __FINIT	.previous
@@ -15,6 +17,7 @@
 #define	__init
 #define __initdata
 #define __initfunc(__arginit) __arginit
+#define __cacheline_aligned
 /* For assembly routines */
 #define __INIT
 #define __FINIT
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/io-unit.h linux/include/asm-sparc/io-unit.h
--- v2.2.3/linux/include/asm-sparc/io-unit.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc/io-unit.h	Wed Mar 10 16:53:37 1999
@@ -27,14 +27,18 @@
 #define IOUNIT_DVMA_SIZE    0x00100000 /* 1M */
 
 /* The format of an iopte in the external page tables */
-#define IOUPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12]) */
-#define IOUPTE_CACHE         0x00000080 /* Cached (in Viking/MXCC) */
-#define IOUPTE_STREAM        0x00000040
-#define IOUPTE_INTRA	     0x00000008 /* Not regular memory - probably direct sbus<->sbus dma 
-					   FIXME: Play with this and find out how we can make use of this */
-#define IOUPTE_WRITE         0x00000004 /* Writeable */
-#define IOUPTE_VALID         0x00000002 /* IOPTE is valid */
-#define IOUPTE_PARITY        0x00000001
+#define IOUPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12])	*/
+#define IOUPTE_CACHE         0x00000080 /* Cached (in Viking/MXCC)		*/
+/* XXX Jakub, find out how to program SBUS streaming cache on XDBUS/sun4d.
+ * XXX Actually, all you should need to do is find out where the registers
+ * XXX are and copy over the sparc64 implementation I wrote.  There may be
+ * XXX some horrible hwbugs though, so be careful.  -DaveM
+ */
+#define IOUPTE_STREAM        0x00000040 /* Translation can use streaming cache	*/
+#define IOUPTE_INTRA	     0x00000008 /* SBUS direct slot->slot transfer	*/
+#define IOUPTE_WRITE         0x00000004 /* Writeable				*/
+#define IOUPTE_VALID         0x00000002 /* IOPTE is valid			*/
+#define IOUPTE_PARITY        0x00000001 /* Parity is checked during DVMA	*/
 
 struct iounit_struct {
 	unsigned int		bmap[(IOUNIT_DMA_SIZE >> (PAGE_SHIFT + 3)) / sizeof(unsigned int)];
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/mmu_context.h linux/include/asm-sparc/mmu_context.h
--- v2.2.3/linux/include/asm-sparc/mmu_context.h	Thu May  7 22:51:54 1998
+++ linux/include/asm-sparc/mmu_context.h	Sun Mar 21 07:23:38 1999
@@ -22,11 +22,9 @@
 
 #define destroy_context(mm) BTFIXUP_CALL(destroy_context)(mm)
 
-/*
- * After we have set current->mm to a new value, this activates
+/* After we have set current->mm to a new value, this activates
  * the context for the new mm so we see the new mappings.
- * XXX this presumably needs a sensible implementation - paulus.
  */
-#define activate_context(tsk)	do { } while(0)
+#define activate_context(tsk)	switch_to_context(tsk)
 
 #endif /* !(__SPARC_MMU_CONTEXT_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h
--- v2.2.3/linux/include/asm-sparc/page.h	Wed Jan 13 15:00:43 1999
+++ linux/include/asm-sparc/page.h	Wed Mar 10 16:53:37 1999
@@ -19,6 +19,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/config.h>
 #include <asm/head.h>       /* for KERNBASE */
 #include <asm/btfixup.h>
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.2.3/linux/include/asm-sparc/pgtable.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc/pgtable.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.77 1998/08/04 20:51:19 davem Exp $ */
+/* $Id: pgtable.h,v 1.78 1999/01/07 14:14:05 jj Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
@@ -574,8 +574,10 @@
 
 #define module_map      vmalloc
 #define module_unmap    vfree
+extern unsigned long *sparc_valid_addr_bitmap;
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)		(test_bit(PG_skip, &(page)->flags))
+#define kern_addr_valid(addr)	(test_bit(__pa((unsigned long)(addr))>>20, sparc_valid_addr_bitmap))
 
 #endif /* !(_SPARC_PGTABLE_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/posix_types.h linux/include/asm-sparc/posix_types.h
--- v2.2.3/linux/include/asm-sparc/posix_types.h	Mon Dec  8 23:58:04 1997
+++ linux/include/asm-sparc/posix_types.h	Wed Mar 10 16:53:37 1999
@@ -37,9 +37,15 @@
 #endif
 
 typedef struct {
+#if defined(__KERNEL__) || defined(__USE_ALL)
 	int	val[2];
+#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */
+	int     __val[2];
+#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */
 } __kernel_fsid_t;
 
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+
 #undef __FD_SET
 static __inline__ void __FD_SET(unsigned long fd, __kernel_fd_set *fdsetp)
 {
@@ -108,5 +114,7 @@
 		tmp++;
 	}
 }
+
+#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */
 
 #endif /* !(__ARCH_SPARC_POSIX_TYPES_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.2.3/linux/include/asm-sparc/processor.h	Tue Jan 19 11:32:53 1999
+++ linux/include/asm-sparc/processor.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.67 1998/07/31 20:03:23 zaitcev Exp $
+/* $Id: processor.h,v 1.69 1999/01/19 07:57:44 davem Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -88,7 +88,7 @@
 #define SPARC_FLAG_UNALIGNED    0x2    /* is allowed to do unaligned accesses */
 
 #define INIT_MMAP { &init_mm, (0), (0), \
-		    __pgprot(0x0) , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
+		    NULL, __pgprot(0x0) , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  { \
 /* uwinmask, kregs, sig_address, sig_desc, ksp, kpc, kpsr, kwim */ \
@@ -145,6 +145,8 @@
 
 /* Free all resources held by a thread. */
 #define release_thread(tsk)		do { } while(0)
+extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+
 
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/resource.h linux/include/asm-sparc/resource.h
--- v2.2.3/linux/include/asm-sparc/resource.h	Fri Dec 13 01:37:40 1996
+++ linux/include/asm-sparc/resource.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: resource.h,v 1.6 1996/11/25 03:30:25 ecd Exp $
+/* $Id: resource.h,v 1.7 1998/11/19 20:01:44 davem Exp $
  * resource.h: Resource definitions.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -29,7 +29,7 @@
 #define INIT_RLIMITS							\
 {									\
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
-    {LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM},				\
+    {LONG_MAX, LONG_MAX}, {_STK_LIM, LONG_MAX},				\
     {       0, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
     {NR_OPEN, NR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}                          \
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/sbus.h linux/include/asm-sparc/sbus.h
--- v2.2.3/linux/include/asm-sparc/sbus.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc/sbus.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.15 1998/05/22 14:33:36 jj Exp $
+/* $Id: sbus.h,v 1.16 1998/12/16 04:33:52 davem Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -94,6 +94,13 @@
         
 #define for_all_sbusdev(device, bus) \
 	for((bus) = SBus_chain, (device) = (bus)->devices; (bus); (device)=((device)->next ? (device)->next : ((bus) = (bus)->next, (bus) ? (bus)->devices : 0)))
+
+/* If you did not get the buffer from mmu_get_*() or sparc_alloc_dvma()
+ * then you must use this to get the 32-bit SBUS dvma address.
+ * And in this case it is your responsibility to make sure the buffer
+ * is GFP_DMA, ie. that it is not greater than MAX_DMA_ADDRESS.
+ */
+#define sbus_dvma_addr(__addr)	((__u32)(__addr))
 
 /* Apply promlib probed SBUS ranges to registers. */
 extern void prom_apply_sbus_ranges(struct linux_sbus *sbus, 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/semaphore-helper.h linux/include/asm-sparc/semaphore-helper.h
--- v2.2.3/linux/include/asm-sparc/semaphore-helper.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/semaphore-helper.h	Wed Mar 10 16:53:37 1999
@@ -0,0 +1,167 @@
+#ifndef _SPARC_SEMAPHORE_HELPER_H
+#define _SPARC_SEMAPHORE_HELPER_H
+
+/*
+ * (barely) SMP- and interrupt-safe semaphore helper functions, sparc version.
+ *
+ * (C) Copyright 1999 David S. Miller (davem@redhat.com)
+ * (C) Copyright 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+#define wake_one_more(sem)	atomic_inc(&(sem)->waking)
+static __inline__ int waking_non_zero(struct semaphore *sem)
+{
+	int ret;
+
+#ifdef __SMP__
+	int tmp;
+
+	__asm__ __volatile__("
+		rd	%%psr, %%g1
+		or	%%g1, %3, %0
+		wr	%0, 0x0, %%psr
+		nop; nop; nop;
+1:		ldstub	[%2 + 3], %0
+		tst	%0
+		bne	1b
+		ld	[%2], %0
+		andn	%0, 0xff, %1
+		subcc	%0, 0x1ff, %0
+		bl,a	1f
+		 mov	0, %0
+		mov	%0, %1
+		mov	1, %0
+1:		st	%1, [%2]
+		wr	%%g1, 0x0, %%psr
+		nop; nop; nop\n" 
+	: "=&r" (ret), "=&r" (tmp)
+	: "r" (&sem->waking), "i" (PSR_PIL)
+	: "g1", "memory", "cc");
+#else
+	__asm__ __volatile__("
+		rd	%%psr, %%g1
+		or	%%g1, %2, %0
+		wr	%0, 0x0, %%psr
+		nop; nop; nop;
+		ld	[%1], %0
+		subcc	%0, 1, %0
+		bl,a	1f
+		 mov	0, %0
+		st	%0, [%1]
+		mov	1, %0
+1:		wr	%%g1, 0x0, %%psr
+		nop; nop; nop\n" 
+	: "=&r" (ret)
+	: "r" (&sem->waking), "i" (PSR_PIL)
+	: "g1", "memory", "cc");
+#endif
+	return ret;
+}
+
+static __inline__ int waking_non_zero_interruptible(struct semaphore *sem,
+						    struct task_struct *tsk)
+{
+	int ret;
+
+#ifdef __SMP__
+	int tmp;
+
+	__asm__ __volatile__("
+		rd	%%psr, %%g1
+		or	%%g1, %3, %0
+		wr	%0, 0x0, %%psr
+		nop; nop; nop;
+1:		ldstub	[%2 + 3], %0
+		tst	%0
+		bne	1b
+		ld	[%2], %0
+		andn	%0, 0xff, %1
+		subcc	%0, 0x1ff, %0
+		bl,a	1f
+		 mov	0, %0
+		mov	%0, %1
+		mov	1, %0
+1:		st	%1, [%2]
+		wr	%%g1, 0x0, %%psr
+		nop; nop; nop\n" 
+	: "=&r" (ret), "=&r" (tmp)
+	: "r" (&sem->waking), "i" (PSR_PIL)
+	: "g1", "memory", "cc");
+#else
+	__asm__ __volatile__("
+		rd	%%psr, %%g1
+		or	%%g1, %2, %0
+		wr	%0, 0x0, %%psr
+		nop; nop; nop;
+		ld	[%1], %0
+		subcc	%0, 1, %0
+		bl,a	1f
+		 mov	0, %0
+		st	%0, [%1]
+		mov	1, %0
+1:		wr	%%g1, 0x0, %%psr
+		nop; nop; nop\n" 
+	: "=&r" (ret)
+	: "r" (&sem->waking), "i" (PSR_PIL)
+	: "g1", "memory", "cc");
+#endif
+	if(ret == 0 && signal_pending(tsk)) {
+		atomic_inc(&sem->count);
+		ret = -EINTR;
+	}
+	return ret;
+}
+
+static __inline__ int waking_non_zero_trylock(struct semaphore *sem)
+{
+	int ret;
+
+#ifdef __SMP__
+	int tmp;
+
+	__asm__ __volatile__("
+		rd	%%psr, %%g1
+		or	%%g1, %3, %0
+		wr	%0, 0x0, %%psr
+		nop; nop; nop;
+1:		ldstub	[%2 + 3], %0
+		tst	%0
+		bne	1b
+		ld	[%2], %0
+		andn	%0, 0xff, %1
+		subcc	%0, 0x1ff, %0
+		bl,a	1f
+		 mov	0, %0
+		mov	%0, %1
+		mov	1, %0
+1:		st	%1, [%2]
+		wr	%%g1, 0x0, %%psr
+		nop; nop; nop\n" 
+	: "=&r" (ret), "=&r" (tmp)
+	: "r" (&sem->waking), "i" (PSR_PIL)
+	: "g1", "memory", "cc");
+#else
+	__asm__ __volatile__("
+		rd	%%psr, %%g1
+		or	%%g1, %2, %0
+		wr	%0, 0x0, %%psr
+		nop; nop; nop;
+		ld	[%1], %0
+		subcc	%0, 1, %0
+		bl,a	1f
+		 mov	0, %0
+		st	%0, [%1]
+		mov	1, %0
+1:		wr	%%g1, 0x0, %%psr
+		nop; nop; nop\n" 
+	: "=&r" (ret)
+	: "r" (&sem->waking), "i" (PSR_PIL)
+	: "g1", "memory", "cc");
+#endif
+	ret = !ret;
+	if(ret == 1)
+		atomic_inc(&sem->count);
+	return ret;
+}
+
+#endif /* !(_SPARC_SEMAPHORE_HELPER_H) */
+
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/semaphore.h linux/include/asm-sparc/semaphore.h
--- v2.2.3/linux/include/asm-sparc/semaphore.h	Tue Jan 19 11:32:53 1999
+++ linux/include/asm-sparc/semaphore.h	Wed Mar 10 16:53:37 1999
@@ -9,60 +9,148 @@
 
 struct semaphore {
 	atomic_t count;
-	int waking;
+	atomic_t waking;
 	struct wait_queue * wait;
 };
 
-#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
-#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
+#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
+#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
 
 extern void __down(struct semaphore * sem);
 extern int __down_interruptible(struct semaphore * sem);
+extern int __down_trylock(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
 #define sema_init(sem, val)	atomic_set(&((sem)->count), val)
 
-#define wake_one_more(sem)	\
-do {				\
-	unsigned long flags;	\
-	save_and_cli(flags);	\
-	sem->waking++;		\
-	restore_flags(flags);	\
-} while(0)
-
-#define waking_non_zero(sem,tsk)\
-({	unsigned long flags;	\
-	int ret = 0;		\
-	save_and_cli(flags);	\
-	if (sem->waking > 0) {	\
-		sem->waking--;	\
-		ret = 1;	\
-	}			\
-	restore_flags(flags);	\
-	ret;			\
-})
-
-/* This isn't quite as clever as the x86 side, I'll be fixing this
- * soon enough.
- */
 extern inline void down(struct semaphore * sem)
 {
-	if (atomic_dec_return(&sem->count) < 0)
-		__down(sem);
+	register atomic_t *ptr asm("g1");
+	register int increment asm("g2");
+
+	ptr = (atomic_t *) __atomic_fool_gcc(sem);
+	increment = 1;
+
+	__asm__ __volatile__("
+	mov	%%o7, %%g4
+	call	___atomic_sub
+	 add	%%o7, 8, %%o7
+	tst	%%g2
+	bl	2f
+	 nop
+1:
+	.subsection 2
+2:	save	%%sp, -64, %%sp
+	mov	%%g1, %%l1
+	mov	%%g5, %%l5
+	call	%3
+	 mov	%%g1, %%o0
+	mov	%%l1, %%g1
+	ba	1b
+	 restore %%l5, %%g0, %%g5
+	.previous\n"
+	: "=&r" (increment)
+	: "0" (increment), "r" (ptr), "i" (__down)
+	: "g3", "g4", "g7", "memory", "cc");
 }
 
 extern inline int down_interruptible(struct semaphore * sem)
 {
-	int ret = 0;
-	if(atomic_dec_return(&sem->count) < 0)
-		ret = __down_interruptible(sem);
-	return ret;
+	register atomic_t *ptr asm("g1");
+	register int increment asm("g2");
+
+	ptr = (atomic_t *) __atomic_fool_gcc(sem);
+	increment = 1;
+
+	__asm__ __volatile__("
+	mov	%%o7, %%g4
+	call	___atomic_sub
+	 add	%%o7, 8, %%o7
+	tst	%%g2
+	bl	2f
+	 clr	%%g2
+1:
+	.subsection 2
+2:	save	%%sp, -64, %%sp
+	mov	%%g1, %%l1
+	mov	%%g5, %%l5
+	call	%3
+	 mov	%%g1, %%o0
+	mov	%%l1, %%g1
+	mov	%%l5, %%g5
+	ba	1b
+	 restore %%o0, %%g0, %%g2
+	.previous\n"
+	: "=&r" (increment)
+	: "0" (increment), "r" (ptr), "i" (__down_interruptible)
+	: "g3", "g4", "g7", "memory", "cc");
+
+	return increment;
+}
+
+extern inline int down_trylock(struct semaphore * sem)
+{
+	register atomic_t *ptr asm("g1");
+	register int increment asm("g2");
+
+	ptr = (atomic_t *) __atomic_fool_gcc(sem);
+	increment = 1;
+
+	__asm__ __volatile__("
+	mov	%%o7, %%g4
+	call	___atomic_sub
+	 add	%%o7, 8, %%o7
+	tst	%%g2
+	bl	2f
+	 clr	%%g2
+1:
+	.subsection 2
+2:	save	%%sp, -64, %%sp
+	mov	%%g1, %%l1
+	mov	%%g5, %%l5
+	call	%3
+	 mov	%%g1, %%o0
+	mov	%%l1, %%g1
+	mov	%%l5, %%g5
+	ba	1b
+	 restore %%o0, %%g0, %%g2
+	.previous\n"
+	: "=&r" (increment)
+	: "0" (increment), "r" (ptr), "i" (__down_trylock)
+	: "g3", "g4", "g7", "memory", "cc");
+
+	return increment;
 }
 
 extern inline void up(struct semaphore * sem)
 {
-	if (atomic_inc_return(&sem->count) <= 0)
-		__up(sem);
+	register atomic_t *ptr asm("g1");
+	register int increment asm("g2");
+
+	ptr = (atomic_t *) __atomic_fool_gcc(sem);
+	increment = 1;
+
+	__asm__ __volatile__("
+	mov	%%o7, %%g4
+	call	___atomic_add
+	 add	%%o7, 8, %%o7
+	tst	%%g2
+	ble	2f
+	 nop
+1:
+	.subsection 2
+2:	save	%%sp, -64, %%sp
+	mov	%%g1, %%l1
+	mov	%%g5, %%l5
+	call	%3
+	 mov	%%g1, %%o0
+	mov	%%l1, %%g1
+	ba	1b
+	 restore %%l5, %%g0, %%g5
+	.previous\n"
+	: "=&r" (increment)
+	: "0" (increment), "r" (ptr), "i" (__up)
+	: "g3", "g4", "g7", "memory", "cc");
 }	
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/smp.h linux/include/asm-sparc/smp.h
--- v2.2.3/linux/include/asm-sparc/smp.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc/smp.h	Wed Mar 10 16:53:37 1999
@@ -96,10 +96,6 @@
 extern __volatile__ int __cpu_logical_map[NR_CPUS];
 extern unsigned long smp_proc_in_lock[NR_CPUS];
 
-/* As idle task checks need_resched in a tight loop, it is not necessary to
-   wake it up. -jj */
-#define smp_send_reschedule(cpu) do {} while (0)
-
 extern __inline__ int cpu_logical_map(int cpu)
 {
 	return __cpu_logical_map[cpu];
@@ -161,6 +157,8 @@
 #endif
 
 #define smp_processor_id() hard_smp_processor_id()
+extern __inline__ void smp_send_reschedule(int cpu) { }
+extern __inline__ void smp_send_stop(void) { }
 
 #endif /* !(__ASSEMBLY__) */
 
@@ -178,10 +176,8 @@
 #define MBOX_IDLECPU2         0xFD
 #define MBOX_STOPCPU2         0xFE
 
-#define PROC_CHANGE_PENALTY     20
+#define PROC_CHANGE_PENALTY     15
 
-#define SMP_FROM_INT		1
-#define SMP_FROM_SYSCALL	2
 #endif /* !(__SMP__) */
 
 #define NO_PROC_ID            0xFF
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/softirq.h linux/include/asm-sparc/softirq.h
--- v2.2.3/linux/include/asm-sparc/softirq.h	Sun Nov  8 14:03:09 1998
+++ linux/include/asm-sparc/softirq.h	Wed Mar 10 16:53:37 1999
@@ -41,7 +41,7 @@
 	unsigned long flags;
 	spin_lock_irqsave(&sparc_bh_lock, flags);
 	bh_base[nr] = routine;
-	bh_mask_count[nr] = 0;
+	atomic_set(&bh_mask_count[nr], 0);
 	bh_mask |= 1 << nr;
 	spin_unlock_irqrestore(&sparc_bh_lock, flags);
 }
@@ -50,8 +50,8 @@
 {
 	unsigned long flags;
 	spin_lock_irqsave(&sparc_bh_lock, flags);
-	bh_base[nr] = NULL;
 	bh_mask &= ~(1 << nr);
+	bh_base[nr] = NULL;
 	spin_unlock_irqrestore(&sparc_bh_lock, flags);
 }
 
@@ -72,7 +72,7 @@
 	unsigned long flags;
 	spin_lock_irqsave(&sparc_bh_lock, flags);
 	bh_mask &= ~(1 << nr);
-	bh_mask_count[nr]++;
+	atomic_inc(&bh_mask_count[nr]);
 	spin_unlock_irqrestore(&sparc_bh_lock, flags);
 	synchronize_bh();
 }
@@ -81,7 +81,7 @@
 {
 	unsigned long flags;
 	spin_lock_irqsave(&sparc_bh_lock, flags);
-	if (!--bh_mask_count[nr])
+	if (atomic_dec_and_test(&bh_mask_count[nr]))
 		bh_mask |= 1 << nr;
 	spin_unlock_irqrestore(&sparc_bh_lock, flags);
 }
@@ -133,27 +133,27 @@
 extern inline void disable_bh(int nr)
 {
 	bh_mask &= ~(1 << nr);
-	bh_mask_count[nr]++;
+	atomic_inc(&bh_mask_count[nr]);
 	synchronize_bh();
 }
 
 extern inline void enable_bh(int nr)
 {
-	if (!--bh_mask_count[nr])
+	if (atomic_dec_and_test(&bh_mask_count[nr]))
 		bh_mask |= 1 << nr;
 }
 
 extern inline void init_bh(int nr, void (*routine)(void))
 {
 	bh_base[nr] = routine;
-	bh_mask_count[nr] = 0;
+	atomic_set(&bh_mask_count[nr], 0);
 	bh_mask |= 1 << nr;
 }
 
 extern inline void remove_bh(int nr)
 {
-	bh_base[nr] = NULL;
 	bh_mask &= ~(1 << nr);
+	bh_base[nr] = NULL;
 }
 
 extern inline void start_bh_atomic(void)
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/spinlock.h linux/include/asm-sparc/spinlock.h
--- v2.2.3/linux/include/asm-sparc/spinlock.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc/spinlock.h	Wed Mar 10 16:53:37 1999
@@ -209,10 +209,10 @@
 	: "g2", "memory");
 }
 
-#define spin_lock_irqsave(lock, flags)		\
+#define spin_lock_irqsave(__lock, flags)	\
 do {						\
-	register spinlock_t *lp asm("g1");	\
-	lp = lock;				\
+	register spinlock_t *__lp asm("g1");	\
+	__lp = (__lock);			\
 	__asm__ __volatile__(			\
 	"rd	%%psr, %0\n\t"			\
 	"or	%0, %1, %%g2\n\t"		\
@@ -231,7 +231,7 @@
 	"b,a	1b\n\t"				\
 	".previous\n"				\
 	: "=r" (flags)				\
-	: "i" (PSR_PIL), "r" (lp)		\
+	: "i" (PSR_PIL), "r" (__lp)		\
 	: "g2", "memory", "cc");		\
 } while(0)
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/system.h linux/include/asm-sparc/system.h
--- v2.2.3/linux/include/asm-sparc/system.h	Sun Nov  8 14:03:09 1998
+++ linux/include/asm-sparc/system.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.71 1998/10/13 03:51:06 jj Exp $ */
+/* $Id: system.h,v 1.72 1999/01/02 16:50:21 davem Exp $ */
 #include <linux/config.h>
 
 #ifndef __SPARC_SYSTEM_H
@@ -294,7 +294,9 @@
 #endif
 
 /* XXX Change this if we ever use a PSO mode kernel. */
-#define mb()  __asm__ __volatile__ ("" : : : "memory")
+#define mb()	__asm__ __volatile__ ("" : : : "memory")
+#define rmb()	mb()
+#define wmb()	mb()
 
 #define nop() __asm__ __volatile__ ("nop");
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/termbits.h linux/include/asm-sparc/termbits.h
--- v2.2.3/linux/include/asm-sparc/termbits.h	Tue May 13 22:41:18 1997
+++ linux/include/asm-sparc/termbits.h	Wed Mar 10 16:53:37 1999
@@ -153,6 +153,22 @@
 #define  B460800  0x00001004
 /* This is what we can do with the Zilogs. */
 #define  B76800   0x00001005
+/* This is what we can do with the SAB82532. */
+#define  B153600  0x00001006
+#define  B307200  0x00001007
+#define  B614400  0x00001008
+#define  B921600  0x00001009
+/* And these are the rest... */
+#define  B500000  0x0000100a
+#define  B576000  0x0000100b
+#define B1000000  0x0000100c
+#define B1152000  0x0000100d
+#define B1500000  0x0000100e
+#define B2000000  0x0000100f
+#define B2500000  0x00001010
+#define B3000000  0x00001011
+#define B3500000  0x00001012
+#define B4000000  0x00001013
 #define CIBAUD	  0x100f0000  /* input baud rate (not used) */
 #define CMSPAR	  0x40000000  /* mark or space (stick) parity */
 #define CRTSCTS	  0x80000000  /* flow control */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/termios.h linux/include/asm-sparc/termios.h
--- v2.2.3/linux/include/asm-sparc/termios.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-sparc/termios.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: termios.h,v 1.27 1998/10/04 06:50:13 davem Exp $ */
+/* $Id: termios.h,v 1.28 1999/01/02 16:50:22 davem Exp $ */
 #ifndef _SPARC_TERMIOS_H
 #define _SPARC_TERMIOS_H
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/timex.h linux/include/asm-sparc/timex.h
--- v2.2.3/linux/include/asm-sparc/timex.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-sparc/timex.h	Wed Mar 10 16:53:37 1999
@@ -12,4 +12,9 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
+/* XXX Maybe do something better at some point... -DaveM */
+typedef unsigned long cycles_t;
+extern cycles_t cacheflush_time;
+#define get_cycles()	(0)
+
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.2.3/linux/include/asm-sparc/unistd.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc/unistd.h	Wed Mar 10 16:53:37 1999
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.48 1998/10/07 01:27:50 davem Exp $ */
+/* $Id: unistd.h,v 1.53 1999/02/21 02:34:54 anton Exp $ */
 #ifndef _SPARC_UNISTD_H
 #define _SPARC_UNISTD_H
 
@@ -28,29 +28,29 @@
 #define __NR_unlink              10 /* Common                                      */
 #define __NR_execv               11 /* SunOS Specific                              */
 #define __NR_chdir               12 /* Common                                      */
-#define __NR_chown               13 /* Common                                      */
+#define __NR_chown		 13 /* Common					   */
 #define __NR_mknod               14 /* Common                                      */
 #define __NR_chmod               15 /* Common                                      */
 #define __NR_lchown              16 /* Common                                      */
 #define __NR_brk                 17 /* Common                                      */
-/* #define __NR_ni_syscall       18    RESERVED for sparc64 perf-counter syscall   */
+#define __NR_perfctr             18 /* Performance counter operations              */
 #define __NR_lseek               19 /* Common                                      */
 #define __NR_getpid              20 /* Common                                      */
 #define __NR_capget		 21 /* Linux Specific				   */
 #define __NR_capset		 22 /* Linux Specific				   */
 #define __NR_setuid              23 /* Implemented via setreuid in SunOS           */
 #define __NR_getuid              24 /* Common                                      */
-/* #define __NR_ni_syscall       25    ENOSYS under SunOS                          */
+/* #define __NR_time alias	 25    ENOSYS under SunOS			   */
 #define __NR_ptrace              26 /* Common                                      */
 #define __NR_alarm               27 /* Implemented via setitimer in SunOS          */
 #define __NR_sigaltstack	 28 /* Common					   */
 #define __NR_pause               29 /* Is sigblock(0)->sigpause() in SunOS         */
 #define __NR_utime               30 /* Implemented via utimes() under SunOS        */
-#define __NR_stty                31 /* Implemented via ioctl() under SunOS         */
-#define __NR_gtty                32 /* Implemented via ioctl() under SunOS         */
+/* #define __NR_stty             31    Implemented via ioctl() under SunOS         */
+/* #define __NR_gtty             32    Implemented via ioctl() under SunOS         */
 #define __NR_access              33 /* Common                                      */
 #define __NR_nice                34 /* Implemented via get/setpriority() in SunOS  */
-#define __NR_ftime               35 /* Implemented via gettimeofday() in SunOS     */
+/* #define __NR_ftime            35    Implemented via gettimeofday() in SunOS     */
 #define __NR_sync                36 /* Common                                      */
 #define __NR_kill                37 /* Common                                      */
 #define __NR_stat                38 /* Common                                      */
@@ -59,7 +59,7 @@
 #define __NR_dup                 41 /* Common                                      */
 #define __NR_pipe                42 /* Common                                      */
 #define __NR_times               43 /* Implemented via getrusage() in SunOS        */
-#define __NR_profil              44 /* Common                                      */
+/* #define __NR_profil           44    Common                                      */
 /* #define __NR_ni_syscall       45    ENOSYS under SunOS                          */
 #define __NR_setgid              46 /* Implemented via setregid() in SunOS         */
 #define __NR_getgid              47 /* Common                                      */
@@ -67,8 +67,8 @@
 #define __NR_geteuid             49 /* SunOS calls getuid()                        */
 #define __NR_getegid             50 /* SunOS calls getgid()                        */
 #define __NR_acct                51 /* Common                                      */
-/* #define __NR_ni_syscall       52    ENOSYS under SunOS                          */
-#define __NR_mctl                53 /* SunOS specific                              */
+/* #define __NR_ni_syscall	 52    ENOSYS under SunOS			   */
+/* #define __NR_mctl             53    SunOS specific                              */
 #define __NR_ioctl               54 /* Common                                      */
 #define __NR_reboot              55 /* Common                                      */
 /* #define __NR_ni_syscall       56    ENOSYS under SunOS                          */
@@ -81,60 +81,60 @@
 /* #define __NR_ni_syscall       63    ENOSYS under SunOS                          */
 #define __NR_getpagesize         64 /* Common                                      */
 #define __NR_msync               65 /* Common in newer 1.3.x revs...               */
-/* #define __NR_ni_syscall       66    ENOSYS under SunOS                          */
+#define __NR_vfork               66 /* Common                                      */
 #define __NR_pread               67 /* Linux Specific                              */
 #define __NR_pwrite              68 /* Linux Specific                              */
-#define __NR_sbrk                69 /* SunOS Specific                              */
-#define __NR_sstk                70 /* SunOS Specific                              */
+/* #define __NR_sbrk             69    SunOS Specific                              */
+/* #define __NR_sstk             70    SunOS Specific                              */
 #define __NR_mmap                71 /* Common                                      */
-#define __NR_vadvise             72 /* SunOS Specific                              */
+/* #define __NR_vadvise          72    SunOS Specific                              */
 #define __NR_munmap              73 /* Common                                      */
 #define __NR_mprotect            74 /* Common                                      */
-#define __NR_madvise             75 /* SunOS Specific                              */
+/* #define __NR_madvise          75    SunOS Specific                              */
 #define __NR_vhangup             76 /* Common                                      */
 /* #define __NR_ni_syscall       77    ENOSYS under SunOS                          */
-#define __NR_mincore             78 /* SunOS Specific                              */
+/* #define __NR_mincore          78    SunOS Specific                              */
 #define __NR_getgroups           79 /* Common                                      */
 #define __NR_setgroups           80 /* Common                                      */
 #define __NR_getpgrp             81 /* Common                                      */
-#define __NR_setpgrp             82 /* setpgid, same difference...                 */
+/* #define __NR_setpgrp          82    setpgid, same difference...                 */
 #define __NR_setitimer           83 /* Common                                      */
 /* #define __NR_ni_syscall       84    ENOSYS under SunOS                          */
 #define __NR_swapon              85 /* Common                                      */
 #define __NR_getitimer           86 /* Common                                      */
-#define __NR_gethostname         87 /* SunOS Specific                              */
+/* #define __NR_gethostname      87    SunOS Specific                              */
 #define __NR_sethostname         88 /* Common                                      */
-#define __NR_getdtablesize       89 /* SunOS Specific                              */
+/* #define __NR_getdtablesize    89    SunOS Specific                              */
 #define __NR_dup2                90 /* Common                                      */
-#define __NR_getdopt             91 /* SunOS Specific                              */
+/* #define __NR_getdopt          91    SunOS Specific                              */
 #define __NR_fcntl               92 /* Common                                      */
 #define __NR_select              93 /* Common                                      */
-#define __NR_setdopt             94 /* SunOS Specific                              */
+/* #define __NR_setdopt          94    SunOS Specific                              */
 #define __NR_fsync               95 /* Common                                      */
 #define __NR_setpriority         96 /* Common                                      */
 #define __NR_socket              97 /* Common                                      */
 #define __NR_connect             98 /* Common                                      */
 #define __NR_accept              99 /* Common                                      */
 #define __NR_getpriority        100 /* Common                                      */
-#define __NR_rt_sigreturn	101 /* Linux Specific                              */
-#define __NR_rt_sigaction	102 /* Linux Specific                              */
-#define __NR_rt_sigprocmask	103 /* Linux Specific                              */
-#define __NR_rt_sigpending	104 /* Linux Specific                              */
-#define __NR_rt_sigtimedwait	105 /* Linux Specific                              */
-#define __NR_rt_sigqueueinfo	106 /* Linux Specific                              */
-#define __NR_rt_sigsuspend	107 /* Linux Specific                              */
-#define __NR_sigvec             108 /* SunOS Specific                              */
-#define __NR_sigblock           109 /* SunOS Specific                              */
-#define __NR_sigsetmask         110 /* SunOS Specific                              */
-#define __NR_sigpause           111 /* SunOS Specific                              */
-#define __NR_sigstack           112 /* SunOS Specific                              */
+#define __NR_rt_sigreturn       101 /* Linux Specific                              */
+#define __NR_rt_sigaction       102 /* Linux Specific                              */
+#define __NR_rt_sigprocmask     103 /* Linux Specific                              */
+#define __NR_rt_sigpending      104 /* Linux Specific                              */
+#define __NR_rt_sigtimedwait    105 /* Linux Specific                              */
+#define __NR_rt_sigqueueinfo    106 /* Linux Specific                              */
+#define __NR_rt_sigsuspend      107 /* Linux Specific                              */
+/* #define __NR_sigvec          108    SunOS Specific                              */
+/* #define __NR_sigblock        109    SunOS Specific                              */
+/* #define __NR_sigsetmask      110    SunOS Specific                              */
+/* #define __NR_sigpause        111    SunOS Specific                              */
+/* #define __NR_sigstack        112    SunOS Specific                              */
 #define __NR_recvmsg            113 /* Common                                      */
 #define __NR_sendmsg            114 /* Common                                      */
-#define __NR_vtrace             115 /* SunOS Specific                              */
+/* #define __NR_vtrace          115    SunOS Specific                              */
 #define __NR_gettimeofday       116 /* Common                                      */
 #define __NR_getrusage          117 /* Common                                      */
 #define __NR_getsockopt         118 /* Common                                      */
-#define __NR_getcwd      	119 /* Linux Specific                              */
+#define __NR_getcwd		119 /* Linux Specific				   */
 #define __NR_readv              120 /* Common                                      */
 #define __NR_writev             121 /* Common                                      */
 #define __NR_settimeofday       122 /* Common                                      */
@@ -155,63 +155,63 @@
 #define __NR_rmdir              137 /* Common                                      */
 #define __NR_utimes             138 /* SunOS Specific                              */
 /* #define __NR_ni_syscall      139    ENOSYS under SunOS                          */
-#define __NR_adjtime            140 /* SunOS Specific                              */
+/* #define __NR_adjtime         140    SunOS Specific                              */
 #define __NR_getpeername        141 /* Common                                      */
-#define __NR_gethostid          142 /* SunOS Specific                              */
+/* #define __NR_gethostid       142    SunOS Specific                              */
 /* #define __NR_ni_syscall      143    ENOSYS under SunOS                          */
 #define __NR_getrlimit          144 /* Common                                      */
 #define __NR_setrlimit          145 /* Common                                      */
-#define __NR_killpg             146 /* SunOS Specific                              */
+/* #define __NR_killpg          146    SunOS Specific                              */
 #define __NR_prctl		147 /* ENOSYS under SunOS                          */
-#define __NR_pciconfig_read     148 /* ENOSYS under SunOS                          */
-#define __NR_pciconfig_write    149 /* ENOSYS under SunOS                          */
+#define __NR_pciconfig_read	148 /* ENOSYS under SunOS                          */
+#define __NR_pciconfig_write	149 /* ENOSYS under SunOS                          */
 #define __NR_getsockname        150 /* Common                                      */
-#define __NR_getmsg             151 /* SunOS Specific                              */
-#define __NR_putmsg             152 /* SunOS Specific                              */
+/* #define __NR_getmsg          151    SunOS Specific                              */
+/* #define __NR_putmsg          152    SunOS Specific                              */
 #define __NR_poll               153 /* Common                                      */
 /* #define __NR_ni_syscall      154    ENOSYS under SunOS                          */
-#define __NR_nfssvc             155 /* SunOS Specific                              */
-#define __NR_getdirentries      156 /* SunOS Specific                              */
+/* #define __NR_nfssvc          155    SunOS Specific                              */
+/* #define __NR_getdirentries   156    SunOS Specific                              */
 #define __NR_statfs             157 /* Common                                      */
 #define __NR_fstatfs            158 /* Common                                      */
 #define __NR_umount             159 /* Common                                      */
-#define __NR_async_daemon       160 /* SunOS Specific                              */
-#define __NR_getfh              161 /* SunOS Specific                              */
+/* #define __NR_async_daemon    160    SunOS Specific                              */
+/* #define __NR_getfh           161    SunOS Specific                              */
 #define __NR_getdomainname      162 /* SunOS Specific                              */
 #define __NR_setdomainname      163 /* Common                                      */
-/* #define __NR_ni_syscall      164    ENOSYS under SunOS                          */
+/* #define __NR_ni_syscall	164    ENOSYS under SunOS			   */
 #define __NR_quotactl           165 /* Common                                      */
-#define __NR_exportfs           166 /* SunOS Specific                              */
+/* #define __NR_exportfs        166    SunOS Specific                              */
 #define __NR_mount              167 /* Common                                      */
 #define __NR_ustat              168 /* Common                                      */
-#define __NR_semsys             169 /* SunOS Specific                              */
-#define __NR_msgsys             170 /* SunOS Specific                              */
-#define __NR_shmsys             171 /* SunOS Specific                              */
-#define __NR_auditsys           172 /* SunOS Specific                              */
-#define __NR_rfssys             173 /* SunOS Specific                              */
+/* #define __NR_semsys          169    SunOS Specific                              */
+/* #define __NR_msgsys          170    SunOS Specific                              */
+/* #define __NR_shmsys          171    SunOS Specific                              */
+/* #define __NR_auditsys        172    SunOS Specific                              */
+/* #define __NR_rfssys          173    SunOS Specific                              */
 #define __NR_getdents           174 /* Common                                      */
 #define __NR_setsid             175 /* Common                                      */
 #define __NR_fchdir             176 /* Common                                      */
-#define __NR_fchroot            177 /* SunOS Specific                              */
-#define __NR_vpixsys            178 /* SunOS Specific                              */
-#define __NR_aioread            179 /* SunOS Specific                              */
-#define __NR_aiowrite           180 /* SunOS Specific                              */
-#define __NR_aiowait            181 /* SunOS Specific                              */
-#define __NR_aiocancel          182 /* SunOS Specific                              */
+/* #define __NR_fchroot         177    SunOS Specific                              */
+/* #define __NR_vpixsys         178    SunOS Specific                              */
+/* #define __NR_aioread         179    SunOS Specific                              */
+/* #define __NR_aiowrite        180    SunOS Specific                              */
+/* #define __NR_aiowait         181    SunOS Specific                              */
+/* #define __NR_aiocancel       182    SunOS Specific                              */
 #define __NR_sigpending         183 /* Common                                      */
-#define __NR_query_module       184 /* Linux Specific                              */
+#define __NR_query_module	184 /* Linux Specific				   */
 #define __NR_setpgid            185 /* Common                                      */
-#define __NR_pathconf           186 /* SunOS Specific                              */
-#define __NR_fpathconf          187 /* SunOS Specific                              */
-#define __NR_sysconf            188 /* SunOS Specific                              */
+/* #define __NR_pathconf        186    SunOS Specific                              */
+/* #define __NR_fpathconf       187    SunOS Specific                              */
+/* #define __NR_sysconf         188    SunOS Specific                              */
 #define __NR_uname              189 /* Linux Specific                              */
 #define __NR_init_module        190 /* Linux Specific                              */
 #define __NR_personality        191 /* Linux Specific                              */
-#define __NR_prof               192 /* Linux Specific                              */
-#define __NR_break              193 /* Linux Specific                              */
-#define __NR_lock               194 /* Linux Specific                              */
-#define __NR_mpx                195 /* Linux Specific                              */
-#define __NR_ulimit             196 /* Linux Specific                              */
+/* #define __NR_prof            192    Linux Specific                              */
+/* #define __NR_break           193    Linux Specific                              */
+/* #define __NR_lock            194    Linux Specific                              */
+/* #define __NR_mpx             195    Linux Specific                              */
+/* #define __NR_ulimit          196    Linux Specific                              */
 #define __NR_getppid            197 /* Linux Specific                              */
 #define __NR_sigaction          198 /* Linux Specific                              */
 #define __NR_sgetmask           199 /* Linux Specific                              */
@@ -220,20 +220,20 @@
 #define __NR_oldlstat           202 /* Linux Specific                              */
 #define __NR_uselib             203 /* Linux Specific                              */
 #define __NR_readdir            204 /* Linux Specific                              */
-#define __NR_ioperm             205 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_ioperm          205    Linux Specific - i386 specific, unused      */
 #define __NR_socketcall         206 /* Linux Specific                              */
 #define __NR_syslog             207 /* Linux Specific                              */
-#define __NR_olduname           208 /* Linux Specific                              */
-#define __NR_iopl               209 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_olduname        208    Linux Specific                              */
+/* #define __NR_iopl            209    Linux Specific - i386 specific, unused      */
 #define __NR_idle               210 /* Linux Specific                              */
-#define __NR_vm86               211 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_vm86            211    Linux Specific - i386 specific, unused      */
 #define __NR_waitpid            212 /* Linux Specific                              */
 #define __NR_swapoff            213 /* Linux Specific                              */
 #define __NR_sysinfo            214 /* Linux Specific                              */
 #define __NR_ipc                215 /* Linux Specific                              */
 #define __NR_sigreturn          216 /* Linux Specific                              */
 #define __NR_clone              217 /* Linux Specific                              */
-#define __NR_modify_ldt         218 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_modify_ldt      218    Linux Specific - i386 specific, unused      */
 #define __NR_adjtimex           219 /* Linux Specific                              */
 #define __NR_sigprocmask        220 /* Linux Specific                              */
 #define __NR_create_module      221 /* Linux Specific                              */
@@ -247,10 +247,10 @@
 #define __NR_setfsgid           229 /* Linux Specific                              */
 #define __NR__newselect         230 /* Linux Specific                              */
 #define __NR_time               231 /* Linux Specific                              */
-#define __NR_oldstat            232 /* Linux Specific                              */
+/* #define __NR_oldstat         232    Linux Specific                              */
 #define __NR_stime              233 /* Linux Specific                              */
-#define __NR_oldfstat           234 /* Linux Specific                              */
-#define __NR_phys               235 /* Linux Specific                              */
+/* #define __NR_oldfstat        234    Linux Specific                              */
+/* #define __NR_phys            235    Linux Specific                              */
 #define __NR__llseek            236 /* Linux Specific                              */
 #define __NR_mlock              237
 #define __NR_munlock            238
@@ -439,40 +439,6 @@
 static __inline__ pid_t wait(int * wait_stat)
 {
 	return waitpid(-1,wait_stat,0);
-}
-
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- * NOTE! Only a kernel-only process(ie the swapper or direct descendants
- * who haven't done an "execve()") should use this: it will work within
- * a system call from a "real" process, but the process memory space will
- * not be free'd until both the parent and the child have exited.
- */
-static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
-	long retval;
-
-	__asm__ __volatile("mov %4, %%g2\n\t"    /* Set aside fn ptr... */
-			   "mov %5, %%g3\n\t"    /* and arg. */
-			   "mov %1, %%g1\n\t"
-			   "mov %2, %%o0\n\t"    /* Clone flags. */
-			   "mov 0, %%o1\n\t"     /* usp arg == 0 */
-			   "t 0x10\n\t"          /* Linux/Sparc clone(). */
-			   "cmp %%o1, 0\n\t"
-			   "be 1f\n\t"           /* The parent, just return. */
-			   " nop\n\t"            /* Delay slot. */
-			   "jmpl %%g2, %%o7\n\t" /* Call the function. */
-			   " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
-			   "mov %3, %%g1\n\t"
-			   "t 0x10\n\t"          /* Linux/Sparc exit(). */
-			   /* Notreached by child. */
-			   "1: mov %%o0, %0\n\t" :
-			   "=r" (retval) :
-			   "i" (__NR_clone), "r" (flags | CLONE_VM),
-			   "i" (__NR_exit),  "r" (fn), "r" (arg) :
-			   "g1", "g2", "g3", "o0", "o1", "memory", "cc");
-	return retval;
 }
 
 #endif /* __KERNEL_SYSCALLS__ */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/asm_offsets.h linux/include/asm-sparc64/asm_offsets.h
--- v2.2.3/linux/include/asm-sparc64/asm_offsets.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc64/asm_offsets.h	Wed Mar 10 16:53:38 1999
@@ -2,7 +2,9 @@
 #ifndef __ASM_OFFSETS_H__
 #define __ASM_OFFSETS_H__
 
-#ifndef __SMP__
+#include <linux/config.h>
+
+#ifndef CONFIG_SMP
 
 #define AOFF_task_state	0x00000000
 #define ASIZ_task_state	0x00000008
@@ -20,220 +22,222 @@
 #define ASIZ_task_counter	0x00000008
 #define AOFF_task_priority	0x00000038
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_has_cpu	0x00000040
+#define AOFF_task_avg_slice	0x00000040
+#define ASIZ_task_avg_slice	0x00000008
+#define AOFF_task_has_cpu	0x00000048
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000044
+#define AOFF_task_processor	0x0000004c
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000048
+#define AOFF_task_last_processor	0x00000050
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000004c
+#define AOFF_task_lock_depth	0x00000054
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000050
+#define AOFF_task_next_task	0x00000058
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000058
+#define AOFF_task_prev_task	0x00000060
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000060
+#define AOFF_task_next_run	0x00000068
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x00000068
+#define AOFF_task_prev_run	0x00000070
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_binfmt	0x00000070
+#define AOFF_task_binfmt	0x00000078
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_exit_code	0x00000078
+#define AOFF_task_exit_code	0x00000080
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x0000007c
+#define AOFF_task_exit_signal	0x00000084
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x00000080
+#define AOFF_task_pdeath_signal	0x00000088
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000088
+#define AOFF_task_personality	0x00000090
 #define ASIZ_task_personality	0x00000008
-#define AOFF_task_pid	0x00000094
+#define AOFF_task_pid	0x0000009c
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x00000098
+#define AOFF_task_pgrp	0x000000a0
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x0000009c
+#define AOFF_task_tty_old_pgrp	0x000000a4
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x000000a0
+#define AOFF_task_session	0x000000a8
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x000000a4
+#define AOFF_task_leader	0x000000ac
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x000000a8
+#define AOFF_task_p_opptr	0x000000b0
 #define ASIZ_task_p_opptr	0x00000008
-#define AOFF_task_p_pptr	0x000000b0
+#define AOFF_task_p_pptr	0x000000b8
 #define ASIZ_task_p_pptr	0x00000008
-#define AOFF_task_p_cptr	0x000000b8
+#define AOFF_task_p_cptr	0x000000c0
 #define ASIZ_task_p_cptr	0x00000008
-#define AOFF_task_p_ysptr	0x000000c0
+#define AOFF_task_p_ysptr	0x000000c8
 #define ASIZ_task_p_ysptr	0x00000008
-#define AOFF_task_p_osptr	0x000000c8
+#define AOFF_task_p_osptr	0x000000d0
 #define ASIZ_task_p_osptr	0x00000008
-#define AOFF_task_pidhash_next	0x000000d0
+#define AOFF_task_pidhash_next	0x000000d8
 #define ASIZ_task_pidhash_next	0x00000008
-#define AOFF_task_pidhash_pprev	0x000000d8
+#define AOFF_task_pidhash_pprev	0x000000e0
 #define ASIZ_task_pidhash_pprev	0x00000008
-#define AOFF_task_tarray_ptr	0x000000e0
+#define AOFF_task_tarray_ptr	0x000000e8
 #define ASIZ_task_tarray_ptr	0x00000008
-#define AOFF_task_wait_chldexit	0x000000e8
+#define AOFF_task_wait_chldexit	0x000000f0
 #define ASIZ_task_wait_chldexit	0x00000008
-#define AOFF_task_policy	0x000000f0
+#define AOFF_task_vfork_sem	0x000000f8
+#define ASIZ_task_vfork_sem	0x00000008
+#define AOFF_task_policy	0x00000100
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x000000f8
+#define AOFF_task_rt_priority	0x00000108
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x00000100
+#define AOFF_task_it_real_value	0x00000110
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x00000108
+#define AOFF_task_it_prof_value	0x00000118
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x00000110
+#define AOFF_task_it_virt_value	0x00000120
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x00000118
+#define AOFF_task_it_real_incr	0x00000128
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x00000120
+#define AOFF_task_it_prof_incr	0x00000130
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x00000128
+#define AOFF_task_it_virt_incr	0x00000138
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x00000130
+#define AOFF_task_real_timer	0x00000140
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000158
+#define AOFF_task_times	0x00000168
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x00000178
+#define AOFF_task_start_time	0x00000188
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_per_cpu_utime	0x00000180
+#define AOFF_task_per_cpu_utime	0x00000190
 #define ASIZ_task_per_cpu_utime	0x00000008
-#define AOFF_task_min_flt	0x00000190
+#define AOFF_task_min_flt	0x000001a0
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x00000198
+#define AOFF_task_maj_flt	0x000001a8
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x000001a0
+#define AOFF_task_nswap	0x000001b0
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x000001a8
+#define AOFF_task_cmin_flt	0x000001b8
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x000001b0
+#define AOFF_task_cmaj_flt	0x000001c0
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x000001b8
+#define AOFF_task_cnswap	0x000001c8
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_swap_address	0x000001c8
-#define ASIZ_task_swap_address	0x00000008
-#define AOFF_task_old_maj_flt	0x000001d0
-#define ASIZ_task_old_maj_flt	0x00000008
-#define AOFF_task_dec_flt	0x000001d8
-#define ASIZ_task_dec_flt	0x00000008
-#define AOFF_task_swap_cnt	0x000001e0
-#define ASIZ_task_swap_cnt	0x00000008
-#define AOFF_task_uid	0x000001e8
+#define AOFF_task_uid	0x000001d4
 #define ASIZ_task_uid	0x00000004
-#define AOFF_task_euid	0x000001ec
+#define AOFF_task_euid	0x000001d8
 #define ASIZ_task_euid	0x00000004
-#define AOFF_task_suid	0x000001f0
+#define AOFF_task_suid	0x000001dc
 #define ASIZ_task_suid	0x00000004
-#define AOFF_task_fsuid	0x000001f4
+#define AOFF_task_fsuid	0x000001e0
 #define ASIZ_task_fsuid	0x00000004
-#define AOFF_task_gid	0x000001f8
+#define AOFF_task_gid	0x000001e4
 #define ASIZ_task_gid	0x00000004
-#define AOFF_task_egid	0x000001fc
+#define AOFF_task_egid	0x000001e8
 #define ASIZ_task_egid	0x00000004
-#define AOFF_task_sgid	0x00000200
+#define AOFF_task_sgid	0x000001ec
 #define ASIZ_task_sgid	0x00000004
-#define AOFF_task_fsgid	0x00000204
+#define AOFF_task_fsgid	0x000001f0
 #define ASIZ_task_fsgid	0x00000004
-#define AOFF_task_ngroups	0x00000208
+#define AOFF_task_ngroups	0x000001f4
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x0000020c
+#define AOFF_task_groups	0x000001f8
 #define ASIZ_task_groups	0x00000080
-#define AOFF_task_cap_effective	0x0000028c
+#define AOFF_task_cap_effective	0x00000278
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x00000290
+#define AOFF_task_cap_inheritable	0x0000027c
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000294
+#define AOFF_task_cap_permitted	0x00000280
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x00000298
+#define AOFF_task_user	0x00000288
 #define ASIZ_task_user	0x00000008
-#define AOFF_task_rlim	0x000002a0
+#define AOFF_task_rlim	0x00000290
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000340
+#define AOFF_task_used_math	0x00000330
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000342
+#define AOFF_task_comm	0x00000332
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000354
+#define AOFF_task_link_count	0x00000344
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000358
+#define AOFF_task_tty	0x00000348
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000360
+#define AOFF_task_semundo	0x00000350
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000368
+#define AOFF_task_semsleeping	0x00000358
 #define ASIZ_task_semsleeping	0x00000008
-#define AOFF_task_tss	0x00000370
+#define AOFF_task_tss	0x00000360
 #define ASIZ_task_tss	0x00000470
-#define AOFF_task_fs	0x000007e0
+#define AOFF_task_fs	0x000007d0
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x000007e8
+#define AOFF_task_files	0x000007d8
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x000007f0
+#define AOFF_task_mm	0x000007e0
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sigmask_lock	0x000007f8
+#define AOFF_task_sigmask_lock	0x000007e8
 #define ASIZ_task_sigmask_lock	0x00000001
-#define AOFF_task_sig	0x00000800
+#define AOFF_task_sig	0x000007f0
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x00000808
+#define AOFF_task_signal	0x000007f8
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000810
+#define AOFF_task_blocked	0x00000800
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000818
+#define AOFF_task_sigqueue	0x00000808
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000820
+#define AOFF_task_sigqueue_tail	0x00000810
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_sas_ss_sp	0x00000828
+#define AOFF_task_sas_ss_sp	0x00000818
 #define ASIZ_task_sas_ss_sp	0x00000008
-#define AOFF_task_sas_ss_size	0x00000830
+#define AOFF_task_sas_ss_size	0x00000820
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000840
+#define ASIZ_task	0x00000830
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
-#define AOFF_mm_mmap_cache	0x00000008
+#define AOFF_mm_mmap_avl	0x00000008
+#define ASIZ_mm_mmap_avl	0x00000008
+#define AOFF_mm_mmap_cache	0x00000010
 #define ASIZ_mm_mmap_cache	0x00000008
-#define AOFF_mm_pgd	0x00000010
+#define AOFF_mm_pgd	0x00000018
 #define ASIZ_mm_pgd	0x00000008
-#define AOFF_mm_count	0x00000018
+#define AOFF_mm_count	0x00000020
 #define ASIZ_mm_count	0x00000004
-#define AOFF_mm_map_count	0x0000001c
+#define AOFF_mm_map_count	0x00000024
 #define ASIZ_mm_map_count	0x00000004
-#define AOFF_mm_mmap_sem	0x00000020
+#define AOFF_mm_mmap_sem	0x00000028
 #define ASIZ_mm_mmap_sem	0x00000010
-#define AOFF_mm_context	0x00000030
+#define AOFF_mm_context	0x00000038
 #define ASIZ_mm_context	0x00000008
-#define AOFF_mm_start_code	0x00000038
+#define AOFF_mm_start_code	0x00000040
 #define ASIZ_mm_start_code	0x00000008
-#define AOFF_mm_end_code	0x00000040
+#define AOFF_mm_end_code	0x00000048
 #define ASIZ_mm_end_code	0x00000008
-#define AOFF_mm_start_data	0x00000048
+#define AOFF_mm_start_data	0x00000050
 #define ASIZ_mm_start_data	0x00000008
-#define AOFF_mm_end_data	0x00000050
+#define AOFF_mm_end_data	0x00000058
 #define ASIZ_mm_end_data	0x00000008
-#define AOFF_mm_start_brk	0x00000058
+#define AOFF_mm_start_brk	0x00000060
 #define ASIZ_mm_start_brk	0x00000008
-#define AOFF_mm_brk	0x00000060
+#define AOFF_mm_brk	0x00000068
 #define ASIZ_mm_brk	0x00000008
-#define AOFF_mm_start_stack	0x00000068
+#define AOFF_mm_start_stack	0x00000070
 #define ASIZ_mm_start_stack	0x00000008
-#define AOFF_mm_arg_start	0x00000070
+#define AOFF_mm_arg_start	0x00000078
 #define ASIZ_mm_arg_start	0x00000008
-#define AOFF_mm_arg_end	0x00000078
+#define AOFF_mm_arg_end	0x00000080
 #define ASIZ_mm_arg_end	0x00000008
-#define AOFF_mm_env_start	0x00000080
+#define AOFF_mm_env_start	0x00000088
 #define ASIZ_mm_env_start	0x00000008
-#define AOFF_mm_env_end	0x00000088
+#define AOFF_mm_env_end	0x00000090
 #define ASIZ_mm_env_end	0x00000008
-#define AOFF_mm_rss	0x00000090
+#define AOFF_mm_rss	0x00000098
 #define ASIZ_mm_rss	0x00000008
-#define AOFF_mm_total_vm	0x00000098
+#define AOFF_mm_total_vm	0x000000a0
 #define ASIZ_mm_total_vm	0x00000008
-#define AOFF_mm_locked_vm	0x000000a0
+#define AOFF_mm_locked_vm	0x000000a8
 #define ASIZ_mm_locked_vm	0x00000008
-#define AOFF_mm_def_flags	0x000000a8
+#define AOFF_mm_def_flags	0x000000b0
 #define ASIZ_mm_def_flags	0x00000008
-#define AOFF_mm_cpu_vm_mask	0x000000b0
+#define AOFF_mm_cpu_vm_mask	0x000000b8
 #define ASIZ_mm_cpu_vm_mask	0x00000008
-#define AOFF_mm_segments	0x000000b8
+#define AOFF_mm_swap_cnt	0x000000c0
+#define ASIZ_mm_swap_cnt	0x00000008
+#define AOFF_mm_swap_address	0x000000c8
+#define ASIZ_mm_swap_address	0x00000008
+#define AOFF_mm_segments	0x000000d0
 #define ASIZ_mm_segments	0x00000008
-#define ASIZ_mm	0x000000c0
+#define ASIZ_mm	0x000000d8
 #define AOFF_thread_ksp	0x00000000
 #define ASIZ_thread_ksp	0x00000008
 #define AOFF_thread_wstate	0x00000008
@@ -284,7 +288,7 @@
 #define ASIZ_thread_xfsr	0x00000038
 #define ASIZ_thread	0x00000470
 
-#else /* __SMP__ */
+#else /* CONFIG_SMP */
 
 #ifndef SPIN_LOCK_DEBUG
 
@@ -304,220 +308,222 @@
 #define ASIZ_task_counter	0x00000008
 #define AOFF_task_priority	0x00000038
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_has_cpu	0x00000040
+#define AOFF_task_avg_slice	0x00000040
+#define ASIZ_task_avg_slice	0x00000008
+#define AOFF_task_has_cpu	0x00000048
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000044
+#define AOFF_task_processor	0x0000004c
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000048
+#define AOFF_task_last_processor	0x00000050
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000004c
+#define AOFF_task_lock_depth	0x00000054
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000050
+#define AOFF_task_next_task	0x00000058
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000058
+#define AOFF_task_prev_task	0x00000060
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000060
+#define AOFF_task_next_run	0x00000068
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x00000068
+#define AOFF_task_prev_run	0x00000070
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_binfmt	0x00000070
+#define AOFF_task_binfmt	0x00000078
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_exit_code	0x00000078
+#define AOFF_task_exit_code	0x00000080
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x0000007c
+#define AOFF_task_exit_signal	0x00000084
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x00000080
+#define AOFF_task_pdeath_signal	0x00000088
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000088
+#define AOFF_task_personality	0x00000090
 #define ASIZ_task_personality	0x00000008
-#define AOFF_task_pid	0x00000094
+#define AOFF_task_pid	0x0000009c
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x00000098
+#define AOFF_task_pgrp	0x000000a0
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x0000009c
+#define AOFF_task_tty_old_pgrp	0x000000a4
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x000000a0
+#define AOFF_task_session	0x000000a8
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x000000a4
+#define AOFF_task_leader	0x000000ac
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x000000a8
+#define AOFF_task_p_opptr	0x000000b0
 #define ASIZ_task_p_opptr	0x00000008
-#define AOFF_task_p_pptr	0x000000b0
+#define AOFF_task_p_pptr	0x000000b8
 #define ASIZ_task_p_pptr	0x00000008
-#define AOFF_task_p_cptr	0x000000b8
+#define AOFF_task_p_cptr	0x000000c0
 #define ASIZ_task_p_cptr	0x00000008
-#define AOFF_task_p_ysptr	0x000000c0
+#define AOFF_task_p_ysptr	0x000000c8
 #define ASIZ_task_p_ysptr	0x00000008
-#define AOFF_task_p_osptr	0x000000c8
+#define AOFF_task_p_osptr	0x000000d0
 #define ASIZ_task_p_osptr	0x00000008
-#define AOFF_task_pidhash_next	0x000000d0
+#define AOFF_task_pidhash_next	0x000000d8
 #define ASIZ_task_pidhash_next	0x00000008
-#define AOFF_task_pidhash_pprev	0x000000d8
+#define AOFF_task_pidhash_pprev	0x000000e0
 #define ASIZ_task_pidhash_pprev	0x00000008
-#define AOFF_task_tarray_ptr	0x000000e0
+#define AOFF_task_tarray_ptr	0x000000e8
 #define ASIZ_task_tarray_ptr	0x00000008
-#define AOFF_task_wait_chldexit	0x000000e8
+#define AOFF_task_wait_chldexit	0x000000f0
 #define ASIZ_task_wait_chldexit	0x00000008
-#define AOFF_task_policy	0x000000f0
+#define AOFF_task_vfork_sem	0x000000f8
+#define ASIZ_task_vfork_sem	0x00000008
+#define AOFF_task_policy	0x00000100
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x000000f8
+#define AOFF_task_rt_priority	0x00000108
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x00000100
+#define AOFF_task_it_real_value	0x00000110
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x00000108
+#define AOFF_task_it_prof_value	0x00000118
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x00000110
+#define AOFF_task_it_virt_value	0x00000120
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x00000118
+#define AOFF_task_it_real_incr	0x00000128
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x00000120
+#define AOFF_task_it_prof_incr	0x00000130
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x00000128
+#define AOFF_task_it_virt_incr	0x00000138
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x00000130
+#define AOFF_task_real_timer	0x00000140
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000158
+#define AOFF_task_times	0x00000168
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x00000178
+#define AOFF_task_start_time	0x00000188
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_per_cpu_utime	0x00000180
+#define AOFF_task_per_cpu_utime	0x00000190
 #define ASIZ_task_per_cpu_utime	0x00000100
-#define AOFF_task_min_flt	0x00000380
+#define AOFF_task_min_flt	0x00000390
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x00000388
+#define AOFF_task_maj_flt	0x00000398
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x00000390
+#define AOFF_task_nswap	0x000003a0
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x00000398
+#define AOFF_task_cmin_flt	0x000003a8
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x000003a0
+#define AOFF_task_cmaj_flt	0x000003b0
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x000003a8
+#define AOFF_task_cnswap	0x000003b8
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_swap_address	0x000003b8
-#define ASIZ_task_swap_address	0x00000008
-#define AOFF_task_old_maj_flt	0x000003c0
-#define ASIZ_task_old_maj_flt	0x00000008
-#define AOFF_task_dec_flt	0x000003c8
-#define ASIZ_task_dec_flt	0x00000008
-#define AOFF_task_swap_cnt	0x000003d0
-#define ASIZ_task_swap_cnt	0x00000008
-#define AOFF_task_uid	0x000003d8
+#define AOFF_task_uid	0x000003c4
 #define ASIZ_task_uid	0x00000004
-#define AOFF_task_euid	0x000003dc
+#define AOFF_task_euid	0x000003c8
 #define ASIZ_task_euid	0x00000004
-#define AOFF_task_suid	0x000003e0
+#define AOFF_task_suid	0x000003cc
 #define ASIZ_task_suid	0x00000004
-#define AOFF_task_fsuid	0x000003e4
+#define AOFF_task_fsuid	0x000003d0
 #define ASIZ_task_fsuid	0x00000004
-#define AOFF_task_gid	0x000003e8
+#define AOFF_task_gid	0x000003d4
 #define ASIZ_task_gid	0x00000004
-#define AOFF_task_egid	0x000003ec
+#define AOFF_task_egid	0x000003d8
 #define ASIZ_task_egid	0x00000004
-#define AOFF_task_sgid	0x000003f0
+#define AOFF_task_sgid	0x000003dc
 #define ASIZ_task_sgid	0x00000004
-#define AOFF_task_fsgid	0x000003f4
+#define AOFF_task_fsgid	0x000003e0
 #define ASIZ_task_fsgid	0x00000004
-#define AOFF_task_ngroups	0x000003f8
+#define AOFF_task_ngroups	0x000003e4
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x000003fc
+#define AOFF_task_groups	0x000003e8
 #define ASIZ_task_groups	0x00000080
-#define AOFF_task_cap_effective	0x0000047c
+#define AOFF_task_cap_effective	0x00000468
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x00000480
+#define AOFF_task_cap_inheritable	0x0000046c
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000484
+#define AOFF_task_cap_permitted	0x00000470
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x00000488
+#define AOFF_task_user	0x00000478
 #define ASIZ_task_user	0x00000008
-#define AOFF_task_rlim	0x00000490
+#define AOFF_task_rlim	0x00000480
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000530
+#define AOFF_task_used_math	0x00000520
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000532
+#define AOFF_task_comm	0x00000522
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000544
+#define AOFF_task_link_count	0x00000534
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000548
+#define AOFF_task_tty	0x00000538
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000550
+#define AOFF_task_semundo	0x00000540
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000558
+#define AOFF_task_semsleeping	0x00000548
 #define ASIZ_task_semsleeping	0x00000008
-#define AOFF_task_tss	0x00000560
+#define AOFF_task_tss	0x00000550
 #define ASIZ_task_tss	0x00000470
-#define AOFF_task_fs	0x000009d0
+#define AOFF_task_fs	0x000009c0
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x000009d8
+#define AOFF_task_files	0x000009c8
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x000009e0
+#define AOFF_task_mm	0x000009d0
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sigmask_lock	0x000009e8
+#define AOFF_task_sigmask_lock	0x000009d8
 #define ASIZ_task_sigmask_lock	0x00000001
-#define AOFF_task_sig	0x000009f0
+#define AOFF_task_sig	0x000009e0
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x000009f8
+#define AOFF_task_signal	0x000009e8
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000a00
+#define AOFF_task_blocked	0x000009f0
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000a08
+#define AOFF_task_sigqueue	0x000009f8
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000a10
+#define AOFF_task_sigqueue_tail	0x00000a00
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_sas_ss_sp	0x00000a18
+#define AOFF_task_sas_ss_sp	0x00000a08
 #define ASIZ_task_sas_ss_sp	0x00000008
-#define AOFF_task_sas_ss_size	0x00000a20
+#define AOFF_task_sas_ss_size	0x00000a10
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000a30
+#define ASIZ_task	0x00000a20
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
-#define AOFF_mm_mmap_cache	0x00000008
+#define AOFF_mm_mmap_avl	0x00000008
+#define ASIZ_mm_mmap_avl	0x00000008
+#define AOFF_mm_mmap_cache	0x00000010
 #define ASIZ_mm_mmap_cache	0x00000008
-#define AOFF_mm_pgd	0x00000010
+#define AOFF_mm_pgd	0x00000018
 #define ASIZ_mm_pgd	0x00000008
-#define AOFF_mm_count	0x00000018
+#define AOFF_mm_count	0x00000020
 #define ASIZ_mm_count	0x00000004
-#define AOFF_mm_map_count	0x0000001c
+#define AOFF_mm_map_count	0x00000024
 #define ASIZ_mm_map_count	0x00000004
-#define AOFF_mm_mmap_sem	0x00000020
+#define AOFF_mm_mmap_sem	0x00000028
 #define ASIZ_mm_mmap_sem	0x00000010
-#define AOFF_mm_context	0x00000030
+#define AOFF_mm_context	0x00000038
 #define ASIZ_mm_context	0x00000008
-#define AOFF_mm_start_code	0x00000038
+#define AOFF_mm_start_code	0x00000040
 #define ASIZ_mm_start_code	0x00000008
-#define AOFF_mm_end_code	0x00000040
+#define AOFF_mm_end_code	0x00000048
 #define ASIZ_mm_end_code	0x00000008
-#define AOFF_mm_start_data	0x00000048
+#define AOFF_mm_start_data	0x00000050
 #define ASIZ_mm_start_data	0x00000008
-#define AOFF_mm_end_data	0x00000050
+#define AOFF_mm_end_data	0x00000058
 #define ASIZ_mm_end_data	0x00000008
-#define AOFF_mm_start_brk	0x00000058
+#define AOFF_mm_start_brk	0x00000060
 #define ASIZ_mm_start_brk	0x00000008
-#define AOFF_mm_brk	0x00000060
+#define AOFF_mm_brk	0x00000068
 #define ASIZ_mm_brk	0x00000008
-#define AOFF_mm_start_stack	0x00000068
+#define AOFF_mm_start_stack	0x00000070
 #define ASIZ_mm_start_stack	0x00000008
-#define AOFF_mm_arg_start	0x00000070
+#define AOFF_mm_arg_start	0x00000078
 #define ASIZ_mm_arg_start	0x00000008
-#define AOFF_mm_arg_end	0x00000078
+#define AOFF_mm_arg_end	0x00000080
 #define ASIZ_mm_arg_end	0x00000008
-#define AOFF_mm_env_start	0x00000080
+#define AOFF_mm_env_start	0x00000088
 #define ASIZ_mm_env_start	0x00000008
-#define AOFF_mm_env_end	0x00000088
+#define AOFF_mm_env_end	0x00000090
 #define ASIZ_mm_env_end	0x00000008
-#define AOFF_mm_rss	0x00000090
+#define AOFF_mm_rss	0x00000098
 #define ASIZ_mm_rss	0x00000008
-#define AOFF_mm_total_vm	0x00000098
+#define AOFF_mm_total_vm	0x000000a0
 #define ASIZ_mm_total_vm	0x00000008
-#define AOFF_mm_locked_vm	0x000000a0
+#define AOFF_mm_locked_vm	0x000000a8
 #define ASIZ_mm_locked_vm	0x00000008
-#define AOFF_mm_def_flags	0x000000a8
+#define AOFF_mm_def_flags	0x000000b0
 #define ASIZ_mm_def_flags	0x00000008
-#define AOFF_mm_cpu_vm_mask	0x000000b0
+#define AOFF_mm_cpu_vm_mask	0x000000b8
 #define ASIZ_mm_cpu_vm_mask	0x00000008
-#define AOFF_mm_segments	0x000000b8
+#define AOFF_mm_swap_cnt	0x000000c0
+#define ASIZ_mm_swap_cnt	0x00000008
+#define AOFF_mm_swap_address	0x000000c8
+#define ASIZ_mm_swap_address	0x00000008
+#define AOFF_mm_segments	0x000000d0
 #define ASIZ_mm_segments	0x00000008
-#define ASIZ_mm	0x000000c0
+#define ASIZ_mm	0x000000d8
 #define AOFF_thread_ksp	0x00000000
 #define ASIZ_thread_ksp	0x00000008
 #define AOFF_thread_wstate	0x00000008
@@ -586,220 +592,222 @@
 #define ASIZ_task_counter	0x00000008
 #define AOFF_task_priority	0x00000038
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_has_cpu	0x00000040
+#define AOFF_task_avg_slice	0x00000040
+#define ASIZ_task_avg_slice	0x00000008
+#define AOFF_task_has_cpu	0x00000048
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x00000044
+#define AOFF_task_processor	0x0000004c
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000048
+#define AOFF_task_last_processor	0x00000050
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x0000004c
+#define AOFF_task_lock_depth	0x00000054
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000050
+#define AOFF_task_next_task	0x00000058
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000058
+#define AOFF_task_prev_task	0x00000060
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000060
+#define AOFF_task_next_run	0x00000068
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x00000068
+#define AOFF_task_prev_run	0x00000070
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_binfmt	0x00000070
+#define AOFF_task_binfmt	0x00000078
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_exit_code	0x00000078
+#define AOFF_task_exit_code	0x00000080
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x0000007c
+#define AOFF_task_exit_signal	0x00000084
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x00000080
+#define AOFF_task_pdeath_signal	0x00000088
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000088
+#define AOFF_task_personality	0x00000090
 #define ASIZ_task_personality	0x00000008
-#define AOFF_task_pid	0x00000094
+#define AOFF_task_pid	0x0000009c
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x00000098
+#define AOFF_task_pgrp	0x000000a0
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x0000009c
+#define AOFF_task_tty_old_pgrp	0x000000a4
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x000000a0
+#define AOFF_task_session	0x000000a8
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x000000a4
+#define AOFF_task_leader	0x000000ac
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x000000a8
+#define AOFF_task_p_opptr	0x000000b0
 #define ASIZ_task_p_opptr	0x00000008
-#define AOFF_task_p_pptr	0x000000b0
+#define AOFF_task_p_pptr	0x000000b8
 #define ASIZ_task_p_pptr	0x00000008
-#define AOFF_task_p_cptr	0x000000b8
+#define AOFF_task_p_cptr	0x000000c0
 #define ASIZ_task_p_cptr	0x00000008
-#define AOFF_task_p_ysptr	0x000000c0
+#define AOFF_task_p_ysptr	0x000000c8
 #define ASIZ_task_p_ysptr	0x00000008
-#define AOFF_task_p_osptr	0x000000c8
+#define AOFF_task_p_osptr	0x000000d0
 #define ASIZ_task_p_osptr	0x00000008
-#define AOFF_task_pidhash_next	0x000000d0
+#define AOFF_task_pidhash_next	0x000000d8
 #define ASIZ_task_pidhash_next	0x00000008
-#define AOFF_task_pidhash_pprev	0x000000d8
+#define AOFF_task_pidhash_pprev	0x000000e0
 #define ASIZ_task_pidhash_pprev	0x00000008
-#define AOFF_task_tarray_ptr	0x000000e0
+#define AOFF_task_tarray_ptr	0x000000e8
 #define ASIZ_task_tarray_ptr	0x00000008
-#define AOFF_task_wait_chldexit	0x000000e8
+#define AOFF_task_wait_chldexit	0x000000f0
 #define ASIZ_task_wait_chldexit	0x00000008
-#define AOFF_task_policy	0x000000f0
+#define AOFF_task_vfork_sem	0x000000f8
+#define ASIZ_task_vfork_sem	0x00000008
+#define AOFF_task_policy	0x00000100
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x000000f8
+#define AOFF_task_rt_priority	0x00000108
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x00000100
+#define AOFF_task_it_real_value	0x00000110
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x00000108
+#define AOFF_task_it_prof_value	0x00000118
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x00000110
+#define AOFF_task_it_virt_value	0x00000120
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x00000118
+#define AOFF_task_it_real_incr	0x00000128
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x00000120
+#define AOFF_task_it_prof_incr	0x00000130
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x00000128
+#define AOFF_task_it_virt_incr	0x00000138
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x00000130
+#define AOFF_task_real_timer	0x00000140
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000158
+#define AOFF_task_times	0x00000168
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x00000178
+#define AOFF_task_start_time	0x00000188
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_per_cpu_utime	0x00000180
+#define AOFF_task_per_cpu_utime	0x00000190
 #define ASIZ_task_per_cpu_utime	0x00000100
-#define AOFF_task_min_flt	0x00000380
+#define AOFF_task_min_flt	0x00000390
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x00000388
+#define AOFF_task_maj_flt	0x00000398
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x00000390
+#define AOFF_task_nswap	0x000003a0
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x00000398
+#define AOFF_task_cmin_flt	0x000003a8
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x000003a0
+#define AOFF_task_cmaj_flt	0x000003b0
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x000003a8
+#define AOFF_task_cnswap	0x000003b8
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_swap_address	0x000003b8
-#define ASIZ_task_swap_address	0x00000008
-#define AOFF_task_old_maj_flt	0x000003c0
-#define ASIZ_task_old_maj_flt	0x00000008
-#define AOFF_task_dec_flt	0x000003c8
-#define ASIZ_task_dec_flt	0x00000008
-#define AOFF_task_swap_cnt	0x000003d0
-#define ASIZ_task_swap_cnt	0x00000008
-#define AOFF_task_uid	0x000003d8
+#define AOFF_task_uid	0x000003c4
 #define ASIZ_task_uid	0x00000004
-#define AOFF_task_euid	0x000003dc
+#define AOFF_task_euid	0x000003c8
 #define ASIZ_task_euid	0x00000004
-#define AOFF_task_suid	0x000003e0
+#define AOFF_task_suid	0x000003cc
 #define ASIZ_task_suid	0x00000004
-#define AOFF_task_fsuid	0x000003e4
+#define AOFF_task_fsuid	0x000003d0
 #define ASIZ_task_fsuid	0x00000004
-#define AOFF_task_gid	0x000003e8
+#define AOFF_task_gid	0x000003d4
 #define ASIZ_task_gid	0x00000004
-#define AOFF_task_egid	0x000003ec
+#define AOFF_task_egid	0x000003d8
 #define ASIZ_task_egid	0x00000004
-#define AOFF_task_sgid	0x000003f0
+#define AOFF_task_sgid	0x000003dc
 #define ASIZ_task_sgid	0x00000004
-#define AOFF_task_fsgid	0x000003f4
+#define AOFF_task_fsgid	0x000003e0
 #define ASIZ_task_fsgid	0x00000004
-#define AOFF_task_ngroups	0x000003f8
+#define AOFF_task_ngroups	0x000003e4
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x000003fc
+#define AOFF_task_groups	0x000003e8
 #define ASIZ_task_groups	0x00000080
-#define AOFF_task_cap_effective	0x0000047c
+#define AOFF_task_cap_effective	0x00000468
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x00000480
+#define AOFF_task_cap_inheritable	0x0000046c
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000484
+#define AOFF_task_cap_permitted	0x00000470
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x00000488
+#define AOFF_task_user	0x00000478
 #define ASIZ_task_user	0x00000008
-#define AOFF_task_rlim	0x00000490
+#define AOFF_task_rlim	0x00000480
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000530
+#define AOFF_task_used_math	0x00000520
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000532
+#define AOFF_task_comm	0x00000522
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000544
+#define AOFF_task_link_count	0x00000534
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000548
+#define AOFF_task_tty	0x00000538
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000550
+#define AOFF_task_semundo	0x00000540
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000558
+#define AOFF_task_semsleeping	0x00000548
 #define ASIZ_task_semsleeping	0x00000008
-#define AOFF_task_tss	0x00000560
+#define AOFF_task_tss	0x00000550
 #define ASIZ_task_tss	0x00000470
-#define AOFF_task_fs	0x000009d0
+#define AOFF_task_fs	0x000009c0
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x000009d8
+#define AOFF_task_files	0x000009c8
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x000009e0
+#define AOFF_task_mm	0x000009d0
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sigmask_lock	0x000009e8
+#define AOFF_task_sigmask_lock	0x000009d8
 #define ASIZ_task_sigmask_lock	0x0000000c
-#define AOFF_task_sig	0x000009f8
+#define AOFF_task_sig	0x000009e8
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x00000a00
+#define AOFF_task_signal	0x000009f0
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000a08
+#define AOFF_task_blocked	0x000009f8
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000a10
+#define AOFF_task_sigqueue	0x00000a00
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000a18
+#define AOFF_task_sigqueue_tail	0x00000a08
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_sas_ss_sp	0x00000a20
+#define AOFF_task_sas_ss_sp	0x00000a10
 #define ASIZ_task_sas_ss_sp	0x00000008
-#define AOFF_task_sas_ss_size	0x00000a28
+#define AOFF_task_sas_ss_size	0x00000a18
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000a30
+#define ASIZ_task	0x00000a20
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
-#define AOFF_mm_mmap_cache	0x00000008
+#define AOFF_mm_mmap_avl	0x00000008
+#define ASIZ_mm_mmap_avl	0x00000008
+#define AOFF_mm_mmap_cache	0x00000010
 #define ASIZ_mm_mmap_cache	0x00000008
-#define AOFF_mm_pgd	0x00000010
+#define AOFF_mm_pgd	0x00000018
 #define ASIZ_mm_pgd	0x00000008
-#define AOFF_mm_count	0x00000018
+#define AOFF_mm_count	0x00000020
 #define ASIZ_mm_count	0x00000004
-#define AOFF_mm_map_count	0x0000001c
+#define AOFF_mm_map_count	0x00000024
 #define ASIZ_mm_map_count	0x00000004
-#define AOFF_mm_mmap_sem	0x00000020
+#define AOFF_mm_mmap_sem	0x00000028
 #define ASIZ_mm_mmap_sem	0x00000010
-#define AOFF_mm_context	0x00000030
+#define AOFF_mm_context	0x00000038
 #define ASIZ_mm_context	0x00000008
-#define AOFF_mm_start_code	0x00000038
+#define AOFF_mm_start_code	0x00000040
 #define ASIZ_mm_start_code	0x00000008
-#define AOFF_mm_end_code	0x00000040
+#define AOFF_mm_end_code	0x00000048
 #define ASIZ_mm_end_code	0x00000008
-#define AOFF_mm_start_data	0x00000048
+#define AOFF_mm_start_data	0x00000050
 #define ASIZ_mm_start_data	0x00000008
-#define AOFF_mm_end_data	0x00000050
+#define AOFF_mm_end_data	0x00000058
 #define ASIZ_mm_end_data	0x00000008
-#define AOFF_mm_start_brk	0x00000058
+#define AOFF_mm_start_brk	0x00000060
 #define ASIZ_mm_start_brk	0x00000008
-#define AOFF_mm_brk	0x00000060
+#define AOFF_mm_brk	0x00000068
 #define ASIZ_mm_brk	0x00000008
-#define AOFF_mm_start_stack	0x00000068
+#define AOFF_mm_start_stack	0x00000070
 #define ASIZ_mm_start_stack	0x00000008
-#define AOFF_mm_arg_start	0x00000070
+#define AOFF_mm_arg_start	0x00000078
 #define ASIZ_mm_arg_start	0x00000008
-#define AOFF_mm_arg_end	0x00000078
+#define AOFF_mm_arg_end	0x00000080
 #define ASIZ_mm_arg_end	0x00000008
-#define AOFF_mm_env_start	0x00000080
+#define AOFF_mm_env_start	0x00000088
 #define ASIZ_mm_env_start	0x00000008
-#define AOFF_mm_env_end	0x00000088
+#define AOFF_mm_env_end	0x00000090
 #define ASIZ_mm_env_end	0x00000008
-#define AOFF_mm_rss	0x00000090
+#define AOFF_mm_rss	0x00000098
 #define ASIZ_mm_rss	0x00000008
-#define AOFF_mm_total_vm	0x00000098
+#define AOFF_mm_total_vm	0x000000a0
 #define ASIZ_mm_total_vm	0x00000008
-#define AOFF_mm_locked_vm	0x000000a0
+#define AOFF_mm_locked_vm	0x000000a8
 #define ASIZ_mm_locked_vm	0x00000008
-#define AOFF_mm_def_flags	0x000000a8
+#define AOFF_mm_def_flags	0x000000b0
 #define ASIZ_mm_def_flags	0x00000008
-#define AOFF_mm_cpu_vm_mask	0x000000b0
+#define AOFF_mm_cpu_vm_mask	0x000000b8
 #define ASIZ_mm_cpu_vm_mask	0x00000008
-#define AOFF_mm_segments	0x000000b8
+#define AOFF_mm_swap_cnt	0x000000c0
+#define ASIZ_mm_swap_cnt	0x00000008
+#define AOFF_mm_swap_address	0x000000c8
+#define ASIZ_mm_swap_address	0x00000008
+#define AOFF_mm_segments	0x000000d0
 #define ASIZ_mm_segments	0x00000008
-#define ASIZ_mm	0x000000c0
+#define ASIZ_mm	0x000000d8
 #define AOFF_thread_ksp	0x00000000
 #define ASIZ_thread_ksp	0x00000008
 #define AOFF_thread_wstate	0x00000008
@@ -851,6 +859,6 @@
 #define ASIZ_thread	0x00000470
 #endif /* SPIN_LOCK_DEBUG */
 
-#endif /* __SMP__ */
+#endif /* CONFIG_SMP */
 
 #endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/bitops.h linux/include/asm-sparc64/bitops.h
--- v2.2.3/linux/include/asm-sparc64/bitops.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc64/bitops.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: bitops.h,v 1.25 1998/07/26 03:05:51 davem Exp $
+/* $Id: bitops.h,v 1.26 1999/01/07 14:14:15 jj Exp $
  * bitops.h: Bit string operations on the V9.
  *
  * Copyright 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -14,8 +14,8 @@
  * primitives used are not privileged.
  */
 
-/* Set bit 'nr' in 32-bit quantity at address 'addr' where bit '0'
- * is in the highest of the four bytes and bit '31' is the high bit
+/* Set bit 'nr' in 64-bit quantity at address 'addr' where bit '0'
+ * is in the highest of the eight bytes and bit '63' is the high bit
  * within the first byte. Sparc is BIG-Endian. Unless noted otherwise
  * all bit-ops return 0 if bit was previously clear and != 0 otherwise.
  */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/cache.h linux/include/asm-sparc64/cache.h
--- v2.2.3/linux/include/asm-sparc64/cache.h	Thu May 29 21:53:09 1997
+++ linux/include/asm-sparc64/cache.h	Wed Mar 10 16:53:38 1999
@@ -9,4 +9,6 @@
 
 #define        L1_CACHE_ALIGN(x)       (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1))
 
+#define        SMP_CACHE_BYTES		64 /* L2 cache line size. */
+
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/dma.h linux/include/asm-sparc64/dma.h
--- v2.2.3/linux/include/asm-sparc64/dma.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc64/dma.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.10 1998/10/27 23:28:50 davem Exp $
+/* $Id: dma.h,v 1.13 1998/12/16 04:33:55 davem Exp $
  * include/asm-sparc64/dma.h
  *
  * Copyright 1996 (C) David S. Miller (davem@caip.rutgers.edu)
@@ -30,10 +30,13 @@
  * things can compile.
  */
 #define MAX_DMA_CHANNELS 8
-#define MAX_DMA_ADDRESS  ((0xf0000000) + PAGE_OFFSET)
 #define DMA_MODE_READ    1
 #define DMA_MODE_WRITE   2
 
+/* This is actually used. */
+extern unsigned long phys_base;
+#define MAX_DMA_ADDRESS  (phys_base + (0xfe000000UL) + PAGE_OFFSET)
+
 /* Useful constants */
 #define SIZE_16MB      (16*1024*1024)
 #define SIZE_64K       (64*1024)
@@ -115,13 +118,14 @@
 #define DMA_DSBL_RD_DRN  0x00001000        /* No EC drain on slave reads */
 #define DMA_BCNT_ENAB    0x00002000        /* If on, use the byte counter */
 #define DMA_TERM_CNTR    0x00004000        /* Terminal counter */
+#define DMA_SCSI_SBUS64  0x00008000        /* HME: Enable 64-bit SBUS mode. */
 #define DMA_CSR_DISAB    0x00010000        /* No FIFO drains during csr */
 #define DMA_SCSI_DISAB   0x00020000        /* No FIFO drains during reg */
 #define DMA_DSBL_WR_INV  0x00020000        /* No EC inval. on slave writes */
 #define DMA_ADD_ENABLE   0x00040000        /* Special ESC DVMA optimization */
 #define DMA_E_BURST8	 0x00040000	   /* ENET: SBUS r/w burst size */
 #define DMA_BRST_SZ      0x000c0000        /* SCSI: SBUS r/w burst size */
-#define DMA_BRST64       0x00080000        /* SCSI: 64byte bursts (HME on UltraSparc only) */
+#define DMA_BRST64       0x000c0000        /* SCSI: 64byte bursts (HME on UltraSparc only) */
 #define DMA_BRST32       0x00040000        /* SCSI: 32byte bursts */
 #define DMA_BRST16       0x00000000        /* SCSI: 16byte bursts */
 #define DMA_BRST0        0x00080000        /* SCSI: no bursts (non-HME gate arrays) */
@@ -135,6 +139,7 @@
 #define DMA_PARITY_OFF   0x02000000        /* HME: disable parity checking */
 #define DMA_LOADED_ADDR  0x04000000        /* Address has been loaded */
 #define DMA_LOADED_NADDR 0x08000000        /* Next address has been loaded */
+#define DMA_RESET_FAS366 0x08000000        /* HME: Assert RESET to FAS366 */
 
 /* Values describing the burst-size property from the PROM */
 #define DMA_BURST1       0x01
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/ethtool.h linux/include/asm-sparc64/ethtool.h
--- v2.2.3/linux/include/asm-sparc64/ethtool.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/ethtool.h	Wed Mar 10 16:53:38 1999
@@ -0,0 +1,79 @@
+/* $Id: ethtool.h,v 1.1 1998/12/19 15:09:40 davem Exp $
+ * ethtool.h: Defines for SparcLinux ethtool.
+ *
+ * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ */
+
+#ifndef _SPARC64_ETHTOOL_H
+#define _SPARC64_ETHTOOL_H
+
+/* We do things like this so it does not matter what kernel
+ * headers you have on your system etc.
+ */
+#undef SIOCETHTOOL
+#define SIOCETHTOOL	(SIOCDEVPRIVATE + 0x0f)
+
+/* This should work for both 32 and 64 bit userland. */
+struct ethtool_cmd {
+	u32	cmd;
+	u32	supported;
+	u16	speed;
+	u8	duplex;
+	u8	port;
+	u8	phy_address;
+	u8	transceiver;
+	u8	autoneg;
+};
+
+/* CMDs currently supported */
+#define SPARC_ETH_GSET		0x00000001	/* Get settings, non-privileged. */
+#define SPARC_ETH_SSET		0x00000002	/* Set settings, privileged. */
+
+/* Indicates what features are supported by the interface. */
+#define SUPPORTED_10baseT_Half		0x00000001
+#define SUPPORTED_10baseT_Full		0x00000002
+#define SUPPORTED_100baseT_Half		0x00000004
+#define SUPPORTED_100baseT_Full		0x00000008
+#define SUPPORTED_1000baseT_Half	0x00000010
+#define SUPPORTED_1000baseT_Full	0x00000020
+#define SUPPORTED_Autoneg		0x00000040
+#define SUPPORTED_TP			0x00000080
+#define SUPPORTED_AUI			0x00000100
+#define SUPPORTED_MII			0x00000200
+#define SUPPORTED_FIBRE			0x00000400
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things.  When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was foced up into this mode or autonegotiated.
+ */
+
+/* The forced speec, 10Mb, 100Mb, gigabit. */
+#define SPEED_10		10
+#define SPEED_100		100
+#define SPEED_1000		1000
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF		0x00
+#define DUPLEX_FULL		0x01
+
+/* Which connector port. */
+#define PORT_TP			0x00
+#define PORT_AUI		0x01
+#define PORT_MII		0x02
+#define PORT_FIBRE		0x03
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL		0x00
+#define XCVR_EXTERNAL		0x01
+#define XCVR_DUMMY1		0x02
+#define XCVR_DUMMY2		0x03
+#define XCVR_DUMMY3		0x04
+
+/* Enable or disable autonegotiation.  If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE		0x00
+#define AUTONEG_ENABLE		0x01
+
+#endif /* _SPARC64_ETHTOOL_H */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/fhc.h linux/include/asm-sparc64/fhc.h
--- v2.2.3/linux/include/asm-sparc64/fhc.h	Sat Aug 16 09:51:10 1997
+++ linux/include/asm-sparc64/fhc.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: fhc.h,v 1.1 1997/08/08 04:26:40 davem Exp $
+/* $Id: fhc.h,v 1.4 1998/12/14 12:18:20 davem Exp $
  * fhc.h: Structures for central/fhc pseudo driver on Sunfire/Starfire/Wildfire.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -7,13 +7,42 @@
 #ifndef _SPARC64_FHC_H
 #define _SPARC64_FHC_H
 
+#include <linux/timer.h>
+
 #include <asm/firehose.h>
 #include <asm/oplib.h>
 
 struct linux_fhc;
 
+struct clock_board_regs {
+	u8	control;
+	u8	_unused1[0x10 - 0x01];
+	u8	stat1;
+	u8	_unused2[0x10 - 0x01];
+	u8	stat2;
+	u8	_unused3[0x10 - 0x01];
+	u8	pwr_stat;
+	u8	_unused4[0x10 - 0x01];
+	u8	pwr_presence;
+	u8	_unused5[0x10 - 0x01];
+	u8	temperature;
+	u8	_unused6[0x10 - 0x01];
+	u8	irq_diag;
+	u8	_unused7[0x10 - 0x01];
+	u8	pwr_stat2;
+	u8	_unused8[0x10 - 0x01];
+};
+
+#define CLOCK_CTRL_LLED		0x04	/* Left LED, 0 == on */
+#define CLOCK_CTRL_MLED		0x02	/* Mid LED, 1 == on */
+#define CLOCK_CTRL_RLED		0x01	/* RIght LED, 1 == on */
+
 struct linux_central {
 	struct linux_fhc		*child;
+	volatile u8			*cfreg;
+	struct clock_board_regs		*clkregs;
+	volatile u8			*clkver;
+	int				slots;
 	int				prom_node;
 	char				prom_name[64];
 
@@ -25,6 +54,8 @@
 	struct linux_fhc		*next;
 	struct linux_central		*parent;	/* NULL if not central FHC */
 	struct fhc_regs			fhc_regs;
+	int				board;
+	int				jtag_master;
 	int				prom_node;
 	char				prom_name[64];
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/firehose.h linux/include/asm-sparc64/firehose.h
--- v2.2.3/linux/include/asm-sparc64/firehose.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc64/firehose.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: firehose.h,v 1.3 1998/06/10 07:28:43 davem Exp $
+/* $Id: firehose.h,v 1.4 1998/12/02 10:16:47 davem Exp $
  * firehose.h: Defines for the Fire Hose Controller (FHC) found
  *             on Sunfire/Starfire/Wildfire systems.
  *
@@ -10,8 +10,6 @@
 
 #include <linux/types.h>
 
-/* XXX I have not fully verified the register sizes in this file yet... -DaveM */
-
 /* Fire Hose Controller Internal Registers */
 struct fhc_internal_regs {
 /*0x0000*/	u32	fhc_id;		/* FHC ID Register			*/
@@ -33,31 +31,31 @@
  * for the FHC, thus we have the following few structs...
  */
 struct fhc_ign_reg {
-/*0x2000*/	u64	fhc_ign;		/* FHC Interrupt Group Number	*/
+/*0x2000*/	u32	fhc_ign;		/* FHC Interrupt Group Number	*/
 };
 
 struct fhc_fanfail_regs {
-/*0x4000*/	u32	_pad0, fhc_ff_imap;	/* FHC FanFail Interrupt Map	*/
-		u64	_pad1;
-/*0x4010*/	u32	_pad2, fhc_ff_iclr;	/* FHC FanFail Interrupt Clear	*/
+/*0x4000*/	u32	fhc_ff_imap;		/* FHC FanFail Interrupt Map	*/
+		u32	_pad[3];
+/*0x4010*/	u32	fhc_ff_iclr;		/* FHC FanFail Interrupt Clear	*/
 };
 
 struct fhc_system_regs {
-/*0x6000*/	u32	_pad0, fhc_sys_imap;	/* FHC System Interrupt Map	*/
-		u64	_pad1;
-/*0x6010*/	u32	_pad2, fhc_sys_iclr;	/* FHC System Interrupt Clear	*/
+/*0x6000*/	u32	fhc_sys_imap;		/* FHC System Interrupt Map	*/
+		u32	_pad[3];
+/*0x6010*/	u32	fhc_sys_iclr;		/* FHC System Interrupt Clear	*/
 };
 
 struct fhc_uart_regs {
-/*0x8000*/	u32	_pad0, fhc_uart_imap;	/* FHC UART Interrupt Map	*/
-		u64	_pad1;
-/*0x8010*/	u32	_pad2, fhc_uart_iclr;	/* FHC UART Interrupt Clear	*/
+/*0x8000*/	u32	fhc_uart_imap;		/* FHC UART Interrupt Map	*/
+		u32	_pad[3];
+/*0x8010*/	u32	fhc_uart_iclr;		/* FHC UART Interrupt Clear	*/
 };
 
 struct fhc_tod_regs {
-/*0xa000*/	u32	_pad0, fhc_tod_imap;	/* FHC TOD Interrupt Map	*/
-		u64	_pad1;
-/*0xa010*/	u32	_pad2, fhc_tod_iclr;	/* FHC TOD Interrupt Clear	*/
+/*0xa000*/	u32	fhc_tod_imap;		/* FHC TOD Interrupt Map	*/
+		u32	_pad1[3];
+/*0xa010*/	u32	fhc_tod_iclr;		/* FHC TOD Interrupt Clear	*/
 };
 
 /* All of the above. */
@@ -105,15 +103,19 @@
 #define FHC_RCS_FENAB		0x01000000 /* Fatal errors elicit system reset	*/
 
 /* FHC Board Status Register */
-#define FHC_BSD_DA64		0x00040000 /* Port A: 0=128bit 1=64bit data path */
-#define FHC_BSD_DB64		0x00020000 /* Port B: 0=128bit 1=64bit data path */
-#define FHC_BSD_BID		0x0001e000 /* Board ID                           */
-#define FHC_BSD_SA		0x00001c00 /* Port A UPA Speed (from the pins)   */
-#define FHC_BSD_SB		0x00000380 /* Port B UPA Speed (from the pins)   */
-#define FHC_BSD_NDIAG		0x00000040 /* Not in Diag Mode                   */
-#define FHC_BSD_NTBED		0x00000020 /* Not in TestBED Mode                */
-#define FHC_BSD_NIA		0x0000001c /* Jumper, bit 18 in PROM space       */
-#define FHC_BSD_SI		0x00000001 /* Spare input pin value              */
+#define FHC_BSR_DA64		0x00040000 /* Port A: 0=128bit 1=64bit data path */
+#define FHC_BSR_DB64		0x00020000 /* Port B: 0=128bit 1=64bit data path */
+#define FHC_BSR_BID		0x0001e000 /* Board ID                           */
+#define FHC_BSR_SA		0x00001c00 /* Port A UPA Speed (from the pins)   */
+#define FHC_BSR_SB		0x00000380 /* Port B UPA Speed (from the pins)   */
+#define FHC_BSR_NDIAG		0x00000040 /* Not in Diag Mode                   */
+#define FHC_BSR_NTBED		0x00000020 /* Not in TestBED Mode                */
+#define FHC_BSR_NIA		0x0000001c /* Jumper, bit 18 in PROM space       */
+#define FHC_BSR_SI		0x00000001 /* Spare input pin value              */
+
+/* FHC JTAG Control Register */
+#define FHC_JTAG_CTRL_MENAB	0x80000000 /* Indicates this is JTAG Master	 */
+#define FHC_JTAG_CTRL_MNONE	0x40000000 /* Indicates no JTAG Master present	 */
 
 /* FHC then has an Interrupt Group Number register, essentially this is a 32-bit
  * register with the low 5 bits specifying the IGN of this FHC for interrupt
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/floppy.h linux/include/asm-sparc64/floppy.h
--- v2.2.3/linux/include/asm-sparc64/floppy.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/floppy.h	Sun Mar 21 07:23:38 1999
@@ -1,4 +1,4 @@
-/* $Id: floppy.h,v 1.16 1998/10/06 20:32:15 ecd Exp $
+/* $Id: floppy.h,v 1.18 1999/03/21 10:51:38 davem Exp $
  * asm-sparc64/floppy.h: Sparc specific parts of the Floppy driver.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -114,6 +114,7 @@
 
 static unsigned char sun_82077_fd_inb(unsigned long port)
 {
+	udelay(5);
 	switch(port & 7) {
 	default:
 		printk("floppy: Asked to read unknown port %lx\n", port);
@@ -131,6 +132,7 @@
 
 static void sun_82077_fd_outb(unsigned char value, unsigned long port)
 {
+	udelay(5);
 	switch(port & 7) {
 	default:
 		printk("floppy: Asked to write to unknown port %lx\n", port);
@@ -702,7 +704,7 @@
         sun_fdops.fd_inb = sun_82077_fd_inb;
         sun_fdops.fd_outb = sun_82077_fd_outb;
 
-	use_virtual_dma = 1;
+	can_use_virtual_dma = use_virtual_dma = 1;
 	sun_fdops.fd_enable_dma = sun_fd_enable_dma;
 	sun_fdops.fd_disable_dma = sun_fd_disable_dma;
 	sun_fdops.fd_set_dma_mode = sun_fd_set_dma_mode;
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/init.h linux/include/asm-sparc64/init.h
--- v2.2.3/linux/include/asm-sparc64/init.h	Wed Apr 23 19:01:28 1997
+++ linux/include/asm-sparc64/init.h	Wed Mar 10 16:53:38 1999
@@ -11,4 +11,6 @@
 #define __FINIT	.previous
 #define __INITDATA	.section	".data.init",#alloc,#write
 
+#define __cacheline_aligned __attribute__ ((aligned (64)))
+
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/iommu.h linux/include/asm-sparc64/iommu.h
--- v2.2.3/linux/include/asm-sparc64/iommu.h	Sat Aug 16 09:51:10 1997
+++ linux/include/asm-sparc64/iommu.h	Wed Mar 10 16:53:38 1999
@@ -38,22 +38,17 @@
 #define IOPTE_VALID         0x8000000000000000 /* IOPTE is valid                   */
 #define IOPTE_64K           0x2000000000000000 /* IOPTE is for 64k page            */
 #define IOPTE_STBUF         0x1000000000000000 /* DVMA can use streaming buffer    */
-#define IOPTE_INTRA         0x0800000000000000 /* XXX what does this thing do?     */
+#define IOPTE_INTRA         0x0800000000000000 /* SBUS slot-->slot direct transfer */
 #define IOPTE_PAGE          0x000001ffffffe000 /* Physical page number (PA[40:13]) */
 #define IOPTE_CACHE         0x0000000000000010 /* Cached (in UPA E-cache)          */
 #define IOPTE_WRITE         0x0000000000000002 /* Writeable                        */
 
 struct iommu_struct {
 	struct sysio_regs	*sysio_regs;
-	unsigned int		*sbuf_flushflag_va;
-	unsigned long		sbuf_flushflag_pa;
-	spinlock_t		iommu_lock;
-
 	iopte_t			*page_table;
-
-	/* For convenience */
-	unsigned long start; /* First managed virtual address */
-	unsigned long end;   /* Last managed virtual address */
+	volatile unsigned int	flushflag;
+	unsigned int		strbuf_enabled;
+	spinlock_t		iommu_lock;
 };
 
 #endif /* !(_SPARC_IOMMU_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/irq.h linux/include/asm-sparc64/irq.h
--- v2.2.3/linux/include/asm-sparc64/irq.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc64/irq.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: irq.h,v 1.10 1998/05/29 06:00:39 ecd Exp $
+/* $Id: irq.h,v 1.14 1998/12/19 11:05:41 davem Exp $
  * irq.h: IRQ registers on the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -15,23 +15,71 @@
 	int dummy;
 };
 
-/* You should not mess with this directly. That's the job of irq.c. */
+/* You should not mess with this directly. That's the job of irq.c.
+ *
+ * If you make changes here, please update hand coded assembler of
+ * SBUS/floppy interrupt handler in entry.S -DaveM
+ *
+ * This is currently one DCACHE line, two buckets per L2 cache
+ * line.  Keep this in mind please.
+ */
 struct ino_bucket {
-	unsigned short ino;
-	short imap_off;
-	unsigned short pil;
-	unsigned short flags;
-	unsigned int *iclr;
+	/* Next handler in per-CPU PIL worklist.  We know that
+	 * bucket pointers have the high 32-bits clear, so to
+	 * save space we only store the bits we need.
+	 */
+/*0x00*/unsigned int irq_chain;
+
+	/* PIL to schedule this IVEC at. */
+/*0x04*/unsigned char pil;
+
+	/* If an IVEC arrives while irq_info is NULL, we
+	 * set this to notify request_irq() about the event.
+	 */
+/*0x05*/unsigned char pending;
+
+	/* Miscellaneous flags. */
+/*0x06*/unsigned char flags;
+
+	/* Unused right now, but we will use it for proper
+	 * enable_irq()/disable_irq() nesting.
+	 */
+/*0x07*/unsigned char __unused;
+
+	/* Reference to handler for this IRQ.  If this is
+	 * non-NULL this means it is active and should be
+	 * serviced.  Else the pending member is set to one
+	 * and later registry of the interrupt checks for
+	 * this condition.
+	 *
+	 * Normally this is just an irq_action structure.
+	 * But, on PCI, if multiple interrupt sources behind
+	 * a bridge have multiple interrupt sources that share
+	 * the same INO bucket, this points to an array of
+	 * pointers to four IRQ action structures.
+	 */
+/*0x08*/void *irq_info;
+
+	/* Sun5 Interrupt Clear Register. */
+/*0x10*/unsigned int *iclr;
+
+	/* Sun5 Interrupt Mapping Register. */
+/*0x18*/unsigned int *imap;
+
 };
 
-#define __irq_ino(irq) ((struct ino_bucket *)(unsigned long)(irq))->ino
+#define NUM_IVECS	8192
+extern struct ino_bucket ivector_table[NUM_IVECS];
+
+#define __irq_ino(irq) \
+        (((struct ino_bucket *)(unsigned long)(irq)) - &ivector_table[0])
 #define __irq_pil(irq) ((struct ino_bucket *)(unsigned long)(irq))->pil
 
 static __inline__ char *__irq_itoa(unsigned int irq)
 {
 	static char buff[16];
 
-	sprintf(buff, "%d,%x", __irq_pil(irq), __irq_ino(irq));
+	sprintf(buff, "%d,%x", __irq_pil(irq), (unsigned int)__irq_ino(irq));
 	return buff;
 }
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/mmu_context.h linux/include/asm-sparc64/mmu_context.h
--- v2.2.3/linux/include/asm-sparc64/mmu_context.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/mmu_context.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: mmu_context.h,v 1.32 1998/10/13 14:03:52 davem Exp $ */
+/* $Id: mmu_context.h,v 1.34 1999/01/11 13:45:44 davem Exp $ */
 #ifndef __SPARC64_MMU_CONTEXT_H
 #define __SPARC64_MMU_CONTEXT_H
 
@@ -89,7 +89,7 @@
 	paddr = __pa(mm->pgd);
 	if((tsk->tss.flags & (SPARC_FLAG_32BIT|SPARC_FLAG_KTHREAD)) ==
 	   (SPARC_FLAG_32BIT))
-		pgd_cache = (unsigned long) mm->pgd[0];
+		pgd_cache = ((unsigned long) mm->pgd[0]) << 11UL;
 	else
 		pgd_cache = 0;
 	__asm__ __volatile__("
@@ -115,13 +115,21 @@
 
 /*
  * After we have set current->mm to a new value, this activates
- * the context for the new mm so we see the new mappings.
+ * the context for the new mm so we see the new mappings.  Currently,
+ * this is always called for 'current', if that changes put appropriate
+ * checks here.
+ *
+ * We set the cpu_vm_mask first to zero to enforce a tlb flush for
+ * the new context above, then we set it to the current cpu so the
+ * smp tlb flush routines do not get confused.
  */
 #define activate_context(__tsk)		\
 do {	flushw_user();			\
+	(__tsk)->mm->cpu_vm_mask = 0;	\
 	spin_lock(&scheduler_lock);	\
 	__get_mmu_context(__tsk);	\
 	spin_unlock(&scheduler_lock);	\
+	(__tsk)->mm->cpu_vm_mask = (1UL<<smp_processor_id()); \
 } while(0)
 
 #endif /* !(__ASSEMBLY__) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/oplib.h linux/include/asm-sparc64/oplib.h
--- v2.2.3/linux/include/asm-sparc64/oplib.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/oplib.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.9 1998/10/06 20:56:05 ecd Exp $
+/* $Id: oplib.h,v 1.10 1998/12/18 10:02:03 davem Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -175,20 +175,52 @@
 extern enum prom_output_device prom_query_output_device(void);
 
 /* Multiprocessor operations... */
-
+#ifdef __SMP__
 /* Start the CPU with the given device tree node, context table, and context
  * at the passed program counter.
  */
 extern void prom_startcpu(int cpunode, unsigned long pc, unsigned long o0);
 
-/* Stop the CPU with the passed device tree node. */
-extern int prom_stopcpu(int cpunode);
+/* Stop the current CPU. */
+extern void prom_stopself(void);
+
+/* Idle the current CPU. */
+extern void prom_idleself(void);
+
+/* Resume the CPU with the passed device tree node. */
+extern void prom_resumecpu(int cpunode);
+#endif
+
+/* Power management interfaces. */
+
+/* Put the current CPU to sleep. */
+extern void prom_sleepself(void);
+
+/* Put the entire system to sleep. */
+extern int prom_sleepsystem(void);
+
+/* Initiate a wakeup event. */
+extern int prom_wakeupsystem(void);
+
+/* MMU and memory related OBP interfaces. */
+
+/* Get unique string identifying SIMM at given physical address. */
+extern int prom_getunumber(unsigned long phys_lo, unsigned long phys_hi,
+			   char *buf, int buflen);
+
+/* Retain physical memory to the caller across soft resets. */
+extern unsigned long prom_retain(char *name,
+				 unsigned long pa_low, unsigned long pa_high,
+				 long size, long align);
 
-/* Idle the CPU with the passed device tree node. */
-extern int prom_idlecpu(int cpunode);
+/* Load explicit I/D TLB entries into the calling processor. */
+extern long prom_itlb_load(unsigned long index,
+			   unsigned long tte_data,
+			   unsigned long vaddr);
 
-/* Re-Start the CPU with the passed device tree node. */
-extern int prom_restartcpu(int cpunode);
+extern long prom_dtlb_load(unsigned long index,
+			   unsigned long tte_data,
+			   unsigned long vaddr);
 
 /* PROM device tree traversal functions... */
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/pbm.h linux/include/asm-sparc64/pbm.h
--- v2.2.3/linux/include/asm-sparc64/pbm.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc64/pbm.h	Mon Mar 15 16:10:44 1999
@@ -1,4 +1,4 @@
-/* $Id: pbm.h,v 1.14 1998/05/29 06:00:40 ecd Exp $
+/* $Id: pbm.h,v 1.16 1999/03/14 18:13:03 davem Exp $
  * pbm.h: U2P PCI bus module pseudo driver software state.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -30,15 +30,21 @@
 
 struct linux_psycho;
 
+/* There can be quite a few ranges and interrupt maps on a PCI
+ * segment.  Thus...
+ */
+#define PROM_PCIRNG_MAX		64
+#define PROM_PCIIMAP_MAX	64
+
 struct linux_pbm_info {
 	struct linux_psycho		*parent;
 	struct pci_vma			*IO_assignments;
 	struct pci_vma			*MEM_assignments;
 	int				prom_node;
 	char				prom_name[64];
-	struct linux_prom_pci_ranges	pbm_ranges[PROMREG_MAX];
+	struct linux_prom_pci_ranges	pbm_ranges[PROM_PCIRNG_MAX];
 	int				num_pbm_ranges;
-	struct linux_prom_pci_intmap	pbm_intmap[PROMREG_MAX];
+	struct linux_prom_pci_intmap	pbm_intmap[PROM_PCIIMAP_MAX];
 	int				num_pbm_intmap;
 	struct linux_prom_pci_intmask	pbm_intmask;
 
@@ -63,6 +69,8 @@
 	unsigned int			pci_first_busno;
 	unsigned int			pci_last_busno;
 	struct pci_bus			*pci_bus;
+
+	void				*starfire_cookie;
 };
 
 /* PCI devices which are not bridges have this placed in their pci_dev
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.2.3/linux/include/asm-sparc64/pgtable.h	Wed Jan 20 23:14:06 1999
+++ linux/include/asm-sparc64/pgtable.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.96 1998/10/27 23:28:42 davem Exp $
+/* $Id: pgtable.h,v 1.102 1999/01/22 16:19:29 jj Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -14,6 +14,7 @@
 
 #ifndef __ASSEMBLY__
 #include <linux/mm.h>
+#include <linux/pagemap.h>
 #endif
 #include <asm/spitfire.h>
 #include <asm/asi.h>
@@ -106,8 +107,9 @@
 
 #define PAGE_NONE	__pgprot (_PAGE_PRESENT | _PAGE_ACCESSED)
 
+/* Don't set the TTE _PAGE_W bit here, else the dirty bit never gets set. */
 #define PAGE_SHARED	__pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
-				  __ACCESS_BITS | _PAGE_W | _PAGE_WRITE)
+				  __ACCESS_BITS | _PAGE_WRITE)
 
 #define PAGE_COPY	__pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
 				  __ACCESS_BITS)
@@ -250,11 +252,13 @@
 #define mk_pte_phys(physpage, pgprot)	(__pte((physpage) | pgprot_val(pgprot)))
 #define pte_modify(_pte, newprot) \
 	(pte_val(_pte) = ((pte_val(_pte) & _PAGE_CHG_MASK) | pgprot_val(newprot)))
-#define pmd_set(pmdp, ptep)		(pmd_val(*(pmdp)) = __pa((unsigned long) (ptep)))
-#define pgd_set(pgdp, pmdp)		(pgd_val(*(pgdp)) = __pa((unsigned long) (pmdp)))
+#define pmd_set(pmdp, ptep)	\
+	(pmd_val(*(pmdp)) = (__pa((unsigned long) (ptep)) >> 11UL))
+#define pgd_set(pgdp, pmdp)	\
+	(pgd_val(*(pgdp)) = (__pa((unsigned long) (pmdp)) >> 11UL))
 #define pte_page(pte)   ((unsigned long) __va(((pte_val(pte)&~PAGE_OFFSET)&~(0xfffUL))))
-#define pmd_page(pmd)			((unsigned long) __va(pmd_val(pmd)))
-#define pgd_page(pgd)			((unsigned long) __va(pgd_val(pgd)))
+#define pmd_page(pmd)			((unsigned long) __va((pmd_val(pmd)<<11UL)))
+#define pgd_page(pgd)			((unsigned long) __va((pgd_val(pgd)<<11UL)))
 #define pte_none(pte) 			(!pte_val(pte))
 #define pte_present(pte)		(pte_val(pte) & _PAGE_PRESENT)
 #define pte_clear(pte)			(pte_val(*(pte)) = 0UL)
@@ -371,7 +375,7 @@
 		(unsigned long)ret->pprev_hash = mask;
 		if (!mask)
 			pgd_quicklist = (unsigned long *)ret->next_hash;
-                ret = (struct page *) (page_address(ret) + off);
+                ret = (struct page *)(page_address(ret) + off);
                 pgd_cache_size--;
         } else {
 		ret = (struct page *) __get_free_page(GFP_KERNEL);
@@ -519,7 +523,7 @@
 
 extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir)
 {
-	if(pgdir != swapper_pg_dir && tsk->mm == current->mm) {
+	if(pgdir != swapper_pg_dir && tsk == current) {
 		register unsigned long paddr asm("o5");
 
 		paddr = __pa(pgdir);
@@ -642,9 +646,11 @@
 
 extern void * module_map (unsigned long size);
 extern void module_unmap (void *addr);
+extern unsigned long *sparc64_valid_addr_bitmap;
 
 /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
 #define PageSkip(page)		(test_bit(PG_skip, &(page)->flags))
+#define kern_addr_valid(addr)	(test_bit(__pa((unsigned long)(addr))>>22, sparc64_valid_addr_bitmap))
 
 extern int io_remap_page_range(unsigned long from, unsigned long offset,
 			       unsigned long size, pgprot_t prot, int space);
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/posix_types.h linux/include/asm-sparc64/posix_types.h
--- v2.2.3/linux/include/asm-sparc64/posix_types.h	Mon Oct  5 13:13:44 1998
+++ linux/include/asm-sparc64/posix_types.h	Wed Mar 10 16:53:38 1999
@@ -38,7 +38,11 @@
 #endif
 
 typedef struct {
+#if defined(__KERNEL__) || defined(__USE_ALL)
 	int	val[2];
+#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */
+	int     __val[2];
+#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */
 } __kernel_fsid_t;
 
 /* Now 32bit compatibility types */
@@ -62,6 +66,8 @@
 typedef long		       __kernel_loff_t32;
 typedef __kernel_fsid_t        __kernel_fsid_t32;
 
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+
 #undef __FD_SET
 static __inline__ void __FD_SET(unsigned long fd, __kernel_fd_set *fdsetp)
 {
@@ -130,5 +136,7 @@
 		tmp++;
 	}
 }
+
+#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */
 
 #endif /* !(__ARCH_SPARC64_POSIX_TYPES_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.2.3/linux/include/asm-sparc64/processor.h	Tue Jan 19 11:32:53 1999
+++ linux/include/asm-sparc64/processor.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.51 1998/10/21 03:21:19 davem Exp $
+/* $Id: processor.h,v 1.53 1999/01/19 07:57:51 davem Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -82,7 +82,7 @@
 #define SPARC_FLAG_PERFCTR	0x200    /* task has performance counters active */
 
 #define INIT_MMAP { &init_mm, 0xfffff80000000000, 0xfffff80001000000, \
-		    PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
+		    NULL, PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  {						\
 /* ksp, wstate, cwp, flags,              ctx, */ 		\
@@ -200,7 +200,8 @@
 	: \
 	: "r" (regs), "r" (sp - REGWIN32_SZ), \
 	  "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0])), \
-	  "r" (current->mm->pgd[0]), "r" (TSB_REG), "i" (ASI_DMMU)); \
+	  "r" (((unsigned long)current->mm->pgd[0])<<11UL), \
+	  "r" (TSB_REG), "i" (ASI_DMMU)); \
 } while(0)
 
 /* Free all resources held by a thread. */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/psycho.h linux/include/asm-sparc64/psycho.h
--- v2.2.3/linux/include/asm-sparc64/psycho.h	Thu Apr 23 20:21:38 1998
+++ linux/include/asm-sparc64/psycho.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: psycho.h,v 1.3 1998/03/15 13:24:28 ecd Exp $
+/* $Id: psycho.h,v 1.4 1998/12/23 10:08:16 davem Exp $
  * psycho.h: UltraSparc AX specific PCI definitions.
  *
  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -263,14 +263,43 @@
 #define PSYCHO_CEAFSR_BLK	0x0000000000800000 /* Trans was block operation    */
 #define PSYCHO_CEAFSR_RESV2	0x00000000007fffff /* Reserved                     */
 
-/* DMA Scoreboard Diagnostic Register(s) */
-#define PSYCHO_DSCORE_VALID	0x8000000000000000 /* Entry is valid               */
-#define PSYCHO_DSCORE_C		0x4000000000000000 /* Transaction cacheable        */
-#define PSYCHO_DSCORE_READ	0x2000000000000000 /* Transaction was a read       */
-#define PSYCHO_DSCORE_TAG	0x1f00000000000000 /* Transaction ID               */
-#define PSYCHO_DSCORE_ADDR	0x00fffffffff80000 /* Transaction PADDR            */
-#define PSYCHO_DSCORE_BMSK	0x000000000007fff8 /* Bytemask of pending transfer */
-#define PSYCHO_DSCORE_SRC	0x0000000000000007 /* Transaction source           */
+/* PSYCHO Performance Monitor Register, the counter holds 2 32-bit event counters. */
+#define PSYCHO_PMCTRL_RESV3	0xffffffffffff0000 /* Reserved                     */
+#define PSYCHO_PMCTRL_CLR1	0x0000000000008000 /* Clear SEL1 counter           */
+#define PSYCHO_PMCTRL_RESV2	0x0000000000006000 /* Reserved                     */
+#define PSYCHO_PMCTRL_SEL1	0x0000000000001f00 /* Event source 1               */
+#define PSYCHO_PMCTRL_CLR0	0x0000000000000080 /* Clear SEL0 counter           */
+#define PSYCHO_PMCTRL_RESV1	0x0000000000000060 /* Reserved                     */
+#define PSYCHO_PMCTRL_SEL0	0x000000000000001f /* Event source 0               */
+
+/* SEL0/SEL1 each can take any one of the following values. */
+#define PMCTRL_SEL_NSDRA	0x00  /* # of streaming dvma reads for PCI A       */
+#define PMCTRL_SEL_NSDWA	0x01  /* # of streaming dvma writes for PCI A      */
+#define PMCTRL_SEL_NCDRA	0x02  /* # of consistent dvma reads for PCI A      */
+#define PMCTRL_SEL_NCDWA	0x03  /* # of consistent dvma writes for PCI A     */
+#define PMCTRL_SEL_SBMA		0x04  /* # of streaming buffer misses on PCI A     */
+#define PMCTRL_SEL_DCGA		0x05  /* # of DVMA granted cycles on PCI A         */
+#define PMCTRL_SEL_DWTA		0x06  /* # of DVMA words transferred on PCI A      */
+#define PMCTRL_SEL_CPIOA	0x07  /* # of PIO cycles used by PSYCHO on PCI A   */
+#define PMCTRL_SEL_NSDRB	0x08  /* # of streaming dvma reads for PCI B       */
+#define PMCTRL_SEL_NSDWB	0x09  /* # of streaming dvma writes for PCI B      */
+#define PMCTRL_SEL_NCDRB	0x0a  /* # of consistent dvma reads for PCI B      */
+#define PMCTRL_SEL_NCDWB	0x0b  /* # of consistent dvma writes for PCI B     */
+#define PMCTRL_SEL_SBMB		0x0c  /* # of streaming buffer misses on PCI B     */
+#define PMCTRL_SEL_DCGB		0x0d  /* # of DVMA granted cycles on PCI B         */
+#define PMCTRL_SEL_DWTB		0x0e  /* # of DVMA words transferred on PCI B      */
+#define PMCTRL_SEL_CPIOB	0x0f  /* # of PIO cycles used by PSYCHO on PCI B   */
+#define PMCTRL_SEL_TMISS	0x10  /* # of IOMMU TLB misses                     */
+#define PMCTRL_SEL_IRQ		0x11  /* # of interrupts                           */
+#define PMCTRL_SEL_INACK	0x12  /* # of interrupt NACKs on UPA               */
+#define PMCTRL_SEL_PRD		0x13  /* # of PIO reads                            */
+#define PMCTRL_SEL_PWR		0x14  /* # of PIO writes                           */
+#define PMCTRL_SEL_MBT		0x15  /* # of Merge Buffer transactions            */
+#define PMCTRL_SEL_PDRTA	0x16  /* # of PCI A DVMA tablewalk induced retries */
+#define PMCTRL_SEL_PDRSA	0x17  /* # of PCI A DVMA str buf induced retries   */
+#define PMCTRL_SEL_PDRTB	0x18  /* # of PCI B DVMA tablewalk induced retries */
+#define PMCTRL_SEL_PDRSB	0x19  /* # of PCI B DVMA str buf induced retries   */
+/* Values 0x1a --> 0x1f are reserved. */
 
 /* PSYCHO PCI Control Register */
 #define PSYCHO_PCICTRL_RESV1	0xfffffff000000000 /* Reserved                     */
@@ -302,6 +331,16 @@
 #define PSYCHO_PCIAFSR_MID	0x000000003e000000 /* MID causing the error        */
 #define PSYCHO_PCIAFSR_RESV3	0x0000000001ffffff /* Reserved                     */
 
+/* PSYCHO Diagnostic Register. */
+#define PSYCHO_PSYDIAG_RESV	0xffffffffffffff80 /* Reserved                     */
+#define PSYCHO_PCIDIAG_DRETRY	0x0000000000000040 /* Disable retry limit          */
+#define PSYCHO_PCIDIAG_DISYNC	0x0000000000000020 /* Disable DMA wr / irq sync    */
+#define PSYCHO_PCIDIAG_DDWSYNC	0x0000000000000010 /* Disable DMA wr / PIO rd sync */
+#define PSYCHO_PCIDIAG_IDDPAR	0x0000000000000008 /* Invert DMA data parity       */
+#define PSYCHO_PCIDIAG_IPDPAR	0x0000000000000004 /* Invert PIO data parity       */
+#define PSYCHO_PCIDIAG_IPAPAR	0x0000000000000002 /* Invert PIO address parity    */
+#define PSYCHO_PCIDIAG_LPBACK	0x0000000000000001 /* Enable loopback mode         */
+
 /* IOMMU things defined fully in asm-sparc64/iommu.h */
 
 /* Streaming Buffer Control Register */
@@ -319,6 +358,15 @@
 /* Streaming Buffer Flush Synchronization Register */
 #define PSYCHO_SBUFSYNC_ADDR	0x000001ffffffffc0 /* Physical address to update   */
 #define PSYCHO_SBUFSYNC_RESV	0x000000000000003f /* Ignored bits                 */
+
+/* DMA Scoreboard Diagnostic Register(s) */
+#define PSYCHO_DSCORE_VALID	0x8000000000000000 /* Entry is valid               */
+#define PSYCHO_DSCORE_C		0x4000000000000000 /* Transaction cacheable        */
+#define PSYCHO_DSCORE_READ	0x2000000000000000 /* Transaction was a read       */
+#define PSYCHO_DSCORE_TAG	0x1f00000000000000 /* Transaction ID               */
+#define PSYCHO_DSCORE_ADDR	0x00fffffffff80000 /* Transaction PADDR            */
+#define PSYCHO_DSCORE_BMSK	0x000000000007fff8 /* Bytemask of pending transfer */
+#define PSYCHO_DSCORE_SRC	0x0000000000000007 /* Transaction source           */
 
 /* PSYCHO Interrupt mapping register(s). */
 #define PSYCHO_IMAP_RESV1	0xffffffff00000000 /* Reserved                     */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/resource.h linux/include/asm-sparc64/resource.h
--- v2.2.3/linux/include/asm-sparc64/resource.h	Thu Jun 26 12:33:40 1997
+++ linux/include/asm-sparc64/resource.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: resource.h,v 1.3 1997/06/14 17:35:09 davem Exp $
+/* $Id: resource.h,v 1.4 1998/11/19 20:01:49 davem Exp $
  * resource.h: Resource definitions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -28,7 +28,7 @@
 #define INIT_RLIMITS							\
 {									\
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
-    {LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM},				\
+    {LONG_MAX, LONG_MAX}, {_STK_LIM, LONG_MAX},				\
     {       0, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
     {NR_OPEN, NR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}                          \
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/sbus.h linux/include/asm-sparc64/sbus.h
--- v2.2.3/linux/include/asm-sparc64/sbus.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc64/sbus.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.7 1998/05/22 14:33:40 jj Exp $
+/* $Id: sbus.h,v 1.10 1998/12/16 04:33:58 davem Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -74,6 +74,8 @@
 	int clock_freq;
 	struct linux_prom_ranges sbus_ranges[PROMREG_MAX];
 	int num_sbus_ranges;
+	int upaid;
+	void *starfire_cookie;
 };
 
 extern struct linux_sbus *SBus_chain;
@@ -87,6 +89,16 @@
         
 #define for_all_sbusdev(device, bus) \
 	for((bus) = SBus_chain, ((device) = (bus) ? (bus)->devices : 0); (bus); (device)=((device)->next ? (device)->next : ((bus) = (bus)->next, (bus) ? (bus)->devices : 0)))
+
+extern void mmu_set_sbus64(struct linux_sbus_device *, int);
+
+/* If you did not get the buffer from mmu_get_*() or sparc_alloc_dvma()
+ * then you must use this to get the 32-bit SBUS dvma address.
+ * And in this case it is your responsibility to make sure the buffer
+ * is GFP_DMA, ie. that it is not greater than MAX_DMA_ADDRESS.
+ */
+extern unsigned long phys_base;
+#define sbus_dvma_addr(__addr)	((__u32)(__pa(__addr) - phys_base))
 
 /* Apply promlib probed SBUS ranges to registers. */
 extern void prom_apply_sbus_ranges(struct linux_sbus *sbus, 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/scatterlist.h linux/include/asm-sparc64/scatterlist.h
--- v2.2.3/linux/include/asm-sparc64/scatterlist.h	Mon Apr 14 16:28:24 1997
+++ linux/include/asm-sparc64/scatterlist.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: scatterlist.h,v 1.4 1997/04/10 05:13:32 davem Exp $ */
+/* $Id: scatterlist.h,v 1.6 1998/12/16 04:33:57 davem Exp $ */
 #ifndef _SPARC64_SCATTERLIST_H
 #define _SPARC64_SCATTERLIST_H
 
@@ -13,6 +13,7 @@
     __u32 dvma_address; /* A place to hang host-specific addresses at. */
 };
 
-#define ISA_DMA_THRESHOLD ((0xf0000000) + PAGE_OFFSET)
+extern unsigned long phys_base;
+#define ISA_DMA_THRESHOLD (phys_base + (0xfe000000UL) + PAGE_OFFSET)
 
 #endif /* !(_SPARC64_SCATTERLIST_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/semaphore-helper.h linux/include/asm-sparc64/semaphore-helper.h
--- v2.2.3/linux/include/asm-sparc64/semaphore-helper.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/semaphore-helper.h	Wed Mar 10 16:53:38 1999
@@ -0,0 +1,68 @@
+#ifndef _SPARC64_SEMAPHORE_HELPER_H
+#define _SPARC64_SEMAPHORE_HELPER_H
+
+/*
+ * SMP- and interrupt-safe semaphore helper functions, sparc64 version.
+ *
+ * (C) Copyright 1999 David S. Miller (davem@redhat.com)
+ * (C) Copyright 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+#define wake_one_more(__sem)      atomic_inc(&((__sem)->waking));
+#define waking_non_zero(__sem)				\
+({	int __ret;					\
+	__asm__ __volatile__(				\
+"1:	ldsw		[%1], %%g5\n\t"			\
+	"brlez,pt	%%g5, 2f\n\t"			\
+	" mov		0, %0\n\t"			\
+	"sub		%%g5, 1, %%g7\n\t"		\
+	"cas		[%1], %%g5, %%g7\n\t"		\
+	"cmp		%%g5, %%g7\n\t"			\
+	"bne,pn		%%icc, 1b\n\t"			\
+	" mov		1, %0\n"			\
+"2:"	: "=&r" (__ret)					\
+	: "r" (&((__sem)->waking))			\
+	: "g5", "g7", "cc", "memory");			\
+	__ret;						\
+})
+
+#define waking_non_zero_interruptible(__sem, __tsk)	\
+({	int __ret;					\
+	__asm__ __volatile__(				\
+"1:	ldsw		[%1], %%g5\n\t"			\
+	"brlez,pt	%%g5, 2f\n\t"			\
+	" mov		0, %0\n\t"			\
+	"sub		%%g5, 1, %%g7\n\t"		\
+	"cas		[%1], %%g5, %%g7\n\t"		\
+	"cmp		%%g5, %%g7\n\t"			\
+	"bne,pn		%%icc, 1b\n\t"			\
+	" mov		1, %0\n"			\
+"2:"	: "=&r" (__ret)					\
+	: "r" (&((__sem)->waking))			\
+	: "g5", "g7", "cc", "memory");			\
+	if(__ret == 0 && signal_pending(__tsk)) {	\
+		atomic_inc(&((__sem)->count));		\
+		__ret = -EINTR;				\
+	}						\
+	__ret;						\
+})
+
+#define waking_non_zero_trylock(__sem)			\
+({	int __ret;					\
+	__asm__ __volatile__(				\
+"1:	ldsw		[%1], %%g5\n\t"			\
+	"brlez,pt	%%g5, 2f\n\t"			\
+	" mov		1, %0\n\t"			\
+	"sub		%%g5, 1, %%g7\n\t"		\
+	"cas		[%1], %%g5, %%g7\n\t"		\
+	"cmp		%%g5, %%g7\n\t"			\
+	"bne,pn		%%icc, 1b\n\t"			\
+	" mov		0, %0\n"			\
+"2:"	: "=&r" (__ret)					\
+	: "r" (&((__sem)->waking))			\
+	: "g5", "g7", "cc", "memory");			\
+	if(__ret == 1)					\
+		atomic_inc(&((__sem)->count));		\
+	__ret;						\
+})
+
+#endif /* !(_SPARC64_SEMAPHORE_HELPER_H) */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/semaphore.h linux/include/asm-sparc64/semaphore.h
--- v2.2.3/linux/include/asm-sparc64/semaphore.h	Tue Jan 19 11:32:53 1999
+++ linux/include/asm-sparc64/semaphore.h	Wed Mar 10 16:53:38 1999
@@ -18,57 +18,135 @@
 
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
+extern int  __down_trylock(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
 #define sema_init(sem, val)	atomic_set(&((sem)->count), val)
 
-#define wake_one_more(sem)      atomic_inc(&sem->waking);
-
-static __inline__ int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
-{
-	int ret;
-
-	__asm__ __volatile__("
-1:	ldsw		[%1], %%g5
-	brlez,pt	%%g5, 2f
-	 mov		0, %0
-	sub		%%g5, 1, %%g7
-	cas		[%1], %%g5, %%g7
-	cmp		%%g5, %%g7
-	bne,pn		%%icc, 1b
-	 mov		1, %0
-2:"	: "=r" (ret)
-	: "r" (&((sem)->waking))
-	: "g5", "g7", "cc", "memory");
-	return ret;
-}
-
 extern __inline__ void down(struct semaphore * sem)
 {
-	int result;
-
-	result = atomic_dec_return(&sem->count);
-	membar("#StoreLoad | #StoreStore");
-	if (result < 0)
-		__down(sem);
+        __asm__ __volatile__("
+	1:	lduw	[%0], %%g5
+		sub	%%g5, 1, %%g7
+		cas	[%0], %%g5, %%g7
+		cmp	%%g5, %%g7
+		bne,pn	%%icc, 1b
+		 cmp	%%g7, 1
+		bl,pn	%%icc, 3f
+		 membar	#StoreStore
+	2:
+		.subsection 2
+	3:	mov	%0, %%g5
+		save	%%sp, -160, %%sp
+		mov	%%g1, %%l1
+		mov	%%g2, %%l2
+		mov	%%g3, %%l3
+		call	%1
+		 mov	%%g5, %%o0
+		mov	%%l1, %%g1
+		mov	%%l2, %%g2
+		ba,pt	%%xcc, 2b
+		 restore %%l3, %%g0, %%g3
+		.previous\n"
+	: : "r" (__atomic_fool_gcc(sem)), "i" (__down)
+	: "g5", "g7", "memory", "cc");
 }
 
 extern __inline__ int down_interruptible(struct semaphore *sem)
 {
-	int result, ret = 0;
+	int ret = 0;
+	
+        __asm__ __volatile__("
+	1:	lduw	[%2], %%g5
+		sub	%%g5, 1, %%g7
+		cas	[%2], %%g5, %%g7
+		cmp	%%g5, %%g7
+		bne,pn	%%icc, 1b
+		 cmp	%%g7, 1
+		bl,pn	%%icc, 3f
+		 membar	#StoreStore
+	2:
+		.subsection 2
+	3:	mov	%2, %%g5
+		save	%%sp, -160, %%sp
+		mov	%%g1, %%l1
+		mov	%%g2, %%l2
+		mov	%%g3, %%l3
+		call	%3
+		 mov	%%g5, %%o0
+		mov	%%l1, %%g1
+		mov	%%l2, %%g2
+		mov	%%l3, %%g3
+		ba,pt	%%xcc, 2b
+		 restore %%o0, %%g0, %0
+		.previous\n"
+	: "=r" (ret)
+	: "0" (ret), "r" (__atomic_fool_gcc(sem)), "i" (__down_interruptible)
+	: "g5", "g7", "memory", "cc");
+	return ret;
+}
 
-	result = atomic_dec_return(&sem->count);
-	membar("#StoreLoad | #StoreStore");
-	if (result < 0)
-		ret = __down_interruptible(sem);
+extern inline int down_trylock(struct semaphore *sem)
+{
+	int ret = 0;
+        __asm__ __volatile__("
+	1:	lduw	[%2], %%g5
+		sub	%%g5, 1, %%g7
+		cas	[%2], %%g5, %%g7
+		cmp	%%g5, %%g7
+		bne,pn	%%icc, 1b
+		 cmp	%%g7, 1
+		bl,pn	%%icc, 3f
+		 membar	#StoreStore
+	2:
+		.subsection 2
+	3:	mov	%2, %%g5
+		save	%%sp, -160, %%sp
+		mov	%%g1, %%l1
+		mov	%%g2, %%l2
+		mov	%%g3, %%l3
+		call	%3
+		 mov	%%g5, %%o0
+		mov	%%l1, %%g1
+		mov	%%l2, %%g2
+		mov	%%l3, %%g3
+		ba,pt	%%xcc, 2b
+		 restore %%o0, %%g0, %0
+		.previous\n"
+	: "=r" (ret)
+	: "0" (ret), "r" (__atomic_fool_gcc(sem)), "i" (__down_trylock)
+	: "g5", "g7", "memory", "cc");
 	return ret;
 }
 
 extern __inline__ void up(struct semaphore * sem)
 {
-	membar("#StoreStore | #LoadStore");
-	if (atomic_inc_return(&sem->count) <= 0)
-		__up(sem);
+	__asm__ __volatile__("
+		membar	#StoreLoad | #LoadLoad
+	1:	lduw	[%0], %%g5
+		add	%%g5, 1, %%g7
+		cas	[%0], %%g5, %%g7
+		cmp	%%g5, %%g7
+		bne,pn	%%icc, 1b
+		 addcc	%%g7, 1, %%g0
+		ble,pn	%%icc, 3f
+		 nop
+	2:
+		.subsection 2
+	3:	mov	%0, %%g5
+		save	%%sp, -160, %%sp
+		mov	%%g1, %%l1
+		mov	%%g2, %%l2
+		mov	%%g3, %%l3
+		call	%1
+		 mov	%%g5, %%o0
+		mov	%%l1, %%g1
+		mov	%%l2, %%g2
+		ba,pt	%%xcc, 2b
+		 restore %%l3, %%g0, %%g3
+		.previous\n"
+	: : "r" (__atomic_fool_gcc(sem)), "i" (__up)
+	: "g5", "g7", "memory", "cc");
 }	
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/smp.h linux/include/asm-sparc64/smp.h
--- v2.2.3/linux/include/asm-sparc64/smp.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/smp.h	Mon Mar 15 16:10:44 1999
@@ -20,7 +20,7 @@
 };
 
 extern int linux_num_cpus;	/* number of CPUs probed  */
-extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+extern struct prom_cpuinfo linux_cpus[64];
 
 #endif /* !(__ASSEMBLY__) */
 
@@ -40,10 +40,15 @@
 	unsigned long	udelay_val;
 
 	/* Dcache line 2 */
-	unsigned long	pgcache_size;
+	unsigned int	pgcache_size;
+	unsigned int	pgdcache_size;
 	unsigned long	*pte_cache;
-	unsigned long	pgdcache_size;
 	unsigned long	*pgd_cache;
+	unsigned int	idle_volume;
+	unsigned int	__pad;
+
+	/* Dcache lines 3 and 4 */
+	unsigned int	irq_worklists[16];
 };
 
 extern struct cpuinfo_sparc cpu_data[NR_CPUS];
@@ -73,21 +78,33 @@
 
 extern __inline__ int hard_smp_processor_id(void)
 {
-	unsigned long upaconfig;
+	extern int this_is_starfire;
+
+	if(this_is_starfire != 0) {
+		extern int starfire_hard_smp_processor_id(void);
 
-	__asm__ __volatile__("ldxa	[%%g0] %1, %0"
-			     : "=r" (upaconfig)
-			     : "i" (ASI_UPA_CONFIG));
-	return ((upaconfig >> 17) & 0x1f);
+		return starfire_hard_smp_processor_id();
+	} else {
+		unsigned long upaconfig;
+		__asm__ __volatile__("ldxa	[%%g0] %1, %0"
+				     : "=r" (upaconfig)
+				     : "i" (ASI_UPA_CONFIG));
+		return ((upaconfig >> 17) & 0x1f);
+	}
 }
 
 #define smp_processor_id() (current->processor)
 
-extern void smp_message_pass(int target, int msg, unsigned long data, int wait);
+/* This needn't do anything as we do not sleep the cpu
+ * inside of the idler task, so an interrupt is not needed
+ * to get a clean fast response.
+ */
+extern __inline__ void smp_send_reschedule(int cpu) { }
 
-/* As idle task checks need_resched in a tight loop, it is not necessary to
-   wake it up. -jj */
-#define smp_send_reschedule(cpu) do {} while (0)
+/* This is a nop as well because we capture all other cpus
+ * anyways when making the PROM active.
+ */
+extern __inline__ void smp_send_stop(void) { }
 
 #endif /* !(__ASSEMBLY__) */
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/softirq.h linux/include/asm-sparc64/softirq.h
--- v2.2.3/linux/include/asm-sparc64/softirq.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/softirq.h	Tue Mar 16 21:52:06 1999
@@ -8,6 +8,7 @@
 
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
+#include <asm/system.h>		/* for membar() */
 
 #ifndef __SMP__
 extern unsigned int local_bh_count;
@@ -36,14 +37,15 @@
 extern inline void init_bh(int nr, void (*routine)(void))
 {
 	bh_base[nr] = routine;
-	bh_mask_count[nr] = 0;
+	atomic_set(&bh_mask_count[nr], 0);
 	bh_mask |= 1 << nr;
 }
 
 extern inline void remove_bh(int nr)
 {
-	bh_base[nr] = NULL;
 	bh_mask &= ~(1 << nr);
+	membar("#StoreStore");
+	bh_base[nr] = NULL;
 }
 
 extern inline void mark_bh(int nr)
@@ -116,13 +118,13 @@
 extern inline void disable_bh(int nr)
 {
 	bh_mask &= ~(1 << nr);
-	bh_mask_count[nr]++;
+	atomic_inc(&bh_mask_count[nr]);
 	synchronize_bh();
 }
 
 extern inline void enable_bh(int nr)
 {
-	if (!--bh_mask_count[nr])
+	if (atomic_dec_and_test(&bh_mask_count[nr]))
 		bh_mask |= 1 << nr;
 }
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/spinlock.h linux/include/asm-sparc64/spinlock.h
--- v2.2.3/linux/include/asm-sparc64/spinlock.h	Thu Nov 19 09:56:29 1998
+++ linux/include/asm-sparc64/spinlock.h	Wed Mar 10 16:53:38 1999
@@ -149,9 +149,9 @@
 	: "memory");
 }
 
-#define spin_lock_irqsave(lock, flags)				\
-do {	register spinlock_t *lp asm("g1");			\
-	lp = lock;						\
+#define spin_lock_irqsave(__lock, flags)			\
+do {	register spinlock_t *__lp asm("g1");			\
+	__lp = (__lock);					\
 	__asm__ __volatile__(					\
 	"\n	rdpr		%%pil, %0\n"			\
 	"	wrpr		%%g0, 15, %%pil\n"		\
@@ -165,7 +165,7 @@
 	"	b,a,pt		%%xcc, 1b\n"			\
 	"	.previous\n"					\
 	: "=&r" (flags)						\
-	: "r" (lp)						\
+	: "r" (__lp)						\
 	: "g7", "memory");					\
 } while(0)
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/sysio.h linux/include/asm-sparc64/sysio.h
--- v2.2.3/linux/include/asm-sparc64/sysio.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/sysio.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: sysio.h,v 1.7 1997/08/18 03:47:26 davem Exp $
+/* $Id: sysio.h,v 1.8 1998/11/29 15:49:54 davem Exp $
  * sysio.h: UltraSparc sun5 specific SBUS definitions.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -260,7 +260,8 @@
 #define SYSIO_SBCNTRL_DPERR	0x003f000000000000 /* DMA Write Parity Error       */
 #define SYSIO_SBCNTRL_RESV2	0x0000800000000000 /* Reserved                     */
 #define SYSIO_SBCNTRL_PPERR	0x00007f0000000000 /* PIO Load Parity Error        */
-#define SYSIO_SBCNTRL_RESV	0x000000fffffffc00 /* Reserved                     */
+#define SYSIO_SBCNTRL_RESV	0x000000fffffff800 /* Reserved                     */
+#define SYSIO_SBCNTRL_FAST	0x0000000000000400 /* Enable Fast-SBUS mode.	   */
 #define SYSIO_SBCNTRL_WEN	0x0000000000000200 /* Power Mgmt Wake Enable       */
 #define SYSIO_SBCNTRL_EEN	0x0000000000000100 /* SBUS Error Interrupt Enable  */
 #define SYSIO_SBCNTRL_RESV3	0x00000000000000c0 /* Reserved                     */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/system.h linux/include/asm-sparc64/system.h
--- v2.2.3/linux/include/asm-sparc64/system.h	Sun Nov  8 14:03:11 1998
+++ linux/include/asm-sparc64/system.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.47 1998/10/21 03:21:20 davem Exp $ */
+/* $Id: system.h,v 1.48 1999/01/02 16:50:28 davem Exp $ */
 #ifndef __SPARC64_SYSTEM_H
 #define __SPARC64_SYSTEM_H
 
@@ -95,6 +95,8 @@
 #define nop() 		__asm__ __volatile__ ("nop")
 
 #define membar(type)	__asm__ __volatile__ ("membar " type : : : "memory");
+#define rmb()		membar("#LoadLoad | #LoadStore")
+#define wmb()		membar("#StoreLoad | #StoreStore")
 
 #define flushi(addr)	__asm__ __volatile__ ("flush %0" : : "r" (addr) : "memory")
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/termbits.h linux/include/asm-sparc64/termbits.h
--- v2.2.3/linux/include/asm-sparc64/termbits.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/termbits.h	Wed Mar 10 16:53:38 1999
@@ -159,6 +159,17 @@
 #define  B307200  0x00001007
 #define  B614400  0x00001008
 #define  B921600  0x00001009
+/* And these are the rest... */
+#define  B500000  0x0000100a
+#define  B576000  0x0000100b
+#define B1000000  0x0000100c
+#define B1152000  0x0000100d
+#define B1500000  0x0000100e
+#define B2000000  0x0000100f
+#define B2500000  0x00001010
+#define B3000000  0x00001011
+#define B3500000  0x00001012
+#define B4000000  0x00001013
 #define CIBAUD	  0x100f0000  /* input baud rate (not used) */
 #define CMSPAR    0x40000000  /* mark or space (stick) parity */
 #define CRTSCTS	  0x80000000  /* flow control */
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/termios.h linux/include/asm-sparc64/termios.h
--- v2.2.3/linux/include/asm-sparc64/termios.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-sparc64/termios.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: termios.h,v 1.6 1998/10/04 06:50:15 davem Exp $ */
+/* $Id: termios.h,v 1.7 1999/01/02 16:50:29 davem Exp $ */
 #ifndef _SPARC64_TERMIOS_H
 #define _SPARC64_TERMIOS_H
 
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/timex.h linux/include/asm-sparc64/timex.h
--- v2.2.3/linux/include/asm-sparc64/timex.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-sparc64/timex.h	Wed Mar 10 16:53:38 1999
@@ -12,4 +12,13 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
+/* Getting on the cycle counter on sparc64. */
+typedef unsigned long cycles_t;
+extern cycles_t cacheflush_time;
+#define get_cycles() \
+({	cycles_t ret; \
+	__asm__("rd	%%tick, %0" : "=r" (ret)); \
+	ret; \
+})
+
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h
--- v2.2.3/linux/include/asm-sparc64/unistd.h	Sun Nov  8 14:03:11 1998
+++ linux/include/asm-sparc64/unistd.h	Wed Mar 10 16:53:38 1999
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.23 1998/10/07 01:28:02 davem Exp $ */
+/* $Id: unistd.h,v 1.26 1999/02/10 22:24:35 jj Exp $ */
 #ifndef _SPARC64_UNISTD_H
 #define _SPARC64_UNISTD_H
 
@@ -46,11 +46,11 @@
 #define __NR_sigaltstack	 28 /* Common					   */
 #define __NR_pause               29 /* Is sigblock(0)->sigpause() in SunOS         */
 #define __NR_utime               30 /* Implemented via utimes() under SunOS        */
-#define __NR_stty                31 /* Implemented via ioctl() under SunOS         */
-#define __NR_gtty                32 /* Implemented via ioctl() under SunOS         */
+/* #define __NR_stty             31    Implemented via ioctl() under SunOS         */
+/* #define __NR_gtty             32    Implemented via ioctl() under SunOS         */
 #define __NR_access              33 /* Common                                      */
 #define __NR_nice                34 /* Implemented via get/setpriority() in SunOS  */
-#define __NR_ftime               35 /* Implemented via gettimeofday() in SunOS     */
+/* #define __NR_ftime            35    Implemented via gettimeofday() in SunOS     */
 #define __NR_sync                36 /* Common                                      */
 #define __NR_kill                37 /* Common                                      */
 #define __NR_stat                38 /* Common                                      */
@@ -59,7 +59,7 @@
 #define __NR_dup                 41 /* Common                                      */
 #define __NR_pipe                42 /* Common                                      */
 #define __NR_times               43 /* Implemented via getrusage() in SunOS        */
-#define __NR_profil              44 /* Common                                      */
+/* #define __NR_profil           44    Common                                      */
 /* #define __NR_ni_syscall       45    ENOSYS under SunOS                          */
 #define __NR_setgid              46 /* Implemented via setregid() in SunOS         */
 #define __NR_getgid              47 /* Common                                      */
@@ -68,7 +68,7 @@
 #define __NR_getegid             50 /* SunOS calls getgid()                        */
 #define __NR_acct                51 /* Common                                      */
 #define __NR_memory_ordering	 52 /* Linux Specific				   */
-#define __NR_mctl                53 /* SunOS specific                              */
+/* #define __NR_mctl             53    SunOS specific                              */
 #define __NR_ioctl               54 /* Common                                      */
 #define __NR_reboot              55 /* Common                                      */
 /* #define __NR_ni_syscall       56    ENOSYS under SunOS                          */
@@ -81,35 +81,35 @@
 /* #define __NR_ni_syscall       63    ENOSYS under SunOS                          */
 #define __NR_getpagesize         64 /* Common                                      */
 #define __NR_msync               65 /* Common in newer 1.3.x revs...               */
-/* #define __NR_ni_syscall       66    ENOSYS under SunOS                          */
+#define __NR_vfork               66 /* Common                                      */
 #define __NR_pread               67 /* Linux Specific                              */
 #define __NR_pwrite              68 /* Linux Specific                              */
-#define __NR_sbrk                69 /* SunOS Specific                              */
-#define __NR_sstk                70 /* SunOS Specific                              */
+/* #define __NR_sbrk             69    SunOS Specific                              */
+/* #define __NR_sstk             70    SunOS Specific                              */
 #define __NR_mmap                71 /* Common                                      */
-#define __NR_vadvise             72 /* SunOS Specific                              */
+/* #define __NR_vadvise          72    SunOS Specific                              */
 #define __NR_munmap              73 /* Common                                      */
 #define __NR_mprotect            74 /* Common                                      */
-#define __NR_madvise             75 /* SunOS Specific                              */
+/* #define __NR_madvise          75    SunOS Specific                              */
 #define __NR_vhangup             76 /* Common                                      */
 /* #define __NR_ni_syscall       77    ENOSYS under SunOS                          */
-#define __NR_mincore             78 /* SunOS Specific                              */
+/* #define __NR_mincore          78    SunOS Specific                              */
 #define __NR_getgroups           79 /* Common                                      */
 #define __NR_setgroups           80 /* Common                                      */
 #define __NR_getpgrp             81 /* Common                                      */
-#define __NR_setpgrp             82 /* setpgid, same difference...                 */
+/* #define __NR_setpgrp          82    setpgid, same difference...                 */
 #define __NR_setitimer           83 /* Common                                      */
 /* #define __NR_ni_syscall       84    ENOSYS under SunOS                          */
 #define __NR_swapon              85 /* Common                                      */
 #define __NR_getitimer           86 /* Common                                      */
-#define __NR_gethostname         87 /* SunOS Specific                              */
+/* #define __NR_gethostname      87    SunOS Specific                              */
 #define __NR_sethostname         88 /* Common                                      */
-#define __NR_getdtablesize       89 /* SunOS Specific                              */
+/* #define __NR_getdtablesize    89    SunOS Specific                              */
 #define __NR_dup2                90 /* Common                                      */
-#define __NR_getdopt             91 /* SunOS Specific                              */
+/* #define __NR_getdopt          91    SunOS Specific                              */
 #define __NR_fcntl               92 /* Common                                      */
 #define __NR_select              93 /* Common                                      */
-#define __NR_setdopt             94 /* SunOS Specific                              */
+/* #define __NR_setdopt          94    SunOS Specific                              */
 #define __NR_fsync               95 /* Common                                      */
 #define __NR_setpriority         96 /* Common                                      */
 #define __NR_socket              97 /* Common                                      */
@@ -123,14 +123,14 @@
 #define __NR_rt_sigtimedwait    105 /* Linux Specific                              */
 #define __NR_rt_sigqueueinfo    106 /* Linux Specific                              */
 #define __NR_rt_sigsuspend      107 /* Linux Specific                              */
-#define __NR_sigvec             108 /* SunOS Specific                              */
-#define __NR_sigblock           109 /* SunOS Specific                              */
-#define __NR_sigsetmask         110 /* SunOS Specific                              */
-#define __NR_sigpause           111 /* SunOS Specific                              */
-#define __NR_sigstack           112 /* SunOS Specific                              */
+/* #define __NR_sigvec          108    SunOS Specific                              */
+/* #define __NR_sigblock        109    SunOS Specific                              */
+/* #define __NR_sigsetmask      110    SunOS Specific                              */
+/* #define __NR_sigpause        111    SunOS Specific                              */
+/* #define __NR_sigstack        112    SunOS Specific                              */
 #define __NR_recvmsg            113 /* Common                                      */
 #define __NR_sendmsg            114 /* Common                                      */
-#define __NR_vtrace             115 /* SunOS Specific                              */
+/* #define __NR_vtrace          115    SunOS Specific                              */
 #define __NR_gettimeofday       116 /* Common                                      */
 #define __NR_getrusage          117 /* Common                                      */
 #define __NR_getsockopt         118 /* Common                                      */
@@ -155,63 +155,63 @@
 #define __NR_rmdir              137 /* Common                                      */
 #define __NR_utimes             138 /* SunOS Specific                              */
 /* #define __NR_ni_syscall      139    ENOSYS under SunOS                          */
-#define __NR_adjtime            140 /* SunOS Specific                              */
+/* #define __NR_adjtime         140    SunOS Specific                              */
 #define __NR_getpeername        141 /* Common                                      */
-#define __NR_gethostid          142 /* SunOS Specific                              */
+/* #define __NR_gethostid       142    SunOS Specific                              */
 /* #define __NR_ni_syscall      143    ENOSYS under SunOS                          */
 #define __NR_getrlimit          144 /* Common                                      */
 #define __NR_setrlimit          145 /* Common                                      */
-#define __NR_killpg             146 /* SunOS Specific                              */
+/* #define __NR_killpg          146    SunOS Specific                              */
 #define __NR_prctl		147 /* ENOSYS under SunOS                          */
 #define __NR_pciconfig_read	148 /* ENOSYS under SunOS                          */
 #define __NR_pciconfig_write	149 /* ENOSYS under SunOS                          */
 #define __NR_getsockname        150 /* Common                                      */
-#define __NR_getmsg             151 /* SunOS Specific                              */
-#define __NR_putmsg             152 /* SunOS Specific                              */
-#define __NR_poll               153 /* SunOS Specific                              */
+/* #define __NR_getmsg          151    SunOS Specific                              */
+/* #define __NR_putmsg          152    SunOS Specific                              */
+#define __NR_poll               153 /* Common                                      */
 /* #define __NR_ni_syscall      154    ENOSYS under SunOS                          */
-#define __NR_nfssvc             155 /* SunOS Specific                              */
-#define __NR_getdirentries      156 /* SunOS Specific                              */
+/* #define __NR_nfssvc          155    SunOS Specific                              */
+/* #define __NR_getdirentries   156    SunOS Specific                              */
 #define __NR_statfs             157 /* Common                                      */
 #define __NR_fstatfs            158 /* Common                                      */
 #define __NR_umount             159 /* Common                                      */
-#define __NR_async_daemon       160 /* SunOS Specific                              */
-#define __NR_getfh              161 /* SunOS Specific                              */
+/* #define __NR_async_daemon    160    SunOS Specific                              */
+/* #define __NR_getfh           161    SunOS Specific                              */
 #define __NR_getdomainname      162 /* SunOS Specific                              */
 #define __NR_setdomainname      163 /* Common                                      */
 #define __NR_utrap_install	164 /* SYSV ABI/v9 required			   */
 #define __NR_quotactl           165 /* Common                                      */
-#define __NR_exportfs           166 /* SunOS Specific                              */
+/* #define __NR_exportfs        166    SunOS Specific                              */
 #define __NR_mount              167 /* Common                                      */
 #define __NR_ustat              168 /* Common                                      */
-#define __NR_semsys             169 /* SunOS Specific                              */
-#define __NR_msgsys             170 /* SunOS Specific                              */
-#define __NR_shmsys             171 /* SunOS Specific                              */
-#define __NR_auditsys           172 /* SunOS Specific                              */
-#define __NR_rfssys             173 /* SunOS Specific                              */
+/* #define __NR_semsys          169    SunOS Specific                              */
+/* #define __NR_msgsys          170    SunOS Specific                              */
+/* #define __NR_shmsys          171    SunOS Specific                              */
+/* #define __NR_auditsys        172    SunOS Specific                              */
+/* #define __NR_rfssys          173    SunOS Specific                              */
 #define __NR_getdents           174 /* Common                                      */
 #define __NR_setsid             175 /* Common                                      */
 #define __NR_fchdir             176 /* Common                                      */
-#define __NR_fchroot            177 /* SunOS Specific                              */
-#define __NR_vpixsys            178 /* SunOS Specific                              */
-#define __NR_aioread            179 /* SunOS Specific                              */
-#define __NR_aiowrite           180 /* SunOS Specific                              */
-#define __NR_aiowait            181 /* SunOS Specific                              */
-#define __NR_aiocancel          182 /* SunOS Specific                              */
+/* #define __NR_fchroot         177    SunOS Specific                              */
+/* #define __NR_vpixsys         178    SunOS Specific                              */
+/* #define __NR_aioread         179    SunOS Specific                              */
+/* #define __NR_aiowrite        180    SunOS Specific                              */
+/* #define __NR_aiowait         181    SunOS Specific                              */
+/* #define __NR_aiocancel       182    SunOS Specific                              */
 #define __NR_sigpending         183 /* Common                                      */
 #define __NR_query_module	184 /* Linux Specific				   */
 #define __NR_setpgid            185 /* Common                                      */
-#define __NR_pathconf           186 /* SunOS Specific                              */
-#define __NR_fpathconf          187 /* SunOS Specific                              */
-#define __NR_sysconf            188 /* SunOS Specific                              */
+/* #define __NR_pathconf        186    SunOS Specific                              */
+/* #define __NR_fpathconf       187    SunOS Specific                              */
+/* #define __NR_sysconf         188    SunOS Specific                              */
 #define __NR_uname              189 /* Linux Specific                              */
 #define __NR_init_module        190 /* Linux Specific                              */
 #define __NR_personality        191 /* Linux Specific                              */
-#define __NR_prof               192 /* Linux Specific                              */
-#define __NR_break              193 /* Linux Specific                              */
-#define __NR_lock               194 /* Linux Specific                              */
-#define __NR_mpx                195 /* Linux Specific                              */
-#define __NR_ulimit             196 /* Linux Specific                              */
+/* #define __NR_prof            192    Linux Specific                              */
+/* #define __NR_break           193    Linux Specific                              */
+/* #define __NR_lock            194    Linux Specific                              */
+/* #define __NR_mpx             195    Linux Specific                              */
+/* #define __NR_ulimit          196    Linux Specific                              */
 #define __NR_getppid            197 /* Linux Specific                              */
 #define __NR_sigaction          198 /* Linux Specific                              */
 #define __NR_sgetmask           199 /* Linux Specific                              */
@@ -220,20 +220,20 @@
 #define __NR_oldlstat           202 /* Linux Specific                              */
 #define __NR_uselib             203 /* Linux Specific                              */
 #define __NR_readdir            204 /* Linux Specific                              */
-#define __NR_ioperm             205 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_ioperm          205    Linux Specific - i386 specific, unused      */
 #define __NR_socketcall         206 /* Linux Specific                              */
 #define __NR_syslog             207 /* Linux Specific                              */
-#define __NR_olduname           208 /* Linux Specific                              */
-#define __NR_iopl               209 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_olduname        208    Linux Specific                              */
+/* #define __NR_iopl            209    Linux Specific - i386 specific, unused      */
 #define __NR_idle               210 /* Linux Specific                              */
-#define __NR_vm86               211 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_vm86            211    Linux Specific - i386 specific, unused      */
 #define __NR_waitpid            212 /* Linux Specific                              */
 #define __NR_swapoff            213 /* Linux Specific                              */
 #define __NR_sysinfo            214 /* Linux Specific                              */
 #define __NR_ipc                215 /* Linux Specific                              */
 #define __NR_sigreturn          216 /* Linux Specific                              */
 #define __NR_clone              217 /* Linux Specific                              */
-#define __NR_modify_ldt         218 /* Linux Specific - i386 specific, unused      */
+/* #define __NR_modify_ldt      218    Linux Specific - i386 specific, unused      */
 #define __NR_adjtimex           219 /* Linux Specific                              */
 #define __NR_sigprocmask        220 /* Linux Specific                              */
 #define __NR_create_module      221 /* Linux Specific                              */
@@ -247,10 +247,10 @@
 #define __NR_setfsgid           229 /* Linux Specific                              */
 #define __NR__newselect         230 /* Linux Specific                              */
 #define __NR_time               231 /* Linux Specific                              */
-#define __NR_oldstat            232 /* Linux Specific                              */
+/* #define __NR_oldstat         232    Linux Specific                              */
 #define __NR_stime              233 /* Linux Specific                              */
-#define __NR_oldfstat           234 /* Linux Specific                              */
-#define __NR_phys               235 /* Linux Specific                              */
+/* #define __NR_oldfstat        234    Linux Specific                              */
+/* #define __NR_phys            235    Linux Specific                              */
 #define __NR__llseek            236 /* Linux Specific                              */
 #define __NR_mlock              237
 #define __NR_munlock            238
@@ -430,70 +430,7 @@
 	return waitpid(-1,wait_stat,0);
 }
 
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- * NOTE! Only a kernel-only process(ie the swapper or direct descendants
- * who haven't done an "execve()") should use this: it will work within
- * a system call from a "real" process, but the process memory space will
- * not be free'd until both the parent and the child have exited.
- */
-static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
-	long retval;
-
-	__asm__ __volatile("mov %1, %%g1\n\t"
-			   "mov %2, %%o0\n\t"	   /* Clone flags. */
-			   "mov 0, %%o1\n\t"	   /* usp arg == 0 */
-			   "t 0x6d\n\t"		   /* Linux/Sparc clone(). */
-			   "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
-			   " mov %%o0, %0\n\t"
-			   "jmpl %4, %%o7\n\t"	   /* Call the function. */
-			   " mov %5, %%o0\n\t"	   /* Set arg in delay. */
-			   "mov %3, %%g1\n\t"
-			   "t 0x6d\n\t"		   /* Linux/Sparc exit(). */
-			   /* Notreached by child. */
-			   "1:" :
-			   "=r" (retval) :
-			   "i" (__NR_clone), "r" (flags | CLONE_VM),
-			   "i" (__NR_exit),  "r" (fn), "r" (arg) :
-			   "g1", "o0", "o1", "memory", "cc");
-	return retval;
-}
-
-static __inline__ pid_t fork(void)
-{
-	long retval;
-
-	__asm__ __volatile("mov %1, %%g1\n\t"
-			   "t 0x6d\n\t"
-			   "brz,a,pn %%o1, 1f\n\t"
-			   " mov %%o0, %0\n\t"
-			   "mov %%g0, %0\n\t"
-			   "1:" :
-			   "=r" (retval) :
-			   "i" (__NR_fork) :
-			   "g1", "o0", "o1", "memory", "cc");
-	return retval;
-}
-
-static __inline__ pid_t clone(unsigned long flags, char *ksp)
-{
-	long retval;
-
-	__asm__ __volatile("mov %1, %%g1\n\t"
-			   "mov %2, %%o0\n\t"
-			   "mov %3, %%o1\n\t"
-			   "t 0x6d\n\t"
-			   "brz,a,pn %%o1, 1f\n\t"
-			   " mov %%o0, %0\n\t"
-			   "mov %%g0, %0\n\t"
-			   "1:" :
-			   "=r" (retval) :
-			   "i" (__NR_fork), "r" (flags), "r" (ksp) :
-			   "g1", "o0", "o1", "memory", "cc");
-	return retval;
-}
+extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 #endif /* __KERNEL_SYSCALLS__ */
 
diff -u --recursive --new-file v2.2.3/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.2.3/linux/include/linux/cdrom.h	Tue Jan 19 11:32:53 1999
+++ linux/include/linux/cdrom.h	Sun Mar 21 18:37:56 1999
@@ -112,6 +112,9 @@
 #define CDROM_DRIVE_STATUS	0x5326  /* Get tray position, etc. */
 #define CDROM_DISC_STATUS	0x5327  /* Get disc type, etc. */
 #define CDROM_CHANGER_NSLOTS    0x5328  /* Get number of slots */
+#define CDROM_LOCKDOOR		0x5329  /* lock or unlock door */
+#define CDROM_DEBUG		0x5330	/* Turn debug messages on/off */
+#define CDROM_GET_CAPABILITY	0x5331	/* get capabilities */
 
 /* This ioctl is only used by sbpcd at the moment */
 #define CDROMAUDIOBUFSIZ        0x5382	/* set the audio buffer size */
@@ -340,14 +343,15 @@
 #define CDC_IOCTLS              0x400   /* driver has non-standard ioctls */
 #define CDC_DRIVE_STATUS        0x800   /* driver implements drive status */
 
-/* drive status possibilities used with the uniform CD-ROM driver */
-#define CDS_NO_INFO		0               /* if not implemented */
+/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
+#define CDS_NO_INFO		0	/* if not implemented */
 #define CDS_NO_DISC		1
 #define CDS_TRAY_OPEN		2
 #define CDS_DRIVE_NOT_READY	3
 #define CDS_DISC_OK		4
 
-/* disc status possibilities, other than CDS_NO_DISC and CDS_NO_INFO */
+/* return values for the CDROM_DISC_STATUS ioctl */
+/* can also return CDS_NO_[INFO|DISC], from above */
 #define CDS_AUDIO		100
 #define CDS_DATA_1		101
 #define CDS_DATA_2		102
diff -u --recursive --new-file v2.2.3/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.2.3/linux/include/linux/dcache.h	Tue Jan 19 11:32:53 1999
+++ linux/include/linux/dcache.h	Fri Mar 19 08:08:57 1999
@@ -6,7 +6,10 @@
 /*
  * linux/include/linux/dcache.h
  *
- * Directory cache data structures
+ * Dirent cache data structures
+ *
+ * (C) Copyright 1997 Thomas Schoebel-Theuer,
+ * with heavy changes by Linus Torvalds
  */
 
 #define D_MAXLEN 1024
diff -u --recursive --new-file v2.2.3/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.2.3/linux/include/linux/genhd.h	Mon Dec 28 15:00:53 1998
+++ linux/include/linux/genhd.h	Sun Mar 21 14:18:21 1999
@@ -10,6 +10,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/types.h>
 
 #define CONFIG_MSDOS_PARTITION 1
 
diff -u --recursive --new-file v2.2.3/linux/include/linux/in.h linux/include/linux/in.h
--- v2.2.3/linux/include/linux/in.h	Mon Oct  5 13:13:44 1998
+++ linux/include/linux/in.h	Sun Mar 21 14:18:21 1999
@@ -80,7 +80,6 @@
 /* These need to appear somewhere around here */
 #define IP_DEFAULT_MULTICAST_TTL        1
 #define IP_DEFAULT_MULTICAST_LOOP       1
-#define IP_MAX_MEMBERSHIPS              20
 
 /* Request struct for multicast socket ops */
 
diff -u --recursive --new-file v2.2.3/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.2.3/linux/include/linux/netdevice.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/netdevice.h	Sun Mar 21 14:19:12 1999
@@ -65,33 +65,6 @@
 #define MAX_HEADER (LL_MAX_HEADER + 48)
 #endif
 
-struct neighbour;
-struct neigh_parms;
-struct sk_buff;
-
-/*
- *	We tag multicasts with these structures.
- */
- 
-struct dev_mc_list
-{	
-	struct dev_mc_list	*next;
-	__u8			dmi_addr[MAX_ADDR_LEN];
-	unsigned char		dmi_addrlen;
-	int			dmi_users;
-	int			dmi_gusers;
-};
-
-struct hh_cache
-{
-	struct hh_cache *hh_next;	/* Next entry			     */
-	atomic_t	hh_refcnt;	/* number of users                   */
-	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
-	int		(*hh_output)(struct sk_buff *skb);
-	/* cached hardware header; allow for machine alignment needs.        */
-	unsigned long	hh_data[16/sizeof(unsigned long)];
-};
-
 /*
  *	Network device statistics. Akin to the 2.0 ether stats but
  *	with byte counters.
@@ -157,6 +130,35 @@
 
 #include <linux/skbuff.h>
 
+struct neighbour;
+struct neigh_parms;
+struct sk_buff;
+
+/*
+ *	We tag multicasts with these structures.
+ */
+ 
+struct dev_mc_list
+{	
+	struct dev_mc_list	*next;
+	__u8			dmi_addr[MAX_ADDR_LEN];
+	unsigned char		dmi_addrlen;
+	int			dmi_users;
+	int			dmi_gusers;
+};
+
+struct hh_cache
+{
+	struct hh_cache *hh_next;	/* Next entry			     */
+	atomic_t	hh_refcnt;	/* number of users                   */
+	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
+	int		(*hh_output)(struct sk_buff *skb);
+	rwlock_t	hh_lock;
+	/* cached hardware header; allow for machine alignment needs.        */
+	unsigned long	hh_data[16/sizeof(unsigned long)];
+};
+
+
 /*
  *	The DEVICE structure.
  *	Actually, this whole structure is a big mistake.  It mixes I/O
@@ -432,6 +434,7 @@
 extern void		dev_mc_discard(struct device *dev);
 extern void		dev_set_promiscuity(struct device *dev, int inc);
 extern void		dev_set_allmulti(struct device *dev, int inc);
+extern void		netdev_state_change(struct device *dev);
 /* Load a device via the kmod */
 extern void		dev_load(const char *name);
 extern void		dev_mcast_init(void);
diff -u --recursive --new-file v2.2.3/linux/include/linux/openpic.h linux/include/linux/openpic.h
--- v2.2.3/linux/include/linux/openpic.h	Fri Oct  9 13:27:16 1998
+++ linux/include/linux/openpic.h	Fri Mar 19 10:51:42 1999
@@ -313,15 +313,6 @@
 #define Vector_Priority			_Vector_Priority.Reg
 #define Destination			_Destination.Reg
 
-
-    /*
-     *  Vendor and Device IDs
-     */
-
-#define OPENPIC_VENDOR_ID_APPLE		0x14
-#define OPENPIC_DEVICE_ID_APPLE_HYDRA	0x46
-
-
     /*
      *  OpenPIC Operations
      */
diff -u --recursive --new-file v2.2.3/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.2.3/linux/include/linux/pci.h	Wed Mar 10 15:29:50 1999
+++ linux/include/linux/pci.h	Sun Mar 21 14:18:21 1999
@@ -388,6 +388,7 @@
 #define PCI_DEVICE_ID_IBM_82G2675	0x001d
 #define PCI_DEVICE_ID_IBM_MCA		0x0020
 #define PCI_DEVICE_ID_IBM_82351		0x0022
+#define PCI_DEVICE_ID_IBM_PYTHON	0x002d
 #define PCI_DEVICE_ID_IBM_SERVERAID	0x002e
 #define PCI_DEVICE_ID_IBM_TR_WAKE	0x003e
 #define PCI_DEVICE_ID_IBM_MPIC		0x0046
diff -u --recursive --new-file v2.2.3/linux/include/linux/pkt_cls.h linux/include/linux/pkt_cls.h
--- v2.2.3/linux/include/linux/pkt_cls.h	Sat May  2 14:19:54 1998
+++ linux/include/linux/pkt_cls.h	Sun Mar 21 07:22:00 1999
@@ -23,9 +23,12 @@
 	TCA_POLICE_TBF,
 	TCA_POLICE_RATE,
 	TCA_POLICE_PEAKRATE,
+	TCA_POLICE_AVRATE,
+	TCA_POLICE_RESULT
+#define TCA_POLICE_RESULT TCA_POLICE_RESULT
 };
 
-#define TCA_POLICE_MAX TCA_POLICE_PEAKRATE
+#define TCA_POLICE_MAX TCA_POLICE_RESULT
 
 /* U32 filters */
 
@@ -113,5 +116,31 @@
 	__u8	tunnelid;
 	__u8	tunnelhdr;
 };
+
+/* ROUTE filter */
+
+enum
+{
+	TCA_ROUTE4_UNSPEC,
+	TCA_ROUTE4_CLASSID,
+	TCA_ROUTE4_TO,
+	TCA_ROUTE4_FROM,
+	TCA_ROUTE4_IIF,
+	TCA_ROUTE4_POLICE,
+};
+
+#define TCA_ROUTE4_MAX TCA_ROUTE4_POLICE
+
+
+/* FW filter */
+
+enum
+{
+	TCA_FW_UNSPEC,
+	TCA_FW_CLASSID,
+	TCA_FW_POLICE,
+};
+
+#define TCA_FW_MAX TCA_FW_POLICE
 
 #endif
diff -u --recursive --new-file v2.2.3/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.2.3/linux/include/linux/proc_fs.h	Wed Mar 10 15:29:50 1999
+++ linux/include/linux/proc_fs.h	Sun Mar 21 14:18:53 1999
@@ -203,7 +203,8 @@
 	PROC_SCSI_53C94,
 	PROC_SCSI_PLUTO,
 	PROC_SCSI_INI9100U,
-	PROC_SCSI_INIA100,
+ 	PROC_SCSI_INIA100,
+	PROC_SCSI_FCAL,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
diff -u --recursive --new-file v2.2.3/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.2.3/linux/include/linux/rtnetlink.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/rtnetlink.h	Sun Mar 21 07:22:00 1999
@@ -139,6 +139,7 @@
 #define RTPROT_RA	9	/* RDISC/ND router advertisments */
 #define RTPROT_MRT	10	/* Merit MRT */
 #define RTPROT_ZEBRA	11	/* Zebra */
+#define RTPROT_BIRD	12	/* BIRD */
 
 /* rtm_scope
 
diff -u --recursive --new-file v2.2.3/linux/include/linux/sunrpc/svcsock.h linux/include/linux/sunrpc/svcsock.h
--- v2.2.3/linux/include/linux/sunrpc/svcsock.h	Sun Nov  8 14:03:12 1998
+++ linux/include/linux/sunrpc/svcsock.h	Sun Mar 21 14:18:51 1999
@@ -39,7 +39,7 @@
 	void			(*sk_odata)(struct sock *, int bytes);
 
 	/* private TCP part */
-	unsigned long		sk_reclen;	/* length of record */
+	int			sk_reclen;	/* length of record */
 	int			sk_tcplen;	/* current read length */
 
 	/* Debugging */
diff -u --recursive --new-file v2.2.3/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.2.3/linux/include/linux/sysctl.h	Thu Jan  7 15:11:40 1999
+++ linux/include/linux/sysctl.h	Sun Mar 21 07:22:00 1999
@@ -161,7 +161,8 @@
 enum
 {
 	NET_UNIX_DESTROY_DELAY=1,
-	NET_UNIX_DELETE_DELAY=2
+	NET_UNIX_DELETE_DELAY=2,
+	NET_UNIX_MAX_DGRAM_QLEN=3,
 };
 
 /* /proc/sys/net/ipv4 */
@@ -225,7 +226,8 @@
 	NET_IPV4_ROUTE_REDIRECT_SILENCE=11,
 	NET_IPV4_ROUTE_ERROR_COST=12,
 	NET_IPV4_ROUTE_ERROR_BURST=13,
-	NET_IPV4_ROUTE_GC_ELASTICITY=14
+	NET_IPV4_ROUTE_GC_ELASTICITY=14,
+	NET_IPV4_ROUTE_MTU_EXPIRES=15
 };
 
 enum
@@ -265,7 +267,8 @@
 	NET_IPV6_ROUTE_GC_MIN_INTERVAL=4,
 	NET_IPV6_ROUTE_GC_TIMEOUT=5,
 	NET_IPV6_ROUTE_GC_INTERVAL=6,
-	NET_IPV6_ROUTE_GC_ELASTICITY=7
+	NET_IPV6_ROUTE_GC_ELASTICITY=7,
+	NET_IPV6_ROUTE_MTU_EXPIRES=8
 };
 
 enum {
diff -u --recursive --new-file v2.2.3/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v2.2.3/linux/include/linux/sysv_fs.h	Thu Dec 31 10:29:03 1998
+++ linux/include/linux/sysv_fs.h	Sun Mar 21 14:19:48 1999
@@ -398,7 +398,6 @@
 extern void sysv_read_inode(struct inode *);
 extern int sysv_notify_change(struct dentry *, struct iattr *);
 extern void sysv_write_inode(struct inode *);
-extern void sysv_put_inode(struct inode *);
 extern int sysv_statfs(struct super_block *, struct statfs *, int);
 extern int sysv_sync_inode(struct inode *);
 extern int sysv_sync_file(struct file *, struct dentry *);
diff -u --recursive --new-file v2.2.3/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.2.3/linux/include/linux/wireless.h	Fri Jul 31 17:11:08 1998
+++ linux/include/linux/wireless.h	Wed Mar 10 16:51:35 1999
@@ -1,7 +1,7 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	4	12.2.97
+ * Version :	6	18.2.99
  *
  * Authors :	Jean Tourrilhes - HPLB - <jt@hplb.hpl.hp.com>
  */
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	5
+#define WIRELESS_EXT	6
 
 /*
  * Changes :
@@ -82,6 +82,10 @@
  * --------
  *	- Missing encoding definitions in range
  *	- Access points stuff
+ *
+ * V5 to V6
+ * --------
+ *	- 802.11 support
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -113,11 +117,19 @@
 #define SIOCGIWAP	0x8B15		/* get access point hardware addresses */
 #define SIOCGIWAPLIST	0x8B17		/* get list of access point in range */
 
+/* 802.11 specific support */
+#define SIOCSIWESSID	0x8B1A		/* set ESSID (network name) */
+#define SIOCGIWESSID	0x8B1B		/* get ESSID */
+/* As the ESSID is a string up to 32 bytes long, it doesn't fit within the
+ * 'iwreq' structure, so we need to use the 'data' member to point to a
+ * string in user space, like it is done for RANGE...
+ */
+
 /* ------------------------- IOCTL STUFF ------------------------- */
 
 /* The first and the last (range) */
 #define SIOCIWFIRST	0x8B00
-#define SIOCIWLAST	0x8B17
+#define SIOCIWLAST	0x8B1B
 
 /* Even : get (world access), odd : set (root access) */
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
diff -u --recursive --new-file v2.2.3/linux/include/net/addrconf.h linux/include/net/addrconf.h
--- v2.2.3/linux/include/net/addrconf.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/addrconf.h	Sun Mar 21 07:22:00 1999
@@ -53,8 +53,9 @@
 
 extern struct inet6_ifaddr *	ipv6_chk_addr(struct in6_addr *addr,
 					      struct device *dev, int nd);
-extern struct inet6_ifaddr *	ipv6_get_saddr(struct dst_entry *dst, 
-					       struct in6_addr *daddr);
+extern int			ipv6_get_saddr(struct dst_entry *dst, 
+					       struct in6_addr *daddr,
+					       struct in6_addr *saddr);
 extern struct inet6_ifaddr *	ipv6_get_lladdr(struct device *dev);
 
 /*
diff -u --recursive --new-file v2.2.3/linux/include/net/dst.h linux/include/net/dst.h
--- v2.2.3/linux/include/net/dst.h	Sat Sep  5 16:46:41 1998
+++ linux/include/net/dst.h	Sun Mar 21 14:19:13 1999
@@ -36,9 +36,10 @@
 	struct device	        *dev;
 	int			obsolete;
 	unsigned long		lastuse;
+	unsigned long		expires;
 	unsigned		mxlock;
-	unsigned		window;
 	unsigned		pmtu;
+	unsigned		window;
 	unsigned		rtt;
 	unsigned long		rate_last;	/* rate limiting for ICMP */
 	unsigned long		rate_tokens;
@@ -98,6 +99,19 @@
 		atomic_dec(&dst->use);
 }
 
+/* The following primitive should be use if and only if
+   destination entry has just been removed from a location
+   accessed directly by hard irq.
+ */
+extern __inline__
+void dst_release_irqwait(struct dst_entry * dst)
+{
+	if (dst) {
+		synchronize_irq();
+		atomic_dec(&dst->use);
+	}
+}
+
 extern __inline__
 struct dst_entry * dst_check(struct dst_entry ** dst_p, u32 cookie)
 {
@@ -151,6 +165,17 @@
 	struct dst_entry * dst = skb->dst;
 	if (dst && dst->ops && dst->ops->link_failure)
 		dst->ops->link_failure(skb);
+}
+
+extern __inline__ void dst_set_expires(struct dst_entry *dst, int timeout)
+{
+	unsigned long expires = jiffies + timeout;
+
+	if (expires == 0)
+		expires = 1;
+
+	if (dst->expires == 0 || (long)(dst->expires - expires) > 0)
+		dst->expires = expires;
 }
 #endif
 
diff -u --recursive --new-file v2.2.3/linux/include/net/ip.h linux/include/net/ip.h
--- v2.2.3/linux/include/net/ip.h	Mon Oct  5 13:13:47 1998
+++ linux/include/net/ip.h	Sun Mar 21 14:19:18 1999
@@ -147,13 +147,14 @@
 	skb->protocol = __constant_htons(ETH_P_IP);
 
 	if (hh) {
+		read_lock_irq(&hh->hh_lock);
 		memcpy(skb->data - 16, hh->hh_data, 16);
+		read_unlock_irq(&hh->hh_lock);
 	        skb_push(skb, dev->hard_header_len);
 		return hh->hh_output(skb);
 	} else if (dst->neighbour)
 		return dst->neighbour->output(skb);
 
-	printk(KERN_DEBUG "khm\n");
 	kfree_skb(skb);
 	return -EINVAL;
 }
diff -u --recursive --new-file v2.2.3/linux/include/net/ip6_fib.h linux/include/net/ip6_fib.h
--- v2.2.3/linux/include/net/ip6_fib.h	Sat Sep  5 16:46:41 1998
+++ linux/include/net/ip6_fib.h	Sun Mar 21 07:22:00 1999
@@ -59,6 +59,7 @@
 
 #define rt6i_dev			u.dst.dev
 #define rt6i_nexthop			u.dst.neighbour
+#define rt6i_expires			u.dst.expires
 
 	struct fib6_node		*rt6i_node;
 
@@ -67,7 +68,6 @@
 	u32				rt6i_flags;
 	u32				rt6i_metric;
 	u8				rt6i_hoplimit;
-	unsigned long			rt6i_expires;
 	atomic_t			rt6i_ref;
 
 	union {
diff -u --recursive --new-file v2.2.3/linux/include/net/ip_fib.h linux/include/net/ip_fib.h
--- v2.2.3/linux/include/net/ip_fib.h	Mon Oct  5 13:13:47 1998
+++ linux/include/net/ip_fib.h	Sun Mar 21 07:22:00 1999
@@ -127,6 +127,8 @@
 	int		(*tb_flush)(struct fib_table *table);
 	int		(*tb_get_info)(struct fib_table *table, char *buf,
 				       int first, int count);
+	void		(*tb_select_default)(struct fib_table *table,
+					     const struct rt_key *key, struct fib_result *res);
 
 	unsigned char	tb_data[0];
 };
@@ -156,6 +158,12 @@
 	return 0;
 }
 
+extern __inline__ void fib_select_default(const struct rt_key *key, struct fib_result *res)
+{
+	if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
+		main_table->tb_select_default(main_table, key, res);
+}
+
 #else /* CONFIG_IP_MULTIPLE_TABLES */
 #define local_table (fib_tables[RT_TABLE_LOCAL])
 #define main_table (fib_tables[RT_TABLE_MAIN])
@@ -179,6 +187,9 @@
 
 	return fib_tables[id] ? : __fib_new_table(id);
 }
+
+extern void fib_select_default(const struct rt_key *key, struct fib_result *res);
+
 #endif /* CONFIG_IP_MULTIPLE_TABLES */
 
 /* Exported by fib_frontend.c */
@@ -189,7 +200,7 @@
 extern int inet_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
 extern int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb);
 extern int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
-			       struct device *dev, u32 *spec_dst);
+			       struct device *dev, u32 *spec_dst, u32 *itag);
 extern void fib_select_multipath(const struct rt_key *key, struct fib_result *res);
 
 /* Exported by fib_semantics.c */
@@ -226,5 +237,21 @@
 extern u32 fib_rules_policy(u32 saddr, struct fib_result *res, unsigned *flags);
 extern void fib_rules_init(void);
 #endif
+
+extern __inline__ void fib_combine_itag(u32 *itag, struct fib_result *res)
+{
+#ifdef CONFIG_NET_CLS_ROUTE
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+	u32 rtag;
+#endif
+	*itag = FIB_RES_NH(*res).nh_tclassid<<16;
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+	rtag = fib_rules_tclass(res);
+	if (*itag == 0)
+		*itag = (rtag<<16);
+	*itag |= (rtag>>16);
+#endif
+#endif
+}
 
 #endif  _NET_FIB_H
diff -u --recursive --new-file v2.2.3/linux/include/net/ip_masq.h linux/include/net/ip_masq.h
--- v2.2.3/linux/include/net/ip_masq.h	Fri Jan  8 22:36:25 1999
+++ linux/include/net/ip_masq.h	Sun Mar 21 14:19:22 1999
@@ -126,6 +126,7 @@
  */
 extern int ip_fw_masquerade(struct sk_buff **, __u32 maddr);
 extern int ip_fw_masq_icmp(struct sk_buff **, __u32 maddr);
+extern int ip_fw_unmasq_icmp(struct sk_buff *);
 extern int ip_fw_demasquerade(struct sk_buff **);
 
 /*
diff -u --recursive --new-file v2.2.3/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- v2.2.3/linux/include/net/ipv6.h	Mon Oct  5 13:13:47 1998
+++ linux/include/net/ipv6.h	Sun Mar 21 14:19:17 1999
@@ -4,7 +4,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: ipv6.h,v 1.14 1998/10/03 09:36:45 davem Exp $
+ *	$Id: ipv6.h,v 1.15 1999/03/21 05:22:16 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -184,14 +184,6 @@
 {
 	return ((a->s6_addr32[0] | a->s6_addr32[1] | 
 		 a->s6_addr32[2] | a->s6_addr32[3] ) == 0); 
-}
-
-extern __inline__ int gfp_any(void)
-{
-	int pri = GFP_KERNEL;
-	if (in_interrupt())
-		pri = GFP_ATOMIC;
-	return pri;
 }
 
 /*
diff -u --recursive --new-file v2.2.3/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.2.3/linux/include/net/neighbour.h	Fri Jul 31 17:11:08 1998
+++ linux/include/net/neighbour.h	Sun Mar 21 14:19:12 1999
@@ -226,7 +226,6 @@
 		neigh->confirmed = jiffies;
 }
 
-
 extern __inline__ struct neighbour *
 neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev)
 {
@@ -258,6 +257,7 @@
 extern __inline__ void neigh_table_lock(struct neigh_table *tbl)
 {
 	atomic_inc(&tbl->lock);
+	synchronize_bh();
 }
 
 extern __inline__ void neigh_table_unlock(struct neigh_table *tbl)
diff -u --recursive --new-file v2.2.3/linux/include/net/pkt_cls.h linux/include/net/pkt_cls.h
--- v2.2.3/linux/include/net/pkt_cls.h	Sat May  2 14:19:54 1998
+++ linux/include/net/pkt_cls.h	Sun Mar 21 07:22:00 1999
@@ -50,7 +50,7 @@
 
 	unsigned long		(*get)(struct tcf_proto*, u32 handle);
 	void			(*put)(struct tcf_proto*, unsigned long);
-	int			(*change)(struct tcf_proto*, u32 handle, struct rtattr **, unsigned long *);
+	int			(*change)(struct tcf_proto*, unsigned long, u32 handle, struct rtattr **, unsigned long *);
 	int			(*delete)(struct tcf_proto*, unsigned long);
 	void			(*walk)(struct tcf_proto*, struct tcf_walker *arg);
 
@@ -75,6 +75,14 @@
 			return err;
 	}
 	return -1;
+}
+
+extern __inline__ unsigned long cls_set_class(unsigned long *clp, unsigned long cl)
+{
+	net_serialize_enter();
+	cl = xchg(clp, cl);
+	net_serialize_leave();
+	return cl;
 }
 
 extern int register_tcf_proto_ops(struct tcf_proto_ops *ops);
diff -u --recursive --new-file v2.2.3/linux/include/net/pkt_sched.h linux/include/net/pkt_sched.h
--- v2.2.3/linux/include/net/pkt_sched.h	Sat Sep  5 16:46:41 1998
+++ linux/include/net/pkt_sched.h	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
 #define PSCHED_JIFFIES 		2
 #define PSCHED_CPU 		3
 
-#define PSCHED_CLOCK_SOURCE	PSCHED_GETTIMEOFDAY
+#define PSCHED_CLOCK_SOURCE	PSCHED_JIFFIES
 
 #include <linux/pkt_sched.h>
 #include <net/pkt_cls.h>
@@ -25,6 +25,7 @@
 {
 	/* Child qdisc manipulation */
 	int			(*graft)(struct Qdisc *, unsigned long cl, struct Qdisc *, struct Qdisc **);
+	struct Qdisc *		(*leaf)(struct Qdisc *, unsigned long cl);
 
 	/* Class manipulation routines */
 	unsigned long		(*get)(struct Qdisc *, u32 classid);
@@ -35,7 +36,7 @@
 
 	/* Filter manipulation */
 	struct tcf_proto **	(*tcf_chain)(struct Qdisc *, unsigned long);
-	unsigned long		(*bind_tcf)(struct Qdisc *, u32 classid);
+	unsigned long		(*bind_tcf)(struct Qdisc *, unsigned long, u32 classid);
 	void			(*unbind_tcf)(struct Qdisc *, unsigned long);
 
 	/* rtnetlink specific */
@@ -57,6 +58,7 @@
 	int			(*init)(struct Qdisc *, struct rtattr *arg);
 	void			(*reset)(struct Qdisc *);
 	void			(*destroy)(struct Qdisc *);
+	int			(*change)(struct Qdisc *, struct rtattr *arg);
 
 	int			(*dump)(struct Qdisc *, struct sk_buff *);
 };
@@ -74,13 +76,12 @@
 	int 			(*enqueue)(struct sk_buff *skb, struct Qdisc *dev);
 	struct sk_buff *	(*dequeue)(struct Qdisc *dev);
 	unsigned		flags;
-#define TCQ_F_DEFAULT	1
-#define TCQ_F_BUILTIN	2
+#define TCQ_F_BUILTIN	1
+#define TCQ_F_THROTTLED	2
 	struct Qdisc_ops	*ops;
 	struct Qdisc		*next;
 	u32			handle;
-	u32			classid;
-	struct Qdisc		*parent;
+	atomic_t		refcnt;
 	struct sk_buff_head	q;
 	struct device 		*dev;
 
@@ -89,6 +90,11 @@
 	unsigned long		tx_last;
 	int			(*reshape_fail)(struct sk_buff *skb, struct Qdisc *q);
 
+	/* This field is deprecated, but it is still used by CBQ
+	 * and it will live until better solution will be invented.
+	 */
+	struct Qdisc		*__parent;
+
 	char			data[0];
 };
 
@@ -129,6 +135,15 @@
    which have fast and precise clock source, but it is too expensive.
  */
 
+/* General note about internal clock.
+
+   Any clock source returns time intervals, measured in units
+   close to 1usec. With source PSCHED_GETTIMEOFDAY it is precisely
+   microseconds, otherwise something close but different chosen to minimize
+   arithmetic cost. Ratio usec/internal untis in form nominator/denominator
+   may be read from /proc/net/psched.
+ */
+
 
 #if PSCHED_CLOCK_SOURCE == PSCHED_GETTIMEOFDAY
 
@@ -138,8 +153,12 @@
 #define PSCHED_GET_TIME(stamp) do_gettimeofday(&(stamp))
 #define PSCHED_US2JIFFIE(usecs) (((usecs)+(1000000/HZ-1))/(1000000/HZ))
 
+#define PSCHED_EXPORTLIST EXPORT_SYMBOL(psched_tod_diff);
+
 #else /* PSCHED_CLOCK_SOURCE != PSCHED_GETTIMEOFDAY */
 
+#define PSCHED_EXPORTLIST PSCHED_EXPORTLIST_1 PSCHED_EXPORTLIST_2
+
 typedef u64	psched_time_t;
 typedef long	psched_tdiff_t;
 
@@ -147,10 +166,6 @@
 
 #if PSCHED_CLOCK_SOURCE == PSCHED_JIFFIES
 
-#define PSCHED_WATCHER unsigned long
-
-extern PSCHED_WATCHER psched_time_mark;
-
 #if HZ == 100
 #define PSCHED_JSCALE 13
 #elif HZ == 1024
@@ -159,22 +174,45 @@
 #define PSCHED_JSCALE 0
 #endif
 
+#define PSCHED_EXPORTLIST_2
+
+#if ~0UL == 0xFFFFFFFF
+
+#define PSCHED_WATCHER unsigned long
+
+extern PSCHED_WATCHER psched_time_mark;
+
 #define PSCHED_GET_TIME(stamp) ((stamp) = psched_time_base + (((unsigned long)(jiffies-psched_time_mark))<<PSCHED_JSCALE))
-#define PSCHED_US2JIFFIE(delay) ((delay)>>PSCHED_JSCALE)
+
+#define PSCHED_EXPORTLIST_1 EXPORT_SYMBOL(psched_time_base); \
+                            EXPORT_SYMBOL(psched_time_mark);
+
+#else
+
+#define PSCHED_GET_TIME(stamp) ((stamp) = (jiffies<<PSCHED_JSCALE))
+
+#define PSCHED_EXPORTLIST_1 
+
+#endif
+
+#define PSCHED_US2JIFFIE(delay) (((delay)+(1<<PSCHED_JSCALE)-1)>>PSCHED_JSCALE)
 
 #elif PSCHED_CLOCK_SOURCE == PSCHED_CPU
 
 extern psched_tdiff_t psched_clock_per_hz;
 extern int psched_clock_scale;
 
+#define PSCHED_EXPORTLIST_2 EXPORT_SYMBOL(psched_clock_per_hz); \
+                            EXPORT_SYMBOL(psched_clock_scale);
+
 #define PSCHED_US2JIFFIE(delay) (((delay)+psched_clock_per_hz-1)/psched_clock_per_hz)
 
 #if CPU == 586 || CPU == 686
 
 #define PSCHED_GET_TIME(stamp) \
-({ u32 hi, lo; \
-   __asm__ __volatile__ (".byte 0x0f,0x31" :"=a" (lo), "=d" (hi)); \
-   (stamp) = ((((u64)hi)<<32) + lo)>>psched_clock_scale; \
+({ u64 __cur; \
+   __asm__ __volatile__ (".byte 0x0f,0x31" :"=A" (__cur)); \
+   (stamp) = __cur>>psched_clock_scale; \
 })
 
 #elif defined (__alpha__)
@@ -191,6 +229,9 @@
    (stamp) = (psched_time_base + __res)>>psched_clock_scale; \
 })
 
+#define PSCHED_EXPORTLIST_1 EXPORT_SYMBOL(psched_time_base); \
+                            EXPORT_SYMBOL(psched_time_mark);
+
 #else
 
 #error PSCHED_CLOCK_SOURCE=PSCHED_CPU is not supported on this arch.
@@ -219,13 +260,15 @@
 	   __delta; \
 })
 
+extern int psched_tod_diff(int delta_sec, int bound);
+
 #define PSCHED_TDIFF_SAFE(tv1, tv2, bound, guard) \
 ({ \
 	   int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \
 	   int __delta = (tv1).tv_usec - (tv2).tv_usec; \
 	   switch (__delta_sec) { \
 	   default: \
-		   __delta = (bound); guard; break; \
+		   __delta = psched_tod_diff(__delta_sec, bound); guard; break; \
 	   case 2: \
 		   __delta += 1000000; \
 	   case 1: \
@@ -290,6 +333,8 @@
 	u32		index;
 
 	int		action;
+	int		result;
+	u32		ewma_rate;
 	u32		burst;
 	u32		mtu;
 
@@ -298,10 +343,12 @@
 	psched_time_t	t_c;
 	struct qdisc_rate_table *R_tab;
 	struct qdisc_rate_table *P_tab;
+
+	struct tc_stats	stats;
 };
 
 extern void tcf_police_destroy(struct tcf_police *p);
-extern struct tcf_police * tcf_police_locate(struct rtattr *rta);
+extern struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est);
 extern int tcf_police_dump(struct sk_buff *skb, struct tcf_police *p);
 extern int tcf_police(struct sk_buff *skb, struct tcf_police *p);
 
@@ -327,7 +374,6 @@
 void qdisc_reset(struct Qdisc *qdisc);
 void qdisc_destroy(struct Qdisc *qdisc);
 struct Qdisc * qdisc_create_dflt(struct device *dev, struct Qdisc_ops *ops);
-struct Qdisc * dev_set_scheduler(struct device *dev, struct Qdisc *qdisc);
 int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt);
 void qdisc_kill_estimator(struct tc_stats *stats);
 struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct rtattr *tab);
diff -u --recursive --new-file v2.2.3/linux/include/net/route.h linux/include/net/route.h
--- v2.2.3/linux/include/net/route.h	Sat Sep  5 16:46:42 1998
+++ linux/include/net/route.h	Sun Mar 21 14:19:16 1999
@@ -57,6 +57,9 @@
 	__u32			src;
 	int			iif;
 	int			oif;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	__u32			fwmark;
+#endif
 	__u8			tos;
 	__u8			scope;
 };
@@ -92,6 +95,16 @@
 };
 
 extern struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
+
+struct ip_rt_acct
+{
+	__u32 	o_bytes;
+	__u32 	o_packets;
+	__u32 	i_bytes;
+	__u32 	i_packets;
+};
+
+extern struct ip_rt_acct ip_rt_acct[256];
 
 extern void		ip_rt_init(void);
 extern void		ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
diff -u --recursive --new-file v2.2.3/linux/include/net/sock.h linux/include/net/sock.h
--- v2.2.3/linux/include/net/sock.h	Wed Mar 10 15:29:50 1999
+++ linux/include/net/sock.h	Sun Mar 21 14:19:17 1999
@@ -918,6 +918,18 @@
 extern void net_reset_timer (struct sock *, int, unsigned long);
 extern void net_timer (unsigned long);
 
+extern __inline__ int gfp_any(void)
+{
+	return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+}
+
+#ifdef __SMP__
+#define net_serialize_enter()	start_bh_atomic()
+#define net_serialize_leave()	end_bh_atomic()
+#else
+#define net_serialize_enter()	barrier();
+#define net_serialize_leave()	barrier();
+#endif
 
 /* 
  *	Enable debug/info messages 
diff -u --recursive --new-file v2.2.3/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.2.3/linux/include/net/tcp.h	Tue Feb 23 15:21:35 1999
+++ linux/include/net/tcp.h	Sun Mar 21 14:19:23 1999
@@ -487,9 +487,7 @@
 extern int			tcp_v4_rcv(struct sk_buff *skb,
 					   unsigned short len);
 
-extern int			tcp_do_sendmsg(struct sock *sk, 
-					       int iovlen, struct iovec *iov,
-					       int flags);
+extern int			tcp_do_sendmsg(struct sock *sk, struct msghdr *msg);
 
 extern int			tcp_ioctl(struct sock *sk, 
 					  int cmd, 
@@ -808,6 +806,23 @@
 		(tcp_packets_in_flight(tp) < tp->snd_cwnd) &&
 		!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
 		tp->retransmits == 0);
+}
+
+/* Push out any pending frames which were held back due to
+ * TCP_CORK or attempt at coalescing tiny packets.
+ * The socket must be locked by the caller.
+ */
+static __inline__ void tcp_push_pending_frames(struct sock *sk, struct tcp_opt *tp)
+{
+	if(tp->send_head) {
+		if(tcp_snd_test(sk, tp->send_head))
+			tcp_write_xmit(sk);
+		else if(tp->packets_out == 0 && !tp->pending) {
+			/* We held off on this in tcp_send_skb() */
+			tp->pending = TIME_PROBE0;
+			tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+		}
+	}
 }
 
 /* This tells the input processing path that an ACK should go out
diff -u --recursive --new-file v2.2.3/linux/include/scsi/scsi.h linux/include/scsi/scsi.h
--- v2.2.3/linux/include/scsi/scsi.h	Tue Dec  2 11:41:45 1997
+++ linux/include/scsi/scsi.h	Mon Mar 15 16:11:31 1999
@@ -133,6 +133,7 @@
 #define TYPE_MOD            0x07    /* Magneto-optical disk - 
 				     * - treated as TYPE_DISK */
 #define TYPE_MEDIUM_CHANGER 0x08
+#define TYPE_ENCLOSURE      0x0d    /* Enclosure Services Device */
 #define TYPE_NO_LUN         0x7f
 
 /*
diff -u --recursive --new-file v2.2.3/linux/kernel/fork.c linux/kernel/fork.c
--- v2.2.3/linux/kernel/fork.c	Wed Mar 10 15:29:50 1999
+++ linux/kernel/fork.c	Wed Mar 17 12:47:19 1999
@@ -510,6 +510,7 @@
 	down(&current->mm->mmap_sem);
 	lock_kernel();
 
+	retval = -EAGAIN;
 	if (p->user) {
 		if (atomic_read(&p->user->count) >= p->rlim[RLIMIT_NPROC].rlim_cur)
 			goto bad_fork_free;
@@ -518,7 +519,6 @@
 	{
 		struct task_struct **tslot;
 		tslot = find_empty_process();
-		retval = -EAGAIN;
 		if (!tslot)
 			goto bad_fork_free;
 		p->tarray_ptr = tslot;
diff -u --recursive --new-file v2.2.3/linux/kernel/sched.c linux/kernel/sched.c
--- v2.2.3/linux/kernel/sched.c	Tue Feb 23 15:21:35 1999
+++ linux/kernel/sched.c	Mon Mar 15 16:11:55 1999
@@ -3,7 +3,6 @@
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
- *  1996-04-21	Modified by Ulrich Windl to make NTP work
  *  1996-12-23  Modified by Dave Grothe to fix bugs in semaphores and
  *              make semaphores SMP safe
  *  1997-01-28  Modified by Finn Arne Gangstad to make timers scale better.
@@ -15,6 +14,7 @@
  *		serialize accesses to xtime/lost_ticks).
  *				Copyright (C) 1998  Andrea Arcangeli
  *  1998-12-28  Implemented better SMP scheduling by Ingo Molnar
+ *  1999-03-10	Improved NTP compatibility by Ulrich Windl
  */
 
 /*
@@ -62,7 +62,7 @@
  * phase-lock loop variables
  */
 /* TIME_ERROR prevents overwriting the CMOS clock */
-int time_state = TIME_ERROR;	/* clock synchronization status */
+int time_state = TIME_OK;	/* clock synchronization status */
 int time_status = STA_UNSYNC;	/* clock status bits */
 long time_offset = 0;		/* time adjustment (us) */
 long time_constant = 2;		/* pll time constant */
@@ -585,7 +585,7 @@
 		long prevstate;
 		cycles_t last_schedule;
 	} schedule_data;
-	char __pad [L1_CACHE_BYTES];
+	char __pad [SMP_CACHE_BYTES];
 } aligned_data [NR_CPUS] __cacheline_aligned = { {{&init_task,0}}};
 
 
@@ -1138,7 +1138,6 @@
     time_maxerror += time_tolerance >> SHIFT_USEC;
     if ( time_maxerror > NTP_PHASE_LIMIT ) {
         time_maxerror = NTP_PHASE_LIMIT;
-	time_state = TIME_ERROR;	/* p. 17, sect. 4.3, (b) */
 	time_status |= STA_UNSYNC;
     }
 
diff -u --recursive --new-file v2.2.3/linux/kernel/softirq.c linux/kernel/softirq.c
--- v2.2.3/linux/kernel/softirq.c	Tue Dec 22 14:16:59 1998
+++ linux/kernel/softirq.c	Sun Mar 21 07:22:00 1999
@@ -62,6 +62,7 @@
 		if (hardirq_trylock(cpu)) {
 			__sti();
 			run_bottom_halves();
+			__cli();
 			hardirq_endlock(cpu);
 		}
 		softirq_endlock(cpu);
diff -u --recursive --new-file v2.2.3/linux/kernel/time.c linux/kernel/time.c
--- v2.2.3/linux/kernel/time.c	Wed Jan 20 23:14:07 1999
+++ linux/kernel/time.c	Thu Mar 11 23:26:03 1999
@@ -96,7 +96,6 @@
 	xtime.tv_usec = 0;
 	time_adjust = 0;	/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
-	time_state = TIME_ERROR;	/* p. 24, (a) */
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
 	sti();
@@ -221,7 +220,7 @@
 int do_adjtimex(struct timex *txc)
 {
         long ltemp, mtemp, save_adjust;
-	int error = 0;
+	int result = time_state;	/* mostly `TIME_OK' */
 
 	/* In order to modify anything, you gotta be super-user! */
 	if (txc->modes && !capable(CAP_SYS_TIME))
@@ -250,16 +249,13 @@
 	/* If there are input parameters, then process them */
 	if (txc->modes)
 	{
-	    if (time_state == TIME_ERROR)
-		time_state = TIME_OK;		/* reset error -- why? */
-
 	    if (txc->modes & ADJ_STATUS)	/* only set allowed bits */
 		time_status =  (txc->status & ~STA_RONLY) |
 			      (time_status & STA_RONLY);
 
 	    if (txc->modes & ADJ_FREQUENCY) {	/* p. 22 */
 		if (txc->freq > MAXFREQ || txc->freq < -MAXFREQ) {
-		    error = -EINVAL;
+		    result = -EINVAL;
 		    goto leave;
 		}
 		time_freq = txc->freq - pps_freq;
@@ -267,7 +263,7 @@
 
 	    if (txc->modes & ADJ_MAXERROR) {
 		if (txc->maxerror < 0 || txc->maxerror >= NTP_PHASE_LIMIT) {
-		    error = -EINVAL;
+		    result = -EINVAL;
 		    goto leave;
 		}
 		time_maxerror = txc->maxerror;
@@ -275,7 +271,7 @@
 
 	    if (txc->modes & ADJ_ESTERROR) {
 		if (txc->esterror < 0 || txc->esterror >= NTP_PHASE_LIMIT) {
-		    error = -EINVAL;
+		    result = -EINVAL;
 		    goto leave;
 		}
 		time_esterror = txc->esterror;
@@ -283,7 +279,7 @@
 
 	    if (txc->modes & ADJ_TIMECONST) {	/* p. 24 */
 		if (txc->constant < 0) {	/* NTP v4 uses values > 6 */
-		    error = -EINVAL;
+		    result = -EINVAL;
 		    goto leave;
 		}
 		time_constant = txc->constant;
@@ -329,7 +325,7 @@
 			    else
 			        time_freq += ltemp >> SHIFT_KH;
 			} else /* calibration interval too short (p. 12) */
-				time_state = TIME_ERROR;
+				result = TIME_ERROR;
 		    } else {	/* PLL mode */
 		        if (mtemp < MAXSEC) {
 			    ltemp *= mtemp;
@@ -342,7 +338,7 @@
 						       time_constant +
 						       SHIFT_KF - SHIFT_USEC);
 			} else /* calibration interval too long (p. 12) */
-				time_state = TIME_ERROR;
+				result = TIME_ERROR;
 		    }
 		    if (time_freq > time_tolerance)
 		        time_freq = time_tolerance;
@@ -354,7 +350,7 @@
 		/* if the quartz is off by more than 10% something is
 		   VERY wrong ! */
 		if (txc->tick < 900000/HZ || txc->tick > 1100000/HZ) {
-		    error = -EINVAL;
+		    result = -EINVAL;
 		    goto leave;
 		}
 		tick = txc->tick;
@@ -370,7 +366,7 @@
 	    || ((time_status & STA_PPSFREQ) != 0
 		&& (time_status & (STA_PPSWANDER|STA_PPSERROR)) != 0))
 	    /* p. 24, (d) */
-		time_state = TIME_ERROR;
+		result = TIME_ERROR;
 	
 	if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
 	    txc->offset	   = save_adjust;
@@ -399,7 +395,7 @@
 	txc->stbcnt	   = pps_stbcnt;
 
 	sti();
-	return(error < 0 ? error : time_state);
+	return(result);
 }
 
 asmlinkage int sys_adjtimex(struct timex *txc_p)
diff -u --recursive --new-file v2.2.3/linux/lib/vsprintf.c linux/lib/vsprintf.c
--- v2.2.3/linux/lib/vsprintf.c	Thu Nov 19 09:56:29 1998
+++ linux/lib/vsprintf.c	Mon Mar 15 11:19:05 1999
@@ -261,6 +261,10 @@
 			}
 			continue;
 
+		case '%':
+			*str++ = '%';
+			continue;
+
 		/* integer number formats - set up the flags and "break" */
 		case 'o':
 			base = 8;
@@ -279,8 +283,7 @@
 			break;
 
 		default:
-			if (*fmt != '%')
-				*str++ = '%';
+			*str++ = '%';
 			if (*fmt)
 				*str++ = *fmt;
 			else
@@ -313,4 +316,3 @@
 	va_end(args);
 	return i;
 }
-
diff -u --recursive --new-file v2.2.3/linux/net/core/dev.c linux/net/core/dev.c
--- v2.2.3/linux/net/core/dev.c	Fri Jan  8 22:36:25 1999
+++ linux/net/core/dev.c	Sun Mar 21 07:22:00 1999
@@ -232,7 +232,9 @@
 	{
 		if(pt==(*pt1))
 		{
+			net_serialize_enter();
 			*pt1=pt->next;
+			net_serialize_leave();
 #ifdef CONFIG_NET_FASTROUTE
 			if (pt->data)
 				netdev_fastroute_obstacles--;
@@ -328,6 +330,12 @@
 	return dev;
 }
 
+void netdev_state_change(struct device *dev)
+{
+	if (dev->flags&IFF_UP)
+		notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+}
+
 
 /*
  *	Find and possibly load an interface.
@@ -422,7 +430,7 @@
 		int i;
 
 		for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++)
-			dst_release(xchg(dev->fastpath+i, NULL));
+			dst_release_irqwait(xchg(dev->fastpath+i, NULL));
 	}
 }
 
@@ -895,22 +903,6 @@
 #endif
 
 		/*
-		 * 	Fetch the packet protocol ID. 
-		 */
-		
-		type = skb->protocol;
-
-		
-#ifdef CONFIG_BRIDGE
-		/*
-		 *	If we are bridging then pass the frame up to the
-		 *	bridging code (if this protocol is to be bridged).
-		 *      If it is bridged then move on
-		 */
-		handle_bridge(skb, type); 
-#endif
-		
-		/*
 	 	 *	Bump the pointer to the next structure.
 		 * 
 		 *	On entry to the protocol layer. skb->data and
@@ -927,11 +919,26 @@
 		}
 
 		/*
+		 * 	Fetch the packet protocol ID. 
+		 */
+
+		type = skb->protocol;
+
+#ifdef CONFIG_BRIDGE
+		/*
+		 *	If we are bridging then pass the frame up to the
+		 *	bridging code (if this protocol is to be bridged).
+		 *      If it is bridged then move on
+		 */
+		handle_bridge(skb, type); 
+#endif
+
+		/*
 		 *	We got a packet ID.  Now loop over the "known protocols"
 		 * 	list. There are two lists. The ptype_all list of taps (normally empty)
 		 *	and the main protocol list which is hashed perfectly for normal protocols.
 		 */
-		
+
 		pt_prev = NULL;
 		for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
 		{
@@ -1536,8 +1543,7 @@
 			return 0;
 
 		case SIOCSIFTXQLEN:
-			/* Why <2? 0 and 1 are valid values. --ANK (980807) */
-			if(/*ifr->ifr_qlen<2 ||*/ ifr->ifr_qlen>1024)
+			if(ifr->ifr_qlen<0)
 				return -EINVAL;
 			dev->tx_queue_len = ifr->ifr_qlen;
 			return 0;
@@ -1817,8 +1823,11 @@
 	/* And unlink it from device chain. */
 	for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
 		if (d == dev) {
+			net_serialize_enter();
 			*dp = d->next;
+			net_serialize_leave();
 			d->next = NULL;
+
 			if (dev->destructor)
 				dev->destructor(dev);
 			return 0;
@@ -1977,7 +1986,9 @@
 			/*
 			 *	It failed to come up. Unhook it.
 			 */
+			net_serialize_enter();
 			*dp = dev->next;
+			net_serialize_leave();
 		} 
 		else
 		{
diff -u --recursive --new-file v2.2.3/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v2.2.3/linux/net/core/dev_mcast.c	Mon Jan 12 15:28:19 1998
+++ linux/net/core/dev_mcast.c	Sun Mar 21 07:22:00 1999
@@ -56,10 +56,9 @@
  *	protocols without doing damage to the protocols when it deletes the
  *	entries. It also helps IP as it tracks overlapping maps.
  *
- *	BUGGGG! IPv6 calls dev_mac_add/delete from BH, it means
- *	that all the functions in this file are racy. [NOT FIXED] --ANK
+ *	Device mc lists are changed by bh at least if IPv6 is enabled,
+ *	so that it must be bh protected.
  */
- 
 
 /*
  *	Update the multicast list into the physical NIC controller.
@@ -77,11 +76,13 @@
 	/*
 	 *	Devices with no set multicast don't get set 
 	 */
-	 
+
 	if(dev->set_multicast_list==NULL)
 		return;
-		
+
+	start_bh_atomic();
 	dev->set_multicast_list(dev);
+	end_bh_atomic();
 }
   
 /*
@@ -90,8 +91,10 @@
  
 int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl)
 {
+	int err = 0;
 	struct dev_mc_list *dmi, **dmip;
 
+	start_bh_atomic();
 	for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) {
 		/*
 		 *	Find the entry we want to delete. The device could
@@ -102,10 +105,10 @@
 				int old_glbl = dmi->dmi_gusers;
 				dmi->dmi_gusers = 0;
 				if (old_glbl == 0)
-					return -ENOENT;
+					break;
 			}
 			if(--dmi->dmi_users)
-				return 0;
+				goto done;
 
 			/*
 			 *	Last user. So delete the entry.
@@ -117,11 +120,15 @@
 			 *	We have altered the list, so the card
 			 *	loaded filter is now wrong. Fix it
 			 */
+			end_bh_atomic();
 			dev_mc_upload(dev);
 			return 0;
 		}
 	}
-	return -ENOENT;
+	err = -ENOENT;
+done:
+	end_bh_atomic();
+	return err;
 }
 
 /*
@@ -130,30 +137,27 @@
  
 int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
 {
-	struct dev_mc_list *dmi;
+	int err = 0;
+	struct dev_mc_list *dmi, *dmi1;
+
+	dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any());
 
+	start_bh_atomic();
 	for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) {
 		if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) {
 			if (glbl) {
 				int old_glbl = dmi->dmi_gusers;
 				dmi->dmi_gusers = 1;
 				if (old_glbl)
-					return 0;
+					goto done;
 			}
 			dmi->dmi_users++;
-			return 0;
+			goto done;
 		}
 	}
 
-	/* GFP_ATOMIC!! It is used by IPv6 from interrupt,
-	   when new address arrives.
-
-	   Particularly, it means that this part of code is weirdly
-	   racy, and needs numerous *_bh_atomic --ANK
-	 */
-	dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi), GFP_ATOMIC);
-	if (dmi==NULL)
-		return -ENOBUFS;
+	if ((dmi=dmi1)==NULL)
+		return -ENOMEM;
 	memcpy(dmi->dmi_addr, addr, alen);
 	dmi->dmi_addrlen=alen;
 	dmi->next=dev->mc_list;
@@ -161,8 +165,15 @@
 	dmi->dmi_gusers=glbl ? 1 : 0;
 	dev->mc_list=dmi;
 	dev->mc_count++;
+	end_bh_atomic();
 	dev_mc_upload(dev);
 	return 0;
+
+done:
+	end_bh_atomic();
+	if (dmi1)
+		kfree(dmi1);
+	return err;
 }
 
 /*
@@ -171,6 +182,7 @@
 
 void dev_mc_discard(struct device *dev)
 {
+	start_bh_atomic();
 	while (dev->mc_list!=NULL) {
 		struct dev_mc_list *tmp=dev->mc_list;
 		dev->mc_list=tmp->next;
@@ -179,6 +191,7 @@
 		kfree_s(tmp,sizeof(*tmp));
 	}
 	dev->mc_count=0;
+	end_bh_atomic();
 }
 
 #ifdef CONFIG_PROC_FS
@@ -189,7 +202,9 @@
 	struct dev_mc_list *m;
 	int len=0;
 	struct device *dev;
-	
+
+	start_bh_atomic();
+
 	for (dev = dev_base; dev; dev = dev->next) {
 		for (m = dev->mc_list; m; m = m->next) {
 			int i;
@@ -214,10 +229,13 @@
 	*eof = 1;
 
 done:
+	end_bh_atomic();
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
 		len=length;
+	if(len<0)
+		len=0;
 	return len;
 }
 #endif
diff -u --recursive --new-file v2.2.3/linux/net/core/filter.c linux/net/core/filter.c
--- v2.2.3/linux/net/core/filter.c	Tue Feb 23 15:21:35 1999
+++ linux/net/core/filter.c	Sun Mar 21 07:22:00 1999
@@ -441,8 +441,9 @@
 
 	if ((err = sk_chk_filter(fp->insns, fp->len))==0) {
 		struct sk_filter *old_fp = sk->filter;
+		net_serialize_enter();
 		sk->filter = fp;
-		wmb();
+		net_serialize_leave();
 		fp = old_fp;
 	}
 
diff -u --recursive --new-file v2.2.3/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.2.3/linux/net/core/neighbour.c	Mon Oct  5 13:13:47 1998
+++ linux/net/core/neighbour.c	Sun Mar 21 07:22:00 1999
@@ -25,8 +25,36 @@
 #endif
 #include <net/neighbour.h>
 #include <net/dst.h>
+#include <net/sock.h>
 #include <linux/rtnetlink.h>
 
+/*
+   NOTE. The most unpleasent question is serialization of
+   accesses to resolved addresses. The problem is that addresses
+   are modified by bh, but they are referenced from normal
+   kernel thread. Before today no locking was made.
+   My reasoning was that corrupted address token will be copied
+   to packet with cosmologically small probability
+   (it is even difficult to estimate such small number)
+   and it is very silly to waste cycles in fast path to lock them.
+
+   But now I changed my mind, but not because previous statement
+   is wrong. Actually, neigh->ha MAY BE not opaque byte array,
+   but reference to some private data. In this case even neglibible
+   corruption probability becomes bug.
+
+   - hh cache is protected by rwlock. It assumes that
+     hh cache update procedure is short and fast, and that
+     read_lock is cheaper than start_bh_atomic().
+   - ha tokens, saved in neighbour entries, are protected
+     by bh_atomic().
+   - no protection is made in /proc reading. It is OK, because
+     /proc is broken by design in any case, and
+     corrupted output is normal behaviour there.
+
+     --ANK (981025)
+ */
+
 #define NEIGH_DEBUG 1
 
 #define NEIGH_PRINTK(x...) printk(x)
@@ -48,6 +76,7 @@
 #ifdef CONFIG_ARPD
 static void neigh_app_notify(struct neighbour *n);
 #endif
+static int pneigh_ifdown(struct neigh_table *tbl, struct device *dev);
 
 static int neigh_glbl_allocs;
 static struct neigh_table *neigh_tables;
@@ -83,8 +112,20 @@
 
 		np = &tbl->hash_buckets[i];
 		while ((n = *np) != NULL) {
+			/* Neighbour record may be discarded if:
+			   - nobody refers to it.
+			   - it is not premanent
+			   - (NEW and probably wrong)
+			     INCOMPLETE entries are kept at least for
+			     n->parms->retrans_time, otherwise we could
+			     flood network with resolution requests.
+			     It is not clear, what is better table overflow
+			     or flooding.
+			 */
 			if (atomic_read(&n->refcnt) == 0 &&
-			    !(n->nud_state&NUD_PERMANENT)) {
+			    !(n->nud_state&NUD_PERMANENT) &&
+			    (n->nud_state != NUD_INCOMPLETE ||
+			     jiffies - n->used > n->parms->retrans_time)) {
 				*np = n->next;
 				n->tbl = NULL;
 				tbl->entries--;
@@ -149,6 +190,7 @@
 
 	del_timer(&tbl->proxy_timer);
 	skb_queue_purge(&tbl->proxy_queue);
+	pneigh_ifdown(tbl, dev);
 	end_bh_atomic();
 	return 0;
 }
@@ -295,7 +337,9 @@
 
 	for (np = &tbl->phash_buckets[hash_val]; (n=*np) != NULL; np = &n->next) {
 		if (memcmp(n->key, pkey, key_len) == 0 && n->dev == dev) {
+			net_serialize_enter();
 			*np = n->next;
+			net_serialize_leave();
 			if (tbl->pdestructor)
 				tbl->pdestructor(n);
 			kfree(n);
@@ -305,6 +349,30 @@
 	return -ENOENT;
 }
 
+static int pneigh_ifdown(struct neigh_table *tbl, struct device *dev)
+{
+	struct pneigh_entry *n, **np;
+	u32 h;
+
+	for (h=0; h<=PNEIGH_HASHMASK; h++) {
+		np = &tbl->phash_buckets[h]; 
+		for (np = &tbl->phash_buckets[h]; (n=*np) != NULL; np = &n->next) {
+			if (n->dev == dev || dev == NULL) {
+				net_serialize_enter();
+				*np = n->next;
+				net_serialize_leave();
+				if (tbl->pdestructor)
+					tbl->pdestructor(n);
+				kfree(n);
+				continue;
+			}
+			np = &n->next;
+		}
+	}
+	return -ENOENT;
+}
+
+
 /*
  *	neighbour must already be out of the table;
  *
@@ -516,11 +584,11 @@
 		return;
 	}
 
-	neigh->probes++;
 	neigh->timer.expires = now + neigh->parms->retrans_time;
 	add_timer(&neigh->timer);
 
 	neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue));
+	neigh->probes++;
 }
 
 int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
@@ -542,6 +610,7 @@
 				add_timer(&neigh->timer);
 
 				neigh->ops->solicit(neigh, skb);
+				neigh->probes++;
 			} else {
 				neigh->nud_state = NUD_FAILED;
 				if (skb)
@@ -581,8 +650,11 @@
 		neigh->dev->header_cache_update;
 
 	if (update) {
-		for (hh=neigh->hh; hh; hh=hh->hh_next)
+		for (hh=neigh->hh; hh; hh=hh->hh_next) {
+			write_lock_irq(&hh->hh_lock);
 			update(hh, neigh->dev, neigh->ha);
+			write_unlock_irq(&hh->hh_lock);
+		}
 	}
 }
 
@@ -655,7 +727,7 @@
 		del_timer(&neigh->timer);
 	neigh->nud_state = new;
 	if (lladdr != neigh->ha) {
-		memcpy(neigh->ha, lladdr, dev->addr_len);
+		memcpy(&neigh->ha, lladdr, dev->addr_len);
 		neigh_update_hhs(neigh);
 		neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
 #ifdef CONFIG_ARPD
@@ -764,14 +836,20 @@
 	__skb_pull(skb, skb->nh.raw - skb->data);
 
 	if (neigh_event_send(neigh, skb) == 0) {
+		int err;
 		struct device *dev = neigh->dev;
-		if (dev->hard_header_cache) {
+		if (dev->hard_header_cache && dst->hh == NULL) {
 			start_bh_atomic();
 			if (dst->hh == NULL)
 				neigh_hh_init(neigh, dst, dst->ops->protocol);
+			err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
+			end_bh_atomic();
+		} else {
+			start_bh_atomic();
+			err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
 			end_bh_atomic();
 		}
-		if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len) >= 0)
+		if (err >= 0)
 			return neigh->ops->queue_xmit(skb);
 		kfree_skb(skb);
 		return -EINVAL;
@@ -788,13 +866,17 @@
 
 int neigh_connected_output(struct sk_buff *skb)
 {
+	int err;
 	struct dst_entry *dst = skb->dst;
 	struct neighbour *neigh = dst->neighbour;
 	struct device *dev = neigh->dev;
 
 	__skb_pull(skb, skb->nh.raw - skb->data);
 
-	if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len) >= 0)
+	start_bh_atomic();
+	err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
+	end_bh_atomic();
+	if (err >= 0)
 		return neigh->ops->queue_xmit(skb);
 	kfree_skb(skb);
 	return -EINVAL;
@@ -868,7 +950,6 @@
 			}
 		}
 		p->next = tbl->parms.next;
-		/* ATOMIC_SET */
 		tbl->parms.next = p;
 	}
 	return p;
@@ -882,8 +963,9 @@
 		return;
 	for (p = &tbl->parms.next; *p; p = &(*p)->next) {
 		if (*p == parms) {
-			/* ATOMIC_SET */
+			net_serialize_enter();
 			*p = parms->next;
+			net_serialize_leave();
 #ifdef CONFIG_SYSCTL
 			neigh_sysctl_unregister(parms);
 #endif
@@ -926,14 +1008,15 @@
 	del_timer(&tbl->gc_timer);
 	del_timer(&tbl->proxy_timer);
 	skb_queue_purge(&tbl->proxy_queue);
-	if (tbl->entries)
-		neigh_ifdown(tbl, NULL);
+	neigh_ifdown(tbl, NULL);
 	end_bh_atomic();
 	if (tbl->entries)
 		printk(KERN_CRIT "neighbour leakage\n");
 	for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
 		if (*tp == tbl) {
+			net_serialize_enter();
 			*tp = tbl->next;
+			net_serialize_leave();
 			break;
 		}
 	}
@@ -976,7 +1059,7 @@
 			return -EINVAL;
 
 		start_bh_atomic();
-		n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
+		n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
 		if (n) {
 			err = neigh_update(n, NULL, NUD_FAILED, 1, 0);
 			neigh_release(n);
@@ -1020,7 +1103,7 @@
 		    nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len))
 			return -EINVAL;
 		start_bh_atomic();
-		n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
+		n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
 		if (n) {
 			if (nlh->nlmsg_flags&NLM_F_EXCL)
 				err = -EEXIST;
@@ -1091,7 +1174,7 @@
 	for (h=0; h <= NEIGH_HASHMASK; h++) {
 		if (h < s_h) continue;
 		if (h > s_h)
-			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(int));
+			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
 		start_bh_atomic();
 		for (n = tbl->hash_buckets[h], idx = 0; n;
 		     n = n->next, idx++) {
@@ -1125,7 +1208,7 @@
 		if (family && tbl->family != family)
 			continue;
 		if (t > s_t)
-			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
 		if (neigh_dump_table(tbl, skb, cb) < 0) 
 			break;
 	}
diff -u --recursive --new-file v2.2.3/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v2.2.3/linux/net/core/rtnetlink.c	Sat Sep  5 16:46:42 1998
+++ linux/net/core/rtnetlink.c	Sun Mar 21 07:22:00 1999
@@ -41,7 +41,6 @@
 
 #include <linux/inet.h>
 #include <linux/netdevice.h>
-#include <net/pkt_sched.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/arp.h>
@@ -49,6 +48,7 @@
 #include <net/tcp.h>
 #include <net/udp.h>
 #include <net/sock.h>
+#include <net/pkt_sched.h>
 
 atomic_t rtnl_rlockct;
 struct wait_queue *rtnl_wait;
diff -u --recursive --new-file v2.2.3/linux/net/core/sock.c linux/net/core/sock.c
--- v2.2.3/linux/net/core/sock.c	Wed Mar 10 15:29:52 1999
+++ linux/net/core/sock.c	Sun Mar 21 07:22:00 1999
@@ -7,7 +7,7 @@
  *		handler for protocols to use and generic option handler.
  *
  *
- * Version:	$Id: sock.c,v 1.76 1999/02/23 08:12:29 davem Exp $
+ * Version:	$Id: sock.c,v 1.77 1999/03/21 05:22:26 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -251,12 +251,10 @@
 			break;
 
 		case SO_PRIORITY:
-			if (val >= 0 && val <= 7) 
-			{
-				if(val==7 && !capable(CAP_NET_ADMIN))
-					return -EPERM;
+			if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN)) 
 				sk->priority = val;
-			}			
+			else
+				return(-EPERM);
 			break;
 
 		case SO_LINGER:
@@ -348,8 +346,9 @@
 
 				filter = sk->filter;
 
+				net_serialize_enter();
 				sk->filter = NULL;
-				wmb();
+				net_serialize_leave();
 				
 				if (filter)
 					sk_filter_release(sk, filter);
@@ -504,6 +503,16 @@
 {
 	if (sk->destruct)
 		sk->destruct(sk);
+
+#ifdef CONFIG_FILTER
+	if (sk->filter) {
+		sk_filter_release(sk, sk->filter);
+		sk->filter = NULL;
+	}
+#endif
+
+	if (atomic_read(&sk->omem_alloc))
+		printk(KERN_DEBUG "sk_free: optmem leakage (%d bytes) detected.\n", atomic_read(&sk->omem_alloc));
 
 #ifdef CONFIG_FILTER
 	if (sk->filter) {
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.2.3/linux/net/ipv4/Config.in	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/Config.in	Sun Mar 21 07:22:00 1999
@@ -32,6 +32,9 @@
 	fi
     fi
     bool 'IP: always defragment (required for masquerading)' CONFIG_IP_ALWAYS_DEFRAG
+    if [ "$CONFIG_IP_MULTIPLE_TABLES" = "y" ]; then
+	bool 'IP: use FWMARK value as routing key' CONFIG_IP_ROUTE_FWMARK
+    fi
   fi
 fi
 if [ "$CONFIG_IP_FIREWALL" = "y" ]; then
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.2.3/linux/net/ipv4/af_inet.c	Tue Feb 23 15:21:35 1999
+++ linux/net/ipv4/af_inet.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.83 1999/02/22 13:54:18 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.85 1999/03/21 05:22:28 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -53,6 +53,7 @@
  *		David S. Miller	:	New socket lookup architecture.
  *					Some other random speedups.
  *		Cyrus Durgin	:	Cleaned up file for kmod hacks.
+ *		Andi Kleen	:	Fix inet_stream_connect TCP race.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -175,8 +176,6 @@
 	if(sk->opt)
 		kfree(sk->opt);
 	dst_release(sk->dst_cache);
-	if (atomic_read(&sk->omem_alloc))
-		printk(KERN_DEBUG "kill_sk_now: optmem leakage (%d bytes) detected.\n", atomic_read(&sk->omem_alloc));
 	sk_free(sk);
 }
 
@@ -613,15 +612,16 @@
 	}
 
 	if(sock->state == SS_CONNECTING) {
+		/* Note: tcp_connected contains SYN_RECV, which may cause
+		   bogus results here. -AK */ 
 		if(tcp_connected(sk->state)) {
 			sock->state = SS_CONNECTED;
 			return 0;
 		}
-		if(sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK)) {
-			if(sk->err)
-				return sock_error(sk);
+		if (sk->zapped || sk->err)
+			goto sock_error;
+		if (flags & O_NONBLOCK)
 			return -EALREADY;
-		}
 	} else {
 		/* We may need to bind the socket. */
 		if (inet_autobind(sk) != 0)
@@ -629,15 +629,17 @@
 		if (sk->prot->connect == NULL) 
 			return(-EOPNOTSUPP);
 		err = sk->prot->connect(sk, uaddr, addr_len);
+		/* Note: there is a theoretical race here when an wake up
+		   occurred before inet_wait_for_connect is entered. In 2.3
+		   the wait queue setup should be moved before the low level
+		   connect call. -AK*/
 		if (err < 0)
 			return(err);
   		sock->state = SS_CONNECTING;
 	}
 	
-	if (sk->state > TCP_FIN_WAIT2 && sock->state == SS_CONNECTING) {
-		sock->state = SS_UNCONNECTED;
-		return sock_error(sk);
-	}
+	if (sk->state > TCP_FIN_WAIT2 && sock->state == SS_CONNECTING)
+		goto sock_error;
 
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) 
 	  	return (-EINPROGRESS);
@@ -649,17 +651,19 @@
 	}
 
 	sock->state = SS_CONNECTED;
-	if ((sk->state != TCP_ESTABLISHED) && sk->err) {
-		/* This is ugly but needed to fix a race in the ICMP error handler */
-		if (sk->protocol == IPPROTO_TCP && sk->zapped) { 
-			lock_sock(sk);  
-			tcp_set_state(sk, TCP_CLOSE);
-			release_sock(sk); 
-		}
-		sock->state = SS_UNCONNECTED;
-		return sock_error(sk);
-	}
+	if ((sk->state != TCP_ESTABLISHED) && sk->err)
+		goto sock_error; 
 	return(0);
+
+sock_error:	
+	/* This is ugly but needed to fix a race in the ICMP error handler */
+	if (sk->zapped && sk->state != TCP_CLOSE) { 
+		lock_sock(sk);  
+		tcp_set_state(sk, TCP_CLOSE);
+		release_sock(sk); 
+	}
+	sock->state = SS_UNCONNECTED;
+	return sock_error(sk);
 }
 
 /*
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.2.3/linux/net/ipv4/arp.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/arp.c	Sun Mar 21 07:22:00 1999
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.75 1998/11/16 04:51:56 davem Exp $
+ * Version:	$Id: arp.c,v 1.77 1999/03/21 05:22:30 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
@@ -294,7 +294,7 @@
 
 static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
 {
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+	dst_link_failure(skb);
 	kfree_skb(skb);
 }
 
@@ -401,8 +401,12 @@
 
 	if (dev == NULL)
 		return 0;
-	if (dst->neighbour == NULL)
-		dst->neighbour = __neigh_lookup(&arp_tbl, &((struct rtable*)dst)->rt_gateway, dev, 1);
+	if (dst->neighbour == NULL) {
+		u32 nexthop = ((struct rtable*)dst)->rt_gateway;
+		if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT))
+			nexthop = 0;
+		dst->neighbour = __neigh_lookup(&arp_tbl, &nexthop, dev, 1);
+	}
 	return (dst->neighbour != NULL);
 }
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.2.3/linux/net/ipv4/devinet.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/devinet.c	Sun Mar 21 07:22:00 1999
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.25 1999/01/04 20:14:33 davem Exp $
+ *	Version: $Id: devinet.c,v 1.26 1999/03/21 05:22:31 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -138,7 +138,9 @@
 #ifdef CONFIG_SYSCTL
 	devinet_sysctl_unregister(&in_dev->cnf);
 #endif
+	net_serialize_enter();
 	in_dev->dev->ip_ptr = NULL;
+	net_serialize_leave();
 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
 	kfree(in_dev);
 }
@@ -172,7 +174,10 @@
 				ifap1 = &ifa->ifa_next;
 				continue;
 			}
+			net_serialize_enter();
 			*ifap1 = ifa->ifa_next;
+			net_serialize_leave();
+
 			rtmsg_ifa(RTM_DELADDR, ifa);
 			notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa);
 			inet_free_ifa(ifa);
@@ -181,8 +186,9 @@
 
 	/* 2. Unlink it */
 
+	net_serialize_enter();
 	*ifap = ifa1->ifa_next;
-
+	net_serialize_leave();
 
 	/* 3. Announce address deletion */
 
@@ -238,8 +244,9 @@
 	}
 
 	ifa->ifa_next = *ifap;
-	/* ATOMIC_SET */
+	net_serialize_enter();
 	*ifap = ifa;
+	net_serialize_leave();
 
 	/* Send message first, then call notifier.
 	   Notifier will trigger FIB update, so that
@@ -650,8 +657,25 @@
 		if (!dst || inet_ifa_match(dst, ifa))
 			return addr;
 	} endfor_ifa(in_dev);
+	
+	if (addr || scope >= RT_SCOPE_LINK)
+		return addr;
 
-	return addr;
+	/* Not loopback addresses on loopback should be preferred
+	   in this case. It is importnat that lo is the first interface
+	   in dev_base list.
+	 */
+	for (dev=dev_base; dev; dev=dev->next) {
+		if ((in_dev=dev->ip_ptr) == NULL)
+			continue;
+
+		for_primary_ifa(in_dev) {
+			if (ifa->ifa_scope <= scope)
+				return ifa->ifa_local;
+		} endfor_ifa(in_dev);
+	}
+
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.2.3/linux/net/ipv4/fib_frontend.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/fib_frontend.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:	$Id: fib_frontend.c,v 1.14 1999/01/04 20:13:55 davem Exp $
+ * Version:	$Id: fib_frontend.c,v 1.15 1999/03/21 05:22:31 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -189,7 +189,7 @@
  */
 
 int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
-			struct device *dev, u32 *spec_dst)
+			struct device *dev, u32 *spec_dst, u32 *itag)
 {
 	struct in_device *in_dev = dev->ip_ptr;
 	struct rt_key key;
@@ -209,6 +209,8 @@
 	if (res.type != RTN_UNICAST)
 		return -EINVAL;
 	*spec_dst = FIB_RES_PREFSRC(res);
+	if (itag)
+		fib_combine_itag(itag, &res);
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
 #else
@@ -231,6 +233,7 @@
 	if (IN_DEV_RPFILTER(in_dev))
 		return -EINVAL;
 	*spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
+	*itag = 0;
 	return 0;
 }
 
@@ -354,7 +357,7 @@
 	for (t=s_t; t<=RT_TABLE_MAX; t++) {
 		if (t < s_t) continue;
 		if (t > s_t)
-			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
 		if ((tb = fib_get_table(t))==NULL)
 			continue;
 		if (tb->tb_dump(tb, skb, cb) < 0) 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.2.3/linux/net/ipv4/fib_hash.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/fib_hash.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:	$Id: fib_hash.c,v 1.6 1998/10/03 09:37:06 davem Exp $
+ * Version:	$Id: fib_hash.c,v 1.7 1999/03/21 05:22:32 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -302,6 +302,90 @@
 	return 1;
 }
 
+static int fn_hash_last_dflt=-1;
+
+static int fib_detect_death(struct fib_info *fi, int order,
+			    struct fib_info **last_resort, int *last_idx)
+{
+	struct neighbour *n;
+	int state = NUD_NONE;
+
+	n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev);
+	if (n) {
+		state = n->nud_state;
+		neigh_release(n);
+	}
+	if (state==NUD_REACHABLE)
+		return 0;
+	if ((state&NUD_VALID) && order != fn_hash_last_dflt)
+		return 0;
+	if ((state&NUD_VALID) ||
+	    (*last_idx<0 && order > fn_hash_last_dflt)) {
+		*last_resort = fi;
+		*last_idx = order;
+	}
+	return 1;
+}
+
+static void
+fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fib_result *res)
+{
+	int order, last_idx;
+	struct fib_node *f;
+	struct fib_info *fi = NULL;
+	struct fib_info *last_resort;
+	struct fn_hash *t = (struct fn_hash*)tb->tb_data;
+	struct fn_zone *fz = t->fn_zones[0];
+
+	if (fz == NULL)
+		return;
+
+	last_idx = -1;
+	last_resort = NULL;
+	order = -1;
+
+	for (f = fz->fz_hash[0]; f; f = f->fn_next) {
+		struct fib_info *next_fi = FIB_INFO(f);
+
+		if ((f->fn_state&FN_S_ZOMBIE) ||
+		    f->fn_scope != res->scope ||
+		    f->fn_type != RTN_UNICAST)
+			continue;
+
+		if (next_fi->fib_priority > res->fi->fib_priority)
+			break;
+		if (!next_fi->fib_nh[0].nh_gw || next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK)
+			continue;
+		f->fn_state |= FN_S_ACCESSED;
+
+		if (fi == NULL) {
+			if (next_fi != res->fi)
+				break;
+		} else if (!fib_detect_death(fi, order, &last_resort, &last_idx)) {
+			res->fi = fi;
+			fn_hash_last_dflt = order;
+			return;
+		}
+		fi = next_fi;
+		order++;
+	}
+
+	if (order<=0 || fi==NULL) {
+		fn_hash_last_dflt = -1;
+		return;
+	}
+
+	if (!fib_detect_death(fi, order, &last_resort, &last_idx)) {
+		res->fi = fi;
+		fn_hash_last_dflt = order;
+		return;
+	}
+
+	if (last_idx >= 0)
+		res->fi = last_resort;
+	fn_hash_last_dflt = last_idx;
+}
+
 #define FIB_SCAN(f, fp) \
 for ( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fn_next)
 
@@ -476,14 +560,16 @@
 	 */
 
 	new_f->fn_next = f;
-	/* ATOMIC_SET */
 	*fp = new_f;
 	fz->fz_nent++;
 
 	if (del_fp) {
 		f = *del_fp;
 		/* Unlink replaced node */
+		net_serialize_enter();
 		*del_fp = f->fn_next;
+		net_serialize_leave();
+
 		if (!(f->fn_state&FN_S_ZOMBIE))
 			rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
 		if (f->fn_state&FN_S_ACCESSED)
@@ -570,7 +656,10 @@
 		rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
 
 		if (matched != 1) {
+			net_serialize_enter();
 			*del_fp = f->fn_next;
+			net_serialize_leave();
+
 			if (f->fn_state&FN_S_ACCESSED)
 				rt_cache_flush(-1);
 			fn_free_node(f);
@@ -600,7 +689,10 @@
 		struct fib_info *fi = FIB_INFO(f);
 
 		if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) {
+			net_serialize_enter();
 			*fp = f->fn_next;
+			net_serialize_leave();
+
 			fn_free_node(f);
 			found++;
 			continue;
@@ -710,7 +802,7 @@
 	for (h=0; h < fz->fz_divisor; h++) {
 		if (h < s_h) continue;
 		if (h > s_h)
-			memset(&cb->args[3], 0, sizeof(cb->args) - 3*sizeof(int));
+			memset(&cb->args[3], 0, sizeof(cb->args) - 3*sizeof(cb->args[0]));
 		if (fz->fz_hash == NULL || fz->fz_hash[h] == NULL)
 			continue;
 		if (fn_hash_dump_bucket(skb, cb, tb, fz, fz->fz_hash[h]) < 0) {
@@ -732,7 +824,7 @@
 	for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
 		if (m < s_m) continue;
 		if (m > s_m)
-			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(int));
+			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
 		if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
 			cb->args[1] = m;
 			return -1;
@@ -784,6 +876,7 @@
 	tb->tb_insert = fn_hash_insert;
 	tb->tb_delete = fn_hash_delete;
 	tb->tb_flush = fn_hash_flush;
+	tb->tb_select_default = fn_hash_select_default;
 #ifdef CONFIG_RTNETLINK
 	tb->tb_dump = fn_hash_dump;
 #endif
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.2.3/linux/net/ipv4/fib_rules.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/fib_rules.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.7 1998/10/03 09:37:09 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.8 1999/03/21 05:22:33 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -16,6 +16,7 @@
  *
  * Fixes:
  * 		Rani Assaf	:	local_rule cannot be deleted
+ *		Marc Boucher	:	routing by fwmark
  */
 
 #include <linux/config.h>
@@ -63,6 +64,9 @@
 	u32		r_srcmap;
 	u8		r_flags;
 	u8		r_tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	u32		r_fwmark;
+#endif
 	int		r_ifindex;
 #ifdef CONFIG_NET_CLS_ROUTE
 	__u32		r_tclassid;
@@ -88,13 +92,18 @@
 		    rtm->rtm_dst_len == r->r_dst_len &&
 		    (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) &&
 		    rtm->rtm_tos == r->r_tos &&
+#ifdef CONFIG_IP_ROUTE_FWMARK
+		    (!rta[RTA_PROTOINFO-1] || memcmp(RTA_DATA(rta[RTA_PROTOINFO-1]), &r->r_fwmark, 4) == 0) &&
+#endif
 		    (!rtm->rtm_type || rtm->rtm_type == r->r_action) &&
 		    (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
 		    (!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) &&
 		    (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
 			if (r == &local_rule)
 				return -EPERM;
+			net_serialize_enter();
 			*rp = r->r_next;
+			net_serialize_leave();
 			if (r != &default_rule && r != &main_rule)
 				kfree(r);
 			return 0;
@@ -155,6 +164,10 @@
 	new_r->r_srcmask = inet_make_mask(rtm->rtm_src_len);
 	new_r->r_dstmask = inet_make_mask(rtm->rtm_dst_len);
 	new_r->r_tos = rtm->rtm_tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	if (rta[RTA_PROTOINFO-1])
+		memcpy(&new_r->r_fwmark, RTA_DATA(rta[RTA_PROTOINFO-1]), 4);
+#endif
 	new_r->r_action = rtm->rtm_type;
 	new_r->r_flags = rtm->rtm_flags;
 	if (rta[RTA_PRIORITY-1])
@@ -267,14 +280,15 @@
 #ifdef CONFIG_IP_ROUTE_TOS
 		    (r->r_tos && r->r_tos != key->tos) ||
 #endif
+#ifdef CONFIG_IP_ROUTE_FWMARK
+		    (r->r_fwmark && r->r_fwmark != key->fwmark) ||
+#endif
 		    (r->r_ifindex && r->r_ifindex != key->iif))
 			continue;
 
 FRprintk("tb %d r %d ", r->r_table, r->r_action);
 		switch (r->r_action) {
 		case RTN_UNICAST:
-			policy = NULL;
-			break;
 		case RTN_NAT:
 			policy = r;
 			break;
@@ -295,14 +309,23 @@
 			res->r = policy;
 			return 0;
 		}
-		if (err < 0)
+		if (err < 0 && err != -EAGAIN)
 			return err;
-FRprintk("RCONT ");
 	}
 FRprintk("FAILURE\n");
 	return -ENETUNREACH;
 }
 
+void fib_select_default(const struct rt_key *key, struct fib_result *res)
+{
+	if (res->r && res->r->r_action == RTN_UNICAST &&
+	    FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) {
+		struct fib_table *tb;
+		if ((tb = fib_get_table(res->r->r_table)) != NULL)
+			tb->tb_select_default(tb, key, res);
+	}
+}
+
 static int fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct device *dev = ptr;
@@ -337,6 +360,10 @@
 	rtm->rtm_dst_len = r->r_dst_len;
 	rtm->rtm_src_len = r->r_src_len;
 	rtm->rtm_tos = r->r_tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	if (r->r_fwmark)
+		RTA_PUT(skb, RTA_PROTOINFO, 4, &r->r_fwmark);
+#endif
 	rtm->rtm_table = r->r_table;
 	rtm->rtm_protocol = 0;
 	rtm->rtm_scope = 0;
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c
--- v2.2.3/linux/net/ipv4/fib_semantics.c	Tue Feb 23 15:21:35 1999
+++ linux/net/ipv4/fib_semantics.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: semantics.
  *
- * Version:	$Id: fib_semantics.c,v 1.12 1999/01/26 05:33:44 davem Exp $
+ * Version:	$Id: fib_semantics.c,v 1.13 1999/03/21 05:22:34 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -89,7 +89,7 @@
 	{ -EINVAL, RT_SCOPE_UNIVERSE},	/* RTN_BLACKHOLE */
 	{ -EHOSTUNREACH, RT_SCOPE_UNIVERSE},/* RTN_UNREACHABLE */
 	{ -EACCES, RT_SCOPE_UNIVERSE},	/* RTN_PROHIBIT */
-	{ 1, RT_SCOPE_UNIVERSE},	/* RTN_THROW */
+	{ -EAGAIN, RT_SCOPE_UNIVERSE},	/* RTN_THROW */
 #ifdef CONFIG_IP_ROUTE_NAT
 	{ 0, RT_SCOPE_HOST},		/* RTN_NAT */
 #else
@@ -420,7 +420,7 @@
 			unsigned flavor = attr->rta_type;
 			if (flavor) {
 				if (flavor > FIB_MAX_METRICS)
-					goto failure;
+					goto err_inval;
 				fi->fib_metrics[flavor-1] = *(unsigned*)RTA_DATA(attr);
 			}
 			attr = RTA_NEXT(attr, attrlen);
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.2.3/linux/net/ipv4/icmp.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/icmp.c	Sun Mar 21 07:22:00 1999
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@cymru.net>
  *
- *	Version: $Id: icmp.c,v 1.49 1999/02/23 08:12:37 davem Exp $
+ *	Version: $Id: icmp.c,v 1.52 1999/03/21 12:04:11 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -279,6 +279,10 @@
 #include <asm/uaccess.h>
 #include <net/checksum.h>
 
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+
 #define min(a,b)	((a)<(b)?(a):(b))
 
 /*
@@ -369,6 +373,12 @@
  *	works for icmp destinations. This means the rate limiting information
  *	for one "ip object" is shared.
  *
+ *	Note that the same dst_entry fields are modified by functions in 
+ *	route.c too, but these work for packet destinations while xrlim_allow
+ *	works for icmp destinations. This means the rate limiting information
+ *	for one "ip object" is shared - and these ICMPs are twice limited:
+ *	by source and by destination.
+ *
  *	RFC 1812: 4.3.2.8 SHOULD be able to limit error message rate
  *			  SHOULD allow setting of rate limits 
  *
@@ -381,10 +391,10 @@
 
 	now = jiffies;
 	dst->rate_tokens += now - dst->rate_last;
+	dst->rate_last = now;
 	if (dst->rate_tokens > XRLIM_BURST_FACTOR*timeout)
 		dst->rate_tokens = XRLIM_BURST_FACTOR*timeout;
 	if (dst->rate_tokens >= timeout) {
-		dst->rate_last = now;
 		dst->rate_tokens -= timeout;
 		return 1;
 	}
@@ -402,6 +412,10 @@
 	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
 		return 1;
 
+	/* Redirect has its own rate limit mechanism */
+	if (type == ICMP_REDIRECT)
+		return 1;
+
 	/* No rate limit on loopback */
 	if (dst->dev && (dst->dev->flags&IFF_LOOPBACK))
  		return 1;
@@ -522,8 +536,13 @@
 	/*
 	 *	Now check at the protocol level
 	 */
-	if (!rt)
+	if (!rt) {
+#ifndef CONFIG_IP_ALWAYS_DEFRAG
+		if (net_ratelimit())
+			printk(KERN_DEBUG "icmp_send: destinationless packet\n");
+#endif
 		return;
+	}
 	if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
 		return;
 	 
@@ -568,6 +587,11 @@
 	if (rt->rt_flags&RTCF_NAT && IPCB(skb_in)->flags&IPSKB_TRANSLATED) {
 		iph->daddr = rt->key.dst;
 		iph->saddr = rt->key.src;
+	}
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+	if (type==ICMP_DEST_UNREACH && IPCB(skb_in)->flags&IPSKB_MASQUERADED) {
+			ip_fw_unmasq_icmp(skb_in);
 	}
 #endif
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.2.3/linux/net/ipv4/igmp.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/igmp.c	Sun Mar 21 07:22:00 1999
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.28 1998/11/30 15:53:13 davem Exp $
+ *	Version: $Id: igmp.c,v 1.29 1999/03/21 05:22:36 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -97,6 +97,7 @@
 #include <linux/mroute.h>
 #endif
 
+#define IP_MAX_MEMBERSHIPS 20
 
 #ifdef CONFIG_IP_MULTICAST
 
@@ -462,7 +463,9 @@
 	for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
 		if (i->multiaddr==addr) {
 			if (--i->users == 0) {
+				net_serialize_enter();
 				*ip = i->next;
+				net_serialize_leave();
 				igmp_group_dropped(i);
 				if (in_dev->dev->flags & IFF_UP)
 					ip_rt_multicast_event(in_dev);
@@ -610,7 +613,9 @@
 			struct in_device *in_dev;
 			if (--iml->count)
 				return 0;
+			net_serialize_enter();
 			*imlp = iml->next;
+			net_serialize_leave();
 			in_dev = inetdev_by_index(iml->multi.imr_ifindex);
 			if (in_dev)
 				ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
@@ -684,6 +689,8 @@
 	len-=(offset-begin);
 	if(len>length)
 		len=length;
+	if(len<0)
+		len=0;
 	return len;
 }
 #endif
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.2.3/linux/net/ipv4/ip_forward.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_forward.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The IP forwarding functionality.
  *		
- * Version:	$Id: ip_forward.c,v 1.42 1998/10/03 09:37:19 davem Exp $
+ * Version:	$Id: ip_forward.c,v 1.43 1999/03/21 05:22:37 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -260,7 +260,7 @@
 		if (rt->rt_flags&RTCF_FAST && !netdev_fastroute_obstacles) {
 			unsigned h = ((*(u8*)&rt->key.dst)^(*(u8*)&rt->key.src))&NETDEV_FASTROUTE_HMASK;
 			/* Time to switch to functional programming :-) */
-			dst_release(xchg(&skb->dev->fastpath[h], dst_clone(&rt->u.dst)));
+			dst_release_irqwait(xchg(&skb->dev->fastpath[h], dst_clone(&rt->u.dst)));
 		}
 #endif
 		ip_send(skb);
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.2.3/linux/net/ipv4/ip_fragment.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/ip_fragment.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.39 1998/08/26 10:35:26 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.40 1999/03/20 23:58:34 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -17,6 +17,7 @@
  *		xxxx		:	Overlapfrag bug.
  *		Ultima          :       ip_expire() kernel panic.
  *		Bill Hawes	:	Frag accounting and evictor fixes.
+ *		John McDonald	:	0 length frag bug.
  */
 
 #include <linux/types.h>
@@ -357,7 +358,7 @@
 	fp = qp->fragments;
 	count = qp->ihlen;
 	while(fp) {
-		if ((fp->len < 0) || ((count + fp->len) > skb->len))
+		if ((fp->len <= 0) || ((count + fp->len) > skb->len))
 			goto out_invalid;
 		memcpy((ptr + fp->offset), fp->ptr, fp->len);
 		if (count == qp->ihlen) {
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- v2.2.3/linux/net/ipv4/ip_gre.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_gre.c	Sun Mar 21 07:22:00 1999
@@ -189,6 +189,48 @@
 	return NULL;
 }
 
+static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+{
+	u32 remote = t->parms.iph.daddr;
+	u32 local = t->parms.iph.saddr;
+	u32 key = t->parms.i_key;
+	unsigned h = HASH(key);
+	int prio = 0;
+
+	if (local)
+		prio |= 1;
+	if (remote && !MULTICAST(remote)) {
+		prio |= 2;
+		h ^= HASH(remote);
+	}
+
+	return &tunnels[prio][h];
+}
+
+static void ipgre_tunnel_link(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp = ipgre_bucket(t);
+
+	net_serialize_enter();
+	t->next = *tp;
+	*tp = t;
+	net_serialize_leave();
+}
+
+static void ipgre_tunnel_unlink(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp;
+
+	for (tp = ipgre_bucket(t); *tp; tp = &(*tp)->next) {
+		if (t == *tp) {
+			net_serialize_enter();
+			*tp = t->next;
+			net_serialize_leave();
+			break;
+		}
+	}
+}
+
 static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int create)
 {
 	u32 remote = parms->iph.daddr;
@@ -241,10 +283,7 @@
 	if (register_netdevice(dev) < 0)
 		goto failed;
 
-	start_bh_atomic();
-	nt->next = t;
-	*tp = nt;
-	end_bh_atomic();
+	ipgre_tunnel_link(nt);
 	/* Do not decrement MOD_USE_COUNT here. */
 	return nt;
 
@@ -256,28 +295,11 @@
 
 static void ipgre_tunnel_destroy(struct device *dev)
 {
-	struct ip_tunnel *t, **tp;
-	struct ip_tunnel *t0 = (struct ip_tunnel*)dev->priv;
-	u32 remote = t0->parms.iph.daddr;
-	u32 local = t0->parms.iph.saddr;
-	unsigned h = HASH(t0->parms.i_key);
-	int prio = 0;
+	ipgre_tunnel_unlink((struct ip_tunnel*)dev->priv);
 
-	if (local)
-		prio |= 1;
-	if (remote && !MULTICAST(remote)) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
-		if (t == t0) {
-			*tp = t->next;
-			if (dev != &ipgre_fb_tunnel_dev) {
-				kfree(dev);
-				MOD_DEC_USE_COUNT;
-			}
-			break;
-		}
+	if (dev != &ipgre_fb_tunnel_dev) {
+		kfree(dev);
+		MOD_DEC_USE_COUNT;
 	}
 }
 
@@ -848,6 +870,41 @@
 			p.o_key = 0;
 
 		t = ipgre_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+
+		if (dev != &ipgre_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
+		    t != &ipgre_fb_tunnel) {
+			if (t != NULL) {
+				if (t->dev != dev) {
+					err = -EEXIST;
+					break;
+				}
+			} else {
+				unsigned nflags=0;
+
+				t = (struct ip_tunnel*)dev->priv;
+
+				if (MULTICAST(p.iph.daddr))
+					nflags = IFF_BROADCAST;
+				else if (p.iph.daddr)
+					nflags = IFF_POINTOPOINT;
+
+				if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) {
+					err = -EINVAL;
+					break;
+				}
+				start_bh_atomic();
+				ipgre_tunnel_unlink(t);
+				t->parms.iph.saddr = p.iph.saddr;
+				t->parms.iph.daddr = p.iph.daddr;
+				t->parms.i_key = p.i_key;
+				t->parms.o_key = p.o_key;
+				memcpy(dev->dev_addr, &p.iph.saddr, 4);
+				memcpy(dev->broadcast, &p.iph.daddr, 4);
+				ipgre_tunnel_link(t);
+				end_bh_atomic();
+				netdev_state_change(dev);
+			}
+		}
 
 		if (t) {
 			err = 0;
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.2.3/linux/net/ipv4/ip_input.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv4/ip_input.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.35 1999/01/12 14:32:48 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.36 1999/03/21 05:22:38 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -387,6 +387,10 @@
 int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct iphdr *iph = skb->nh.iph;
+#ifdef	CONFIG_FIREWALL
+	int fwres;
+	u16 rport;
+#endif /* CONFIG_FIREWALL */
 
 	/*
 	 * 	When the interface is in promisc. mode, drop all the crap
@@ -427,6 +431,30 @@
 	__skb_trim(skb, len);
 	}
 	
+#ifdef CONFIG_IP_ALWAYS_DEFRAG
+	/* Won't send ICMP reply, since skb->dst == NULL. --RR */
+	if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+		skb = ip_defrag(skb);
+		if (!skb)
+			return 0;
+		iph = skb->nh.iph;
+		ip_send_check(iph);
+	}
+#endif
+
+#ifdef CONFIG_FIREWALL
+	/*
+	 *	See if the firewall wants to dispose of the packet. 
+	 *
+	 * We can't do ICMP reply or local delivery before routing,
+	 * so we delay those decisions until after route. --RR
+	 */
+	fwres = call_in_firewall(PF_INET, dev, iph, &rport, &skb);
+	if (fwres < FW_ACCEPT && fwres != FW_REJECT)
+		goto drop;
+	iph = skb->nh.iph;
+#endif /* CONFIG_FIREWALL */
+
 	/*
 	 *	Initialise the virtual path cache for the packet. It describes
 	 *	how the packet travels inside Linux networking.
@@ -442,13 +470,13 @@
 #endif
 	}
 
-#ifdef CONFIG_IP_ALWAYS_DEFRAG
-	if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
-		skb = ip_defrag(skb);
-		if (!skb)
-			return 0;
-		iph = skb->nh.iph;
-		ip_send_check(iph);
+#ifdef CONFIG_NET_CLS_ROUTE
+	if (skb->dst->tclassid) {
+		u32 idx = skb->dst->tclassid;
+		ip_rt_acct[idx&0xFF].o_packets++;
+		ip_rt_acct[idx&0xFF].o_bytes+=skb->len;
+		ip_rt_acct[(idx>>16)&0xFF].i_packets++;
+		ip_rt_acct[(idx>>16)&0xFF].i_bytes+=skb->len;
 	}
 #endif
 
@@ -462,7 +490,7 @@
 		   and running sniffer is extremely rare condition.
 		                                      --ANK (980813)
 		*/
-		   
+
 		skb = skb_cow(skb, skb_headroom(skb));
 		if (skb == NULL)
 			return 0;
@@ -486,51 +514,17 @@
 		}
 	}
 
-	/*
-	 *	See if the firewall wants to dispose of the packet. 
-	 *
-	 *	Note: the current standard firewall code expects that the 
-	 *	destination address was already checked against the interface 
-	 *	address lists.
-	 *
-	 *	If this code is ever moved in front of ip_route_input() you need
-	 *	to fix the fw code [moving it might be a good idea anyways,
-	 *	so that we can firewall against potentially bugs in the options
-	 *	or routing code]
-	 */
-	
-#ifdef	CONFIG_FIREWALL
-        {
-		int fwres;
-		u16 rport;
-#ifdef  CONFIG_IP_ROUTE_TOS
-		u8  tos = iph->tos;
-#endif
-
-		if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport, &skb))<FW_ACCEPT) {
-			if (fwres==FW_REJECT)
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
-			goto drop;
-		}
-
+#ifdef CONFIG_FIREWALL
 #ifdef	CONFIG_IP_TRANSPARENT_PROXY
-		if (fwres==FW_REDIRECT && (IPCB(skb)->redirport = rport) != 0)
-			return ip_local_deliver(skb);
-#endif
-#ifdef	CONFIG_IP_ROUTE_TOS
-		/* It is for 2.2 only. Firewalling should make smart
-		   rerouting itself, ideally, but now it is too late
-		   to teach it. 			--ANK (980905)
-		 */
-		if (iph->tos != tos && ((struct rtable*)skb->dst)->rt_type == RTN_UNICAST) {
-			dst_release(skb->dst);
-			skb->dst = NULL;
-			if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
-				goto drop; 
-		}
-#endif
+	if (fwres == FW_REDIRECT && (IPCB(skb)->redirport = rport) != 0)
+		return ip_local_deliver(skb);
+#endif /* CONFIG_IP_TRANSPARENT_PROXY */
+
+	if (fwres == FW_REJECT) {
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+		goto drop;
 	}
-#endif
+#endif /* CONFIG_FIREWALL */
 
 	return skb->dst->input(skb);
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.2.3/linux/net/ipv4/ip_masq.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv4/ip_masq.c	Tue Mar 16 21:52:05 1999
@@ -4,7 +4,7 @@
  *
  * 	Copyright (c) 1994 Pauline Middelink
  *
- *	$Id: ip_masq.c,v 1.33 1999/01/15 06:45:17 davem Exp $
+ *	$Id: ip_masq.c,v 1.34 1999/03/17 01:53:51 davem Exp $
  *
  *
  *	See ip_fw.c for original log
@@ -1294,7 +1294,79 @@
 	return 0;
  }
 
+/*
+ *	Restore original addresses and ports in the original IP
+ *	datagram if the failing packet has been [de]masqueraded.
+ *	This is ugly in the extreme.  We no longer have the original
+ *	packet so we have to reconstruct it from the failing packet
+ *	plus data in the masq tables.  The resulting "original data"
+ *	should be good enough to tell the sender which session to
+ *	throttle.  Relies on far too much knowledge of masq internals,
+ *	there ought to be a better way - KAO 990303.
+ *
+ *	Moved here from icmp.c - JJC.
+ *	Already known: type == ICMP_DEST_UNREACH, IPSKB_MASQUERADED
+ *	skb->nh.iph points to original header.
+ *
+ *	Must try both OUT and IN tables; we could add a flag
+ *	ala IPSKB_MASQUERADED to avoid 2nd tables lookup, but this is VERY
+ *	unlike because routing makes mtu decision before reaching 
+ *	ip_fw_masquerade().
+ *	
+ */
+int ip_fw_unmasq_icmp(struct sk_buff *skb) {
+	struct ip_masq *ms;
+	struct iphdr *iph = skb->nh.iph;
+	__u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+
+	/* 
+	 *	Always called from _bh context: use read_[un]lock()
+	 */
+
+	/*
+	 * 	Peek "out" table, this packet has bounced:
+	 *	out->in(frag_needed!)->OUT[icmp]
+	 *
+	 *	iph->daddr is IN host
+	 *	iph->saddr is OUT host
+	 */
+	read_lock(&__ip_masq_lock);
+	ms = __ip_masq_out_get(iph->protocol,
+			iph->daddr, portp[1],
+			iph->saddr, portp[0]);
+	read_unlock(&__ip_masq_lock);
+	if (ms) {
+		IP_MASQ_DEBUG(1, "Incoming frag_need rewrited from %d.%d.%d.%d to %d.%d.%d.%d\n",
+			NIPQUAD(iph->daddr), NIPQUAD(ms->maddr));
+		iph->daddr = ms->maddr;
+		portp[1] = ms->mport;
+		__ip_masq_put(ms);
+		return 1;
+	}
+	/*
+	 * 	Peek "in" table
+	 *	in->out(frag_needed!)->IN[icmp]
+	 *
+	 *	iph->daddr is OUT host
+	 *	iph->saddr is MASQ host
+	 *
+	 */
+	read_lock(&__ip_masq_lock);
+	ms = __ip_masq_in_get(iph->protocol,
+			iph->daddr, portp[1],
+			iph->saddr, portp[0]);
+	read_unlock(&__ip_masq_lock);
+	if (ms) {
+		IP_MASQ_DEBUG(1, "Outgoing frag_need rewrited from %d.%d.%d.%d to %d.%d.%d.%d\n",
+			NIPQUAD(iph->saddr), NIPQUAD(ms->saddr));
+		iph->saddr = ms->saddr;
+		portp[0] = ms->sport;
+		__ip_masq_put(ms);
+		return 1;
+	}
+	return 0;
 
+}
 /*
  *	Handle ICMP messages in forward direction.
  *	Find any that might be relevant, check against existing connections,
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- v2.2.3/linux/net/ipv4/ip_nat_dumb.c	Sun Nov  8 14:03:14 1998
+++ linux/net/ipv4/ip_nat_dumb.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		Dumb Network Address Translation.
  *
- * Version:	$Id: ip_nat_dumb.c,v 1.7 1998/10/06 04:49:09 davem Exp $
+ * Version:	$Id: ip_nat_dumb.c,v 1.8 1999/03/21 05:22:40 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -89,6 +89,8 @@
 		{
 			struct icmphdr *icmph = (struct icmphdr*)((char*)iph + (iph->ihl<<2));
 			struct   iphdr *ciph;
+			u32 idaddr, isaddr;
+			int updated;
 
 			if ((icmph->type != ICMP_DEST_UNREACH) &&
 			    (icmph->type != ICMP_TIME_EXCEEDED) &&
@@ -100,8 +102,14 @@
 			if ((u8*)(ciph+1) > skb->tail)
 				goto truncated;
 
-			if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr)
+			isaddr = ciph->saddr;
+			idaddr = ciph->daddr;
+			updated = 0;
+
+			if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr) {
 				ciph->saddr = iph->daddr;
+				updated = 1;
+			}
 			if (rt->rt_flags&RTCF_SNAT) {
 				if (ciph->daddr != osaddr) {
 					struct   fib_result res;
@@ -115,16 +123,27 @@
 #ifdef CONFIG_IP_ROUTE_TOS
 					key.tos = RT_TOS(ciph->tos);
 #endif
+#ifdef CONFIG_IP_ROUTE_FWMARK
+					key.fwmark = 0;
+#endif
 					/* Use fib_lookup() until we get our own
 					 * hash table of NATed hosts -- Rani
 				 	 */
-					if (fib_lookup(&key, &res) != 0)
-						return 0;
-					if (res.r)
+					if (fib_lookup(&key, &res) == 0 && res.r) {
 						ciph->daddr = fib_rules_policy(ciph->daddr, &res, &flags);
-				}
-				else
+						if (ciph->daddr != idaddr)
+							updated = 1;
+					}
+				} else {
 					ciph->daddr = iph->saddr;
+					updated = 1;
+				}
+			}
+			if (updated) {
+				cksum  = &icmph->checksum;
+				/* Using tcpudp primitive. Why not? */
+				check  = csum_tcpudp_magic(ciph->saddr, ciph->daddr, 0, 0, ~(*cksum));
+				*cksum = csum_tcpudp_magic(~isaddr, ~idaddr, 0, 0, ~check);
 			}
 			break;
 		}
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v2.2.3/linux/net/ipv4/ip_options.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_options.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The options processing module for ip.c
  *
- * Version:	$Id: ip_options.c,v 1.15 1998/10/03 09:37:27 davem Exp $
+ * Version:	$Id: ip_options.c,v 1.16 1999/03/21 05:22:40 davem Exp $
  *
  * Authors:	A.N.Kuznetsov
  *		
@@ -137,17 +137,17 @@
 			if (sopt->ts_needtime) {
 				if (soffset + 3 > optlen)
 					return -EINVAL;
-				dopt->ts_needtime = 1;
-				soffset += 4;
-				if ((dptr[3]&0xF) == IPOPT_TS_PRESPEC) {
-					__u32 addr;
-					if (soffset + 3 > optlen)
-						return -EINVAL;
+				if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
+					dopt->ts_needtime = 1;
 					soffset += 4;
+				} else {
+					dopt->ts_needtime = 0;
+
 					if (soffset + 8 <= optlen) {
-						dopt->ts_needtime = 0;
+						__u32 addr;
+
 						memcpy(&addr, sptr+soffset-1, 4);
-						if (inet_addr_type(addr) != RTN_UNICAST) {
+						if (inet_addr_type(addr) != RTN_LOCAL) {
 							dopt->ts_needtime = 1;
 							soffset += 8;
 						}
@@ -471,19 +471,21 @@
 	}
 	if (opt->rr_needaddr) {
 		unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
-		memset(&optptr[optptr[2]-1], 0, 4);
 		optptr[2] -= 4;
+		memset(&optptr[optptr[2]-1], 0, 4);
 	}
 	if (opt->ts) {
 		unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
 		if (opt->ts_needtime) {
-			memset(&optptr[optptr[2]-1], 0, 4);
 			optptr[2] -= 4;
-		}
-		if (opt->ts_needaddr)
 			memset(&optptr[optptr[2]-1], 0, 4);
-		if ((optptr[3]&0xF) != IPOPT_TS_PRESPEC)
+			if ((optptr[3]&0xF) == IPOPT_TS_PRESPEC)
+				optptr[2] -= 4;
+		}
+		if (opt->ts_needaddr) {
 			optptr[2] -= 4;
+			memset(&optptr[optptr[2]-1], 0, 4);
+		}
 	}
 }
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.2.3/linux/net/ipv4/ip_output.c	Tue Feb 23 15:21:35 1999
+++ linux/net/ipv4/ip_output.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.65 1999/01/21 13:37:34 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.66 1999/03/21 05:22:41 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -36,8 +36,7 @@
  *					for decreased register pressure on x86 
  *					and more readibility. 
  *		Marc Boucher	:	When call_out_firewall returns FW_QUEUE,
- *					silently abort send instead of failing
- *					with -EPERM.
+ *					silently drop skb instead of failing with -EPERM.
  */
 
 #include <asm/uaccess.h>
@@ -132,8 +131,16 @@
 	dev = rt->u.dst.dev;
 
 #ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_INET, dev, iph, NULL, &skb) < FW_ACCEPT)
-		goto drop;
+	/* Now we have no better mechanism to notify about error. */
+	switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
+	case FW_REJECT:
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+		/* Fall thru... */
+	case FW_BLOCK:
+	case FW_QUEUE:
+		kfree_skb(skb);
+		return;
+	}
 #endif
 
 	ip_send_check(iph);
@@ -141,11 +148,6 @@
 	/* Send it out. */
 	skb->dst->output(skb);
 	return;
-
-#ifdef CONFIG_FIREWALL
-drop:
-	kfree_skb(skb);
-#endif
 }
 
 int __ip_finish_output(struct sk_buff *skb)
@@ -292,8 +294,17 @@
 	dev = rt->u.dst.dev;
 
 #ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_INET, dev, iph, NULL, &skb) < FW_ACCEPT) 
-		goto drop;
+	/* Now we have no better mechanism to notify about error. */
+	switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
+	case FW_REJECT:
+		start_bh_atomic();
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+		end_bh_atomic();
+		/* Fall thru... */
+	case FW_BLOCK:
+	case FW_QUEUE:
+ 		goto drop;
+	}
 #endif
 
 	/* This can happen when the transport layer has segments queued
@@ -340,8 +351,12 @@
 		 */
 		iph->frag_off |= __constant_htons(IP_DF);
 		printk(KERN_DEBUG "sending pkt_too_big to self\n");
+
+		/* icmp_send is not reenterable, so that bh_atomic... --ANK */
+		start_bh_atomic();
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(rt->u.dst.pmtu));
+		end_bh_atomic();
 		goto drop;
 	}
 	ip_fragment(skb, skb->dst->output);
@@ -402,14 +417,13 @@
 	if (ip_dont_fragment(sk, &rt->u.dst))
 		df = htons(IP_DF);
   
-	if (!sk->ip_hdrincl)
-		length -= sizeof(struct iphdr);
+	length -= sizeof(struct iphdr);
 
 	if (opt) {
 		fragheaderlen = sizeof(struct iphdr) + opt->optlen;
 		maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
 	} else {
-		fragheaderlen = sk->ip_hdrincl ? 0 : sizeof(struct iphdr);
+		fragheaderlen = sizeof(struct iphdr);
 		
 		/*
 		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
@@ -474,7 +488,6 @@
 	 */
 	 
 	do {
-		int error;
 		char *data;
 		struct sk_buff * skb;
 
@@ -482,15 +495,10 @@
 		 *	Get the memory we require with some space left for alignment.
 		 */
 
-		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &error);
-		if (skb == NULL) {
-			ip_statistics.IpOutDiscards++;
-			if(nfrags>1)
-				ip_statistics.IpFragCreates++;			
-			dev_unlock_list();
-			return(error);
-		}
-		
+		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);
+		if (skb == NULL)
+			goto error;
+
 		/*
 		 *	Fill in the control structures
 		 */
@@ -510,7 +518,7 @@
 		 *	Only write IP header onto non-raw packets 
 		 */
 		 
-		if(!sk->ip_hdrincl) {
+		{
 			struct iphdr *iph = (struct iphdr *)data;
 
 			iph->version = 4;
@@ -547,53 +555,46 @@
 		 *	User data callback
 		 */
 
-		err = 0;
-		if (getfrag(frag, data, offset, fraglen-fragheaderlen))
+		if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {
 			err = -EFAULT;
-		
-		/*
-		 *	Account for the fragment.
-		 */
-
-#ifdef CONFIG_FIREWALL
-		if(!err) {
-			int fw_res;
-
-			fw_res = call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb);
-			if(fw_res == FW_QUEUE) {
-				kfree_skb(skb);
-				skb = NULL;
-			} else if(fw_res < FW_ACCEPT) {
-				err = -EPERM;
-			}
-		}
-#endif
-
-		if (err) { 
-			ip_statistics.IpOutDiscards++;
 			kfree_skb(skb);
-			dev_unlock_list();
-			return err; 
+			goto error;
 		}
-			
 
 		offset -= (maxfraglen-fragheaderlen);
 		fraglen = maxfraglen;
 
 		nfrags++;
 
-		err = 0; 
-		if (skb && rt->u.dst.output(skb)) {
-			err = -ENETDOWN;
-			ip_statistics.IpOutDiscards++;	
-			break;
+#ifdef CONFIG_FIREWALL
+		switch (call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb)) {
+		case FW_QUEUE:
+			kfree_skb(skb);
+			continue;
+		case FW_BLOCK:
+		case FW_REJECT:
+			kfree_skb(skb);
+			err = -EPERM;
+			goto error;
 		}
+#endif
+
+		err = -ENETDOWN;
+		if (rt->u.dst.output(skb))
+			goto error;
 	} while (offset >= 0);
 
 	if (nfrags>1)
 		ip_statistics.IpFragCreates += nfrags;
 	dev_unlock_list();
-	return err;
+	return 0;
+
+error:
+	ip_statistics.IpOutDiscards++;
+	if (nfrags>1)
+		ip_statistics.IpFragCreates += nfrags;
+	dev_unlock_list();
+	return err; 
 }
 
 
@@ -621,14 +622,20 @@
 	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
 	 */
 
-	if (!sk->ip_hdrincl)
+	if (!sk->ip_hdrincl) {
 		length += sizeof(struct iphdr);
 
-	/*
-	 * 	Check for slow path.
-	 */
-	if (length > rt->u.dst.pmtu || ipc->opt != NULL)  
-		return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags); 
+		/*
+		 * 	Check for slow path.
+		 */
+		if (length > rt->u.dst.pmtu || ipc->opt != NULL)  
+			return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags); 
+	} else {
+		if (length > rt->u.dst.dev->mtu) {
+			ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
+			return -EMSGSIZE;
+		}
+	}
 
 	/*
 	 *	Do path mtu discovery if needed.
@@ -636,7 +643,7 @@
 	df = 0;
 	if (ip_dont_fragment(sk, &rt->u.dst))
 		df = htons(IP_DF);
-	 	
+
 	/* 
 	 *	Fast path for unfragmented frames without options. 
 	 */ 
@@ -679,31 +686,27 @@
 
 	dev_unlock_list();
 
-	if (err) 
-		err = -EFAULT;
+	if (err)
+		goto error_fault;
 
 #ifdef CONFIG_FIREWALL
-	if(!err) {
-		int fw_res;
-
-		fw_res = call_out_firewall(PF_INET, rt->u.dst.dev, iph, NULL, &skb);
-		if(fw_res == FW_QUEUE) {
-			/* re-queued elsewhere; silently abort this send */
-			kfree_skb(skb);
-			return 0;
-		}
-		if(fw_res < FW_ACCEPT)
-			err = -EPERM;
-	}
-#endif
-
-	if (err) { 
+	switch (call_out_firewall(PF_INET, rt->u.dst.dev, iph, NULL, &skb)) {
+	case FW_QUEUE:
 		kfree_skb(skb);
+		return 0;
+	case FW_BLOCK:
+	case FW_REJECT:
+		kfree_skb(skb);
+		err = -EPERM;
 		goto error;
 	}
-	
+#endif
+
 	return rt->u.dst.output(skb);
 
+error_fault:
+	err = -EFAULT;
+	kfree_skb(skb);
 error:
 	ip_statistics.IpOutDiscards++;
 	return err; 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.2.3/linux/net/ipv4/ip_sockglue.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_sockglue.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The IP to API glue.
  *		
- * Version:	$Id: ip_sockglue.c,v 1.39 1998/10/03 09:37:33 davem Exp $
+ * Version:	$Id: ip_sockglue.c,v 1.40 1999/03/21 05:22:42 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -209,7 +209,9 @@
 					kfree(new_ra);
 				return -EADDRINUSE;
 			}
+			net_serialize_enter();
 			*rap = ra->next;
+			net_serialize_leave();
 			if (ra->destructor)
 				ra->destructor(sk);
 			kfree(ra);
@@ -220,10 +222,10 @@
 		return -ENOBUFS;
 	new_ra->sk = sk;
 	new_ra->destructor = destructor;
-	start_bh_atomic();
 	new_ra->next = ra;
+	net_serialize_enter();
 	*rap = new_ra;
-	end_bh_atomic();
+	net_serialize_leave();
 	return 0;
 }
 
@@ -404,7 +406,7 @@
 			err = ip_options_get(&opt, optval, optlen, 1);
 			if (err)
 				return err;
-			start_bh_atomic();
+			lock_sock(sk);
 			if (sk->type == SOCK_STREAM) {
 				struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -420,7 +422,7 @@
 #endif
 			}
 			opt = xchg(&sk->opt, opt);
-			end_bh_atomic();
+			release_sock(sk);
 			if (opt)
 				kfree_s(opt, sizeof(struct ip_options) + opt->optlen);
 			return 0;
@@ -463,11 +465,12 @@
 			    !capable(CAP_NET_ADMIN))
 				return -EPERM;
 			if (sk->ip_tos != val) {
+				lock_sock(sk);
 				sk->ip_tos=val;
 				sk->priority = rt_tos2priority(val);
 				dst_release(xchg(&sk->dst_cache, NULL)); 
+				release_sock(sk);
 			}
-			sk->priority = rt_tos2priority(val);
 			return 0;
 		case IP_TTL:
 			if (optlen<1)
@@ -637,11 +640,11 @@
 			{
 				unsigned char optbuf[sizeof(struct ip_options)+40];
 				struct ip_options * opt = (struct ip_options*)optbuf;
-				start_bh_atomic();
+				lock_sock(sk);
 				opt->optlen = 0;
 				if (sk->opt)
 					memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
-				end_bh_atomic();
+				release_sock(sk);
 				if (opt->optlen == 0) 
 					return put_user(0, optlen);
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.2.3/linux/net/ipv4/ipip.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ipip.c	Sun Mar 21 07:22:00 1999
@@ -1,7 +1,7 @@
 /*
  *	Linux NET3:	IP/IP protocol decoder. 
  *
- *	Version: $Id: ipip.c,v 1.24 1998/10/03 09:37:35 davem Exp $
+ *	Version: $Id: ipip.c,v 1.25 1999/03/21 05:22:43 davem Exp $
  *
  *	Authors:
  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
@@ -157,6 +157,49 @@
 	return NULL;
 }
 
+static struct ip_tunnel **ipip_bucket(struct ip_tunnel *t)
+{
+	u32 remote = t->parms.iph.daddr;
+	u32 local = t->parms.iph.saddr;
+	unsigned h = 0;
+	int prio = 0;
+
+	if (remote) {
+		prio |= 2;
+		h ^= HASH(remote);
+	}
+	if (local) {
+		prio |= 1;
+		h ^= HASH(local);
+	}
+	return &tunnels[prio][h];
+}
+
+
+static void ipip_tunnel_unlink(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp;
+
+	for (tp = ipip_bucket(t); *tp; tp = &(*tp)->next) {
+		if (t == *tp) {
+			net_serialize_enter();
+			*tp = t->next;
+			net_serialize_leave();
+			break;
+		}
+	}
+}
+
+static void ipip_tunnel_link(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp = ipip_bucket(t);
+
+	net_serialize_enter();
+	t->next = *tp;
+	*tp = t;
+	net_serialize_leave();
+}
+
 struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int create)
 {
 	u32 remote = parms->iph.daddr;
@@ -208,10 +251,7 @@
 	if (register_netdevice(dev) < 0)
 		goto failed;
 
-	start_bh_atomic();
-	nt->next = t;
-	*tp = nt;
-	end_bh_atomic();
+	ipip_tunnel_link(nt);
 	/* Do not decrement MOD_USE_COUNT here. */
 	return nt;
 
@@ -221,39 +261,20 @@
 	return NULL;
 }
 
+
 static void ipip_tunnel_destroy(struct device *dev)
 {
-	struct ip_tunnel *t, **tp;
-	struct ip_tunnel *t0 = (struct ip_tunnel*)dev->priv;
-	u32 remote = t0->parms.iph.daddr;
-	u32 local = t0->parms.iph.saddr;
-	unsigned h = 0;
-	int prio = 0;
-
 	if (dev == &ipip_fb_tunnel_dev) {
+		net_serialize_enter();
 		tunnels_wc[0] = NULL;
-		return;
-	}
-
-	if (remote) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	if (local) {
-		prio |= 1;
-		h ^= HASH(local);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
-		if (t == t0) {
-			*tp = t->next;
-			kfree(dev);
-			MOD_DEC_USE_COUNT;
-			break;
-		}
+		net_serialize_leave();
+	} else {
+		ipip_tunnel_unlink((struct ip_tunnel*)dev->priv);
+		kfree(dev);
+		MOD_DEC_USE_COUNT;
 	}
 }
 
-
 void ipip_err(struct sk_buff *skb, unsigned char *dp, int len)
 {
 #ifndef I_WISH_WORLD_WERE_PERFECT
@@ -641,6 +662,32 @@
 			p.iph.frag_off |= __constant_htons(IP_DF);
 
 		t = ipip_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+
+		if (dev != &ipip_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
+		    t != &ipip_fb_tunnel) {
+			if (t != NULL) {
+				if (t->dev != dev) {
+					err = -EEXIST;
+					break;
+				}
+			} else {
+				if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
+				    (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
+					err = -EINVAL;
+					break;
+				}
+				t = (struct ip_tunnel*)dev->priv;
+				start_bh_atomic();
+				ipip_tunnel_unlink(t);
+				t->parms.iph.saddr = p.iph.saddr;
+				t->parms.iph.daddr = p.iph.daddr;
+				memcpy(dev->dev_addr, &p.iph.saddr, 4);
+				memcpy(dev->broadcast, &p.iph.daddr, 4);
+				ipip_tunnel_link(t);
+				end_bh_atomic();
+				netdev_state_change(dev);
+			}
+		}
 
 		if (t) {
 			err = 0;
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.2.3/linux/net/ipv4/ipmr.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv4/ipmr.c	Sun Mar 21 07:22:00 1999
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.38 1999/01/12 14:34:40 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.39 1999/03/21 05:22:44 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -138,6 +138,8 @@
 
 static int reg_vif_xmit(struct sk_buff *skb, struct device *dev)
 {
+	((struct net_device_stats*)dev->priv)->tx_bytes += skb->len;
+	((struct net_device_stats*)dev->priv)->tx_packets++;
 	ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT);
 	kfree_skb(skb);
 	return 0;
@@ -449,6 +451,9 @@
 	struct igmpmsg *msg;
 	int ret;
 
+	if (mroute_socket==NULL)
+		return -EINVAL;
+
 #ifdef CONFIG_IP_PIMSM
 	if (assert == IGMPMSG_WHOLEPKT)
 		skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
@@ -656,7 +661,9 @@
 {
 	if (sk == mroute_socket) {
 		ipv4_devconf.mc_forwarding = 0;
+		net_serialize_enter();
 		mroute_socket=NULL;
+		net_serialize_leave();
 		mroute_close(sk);
 	}
 }
@@ -1045,7 +1052,7 @@
 
 	dev = rt->u.dst.dev;
 
-	if (skb->len+encap > rt->u.dst.pmtu /* && (ntohs(iph->frag_off) & IP_DF) */) {
+	if (skb->len+encap > rt->u.dst.pmtu && (ntohs(iph->frag_off) & IP_DF)) {
 		/* Do not fragment multicasts. Alas, IPv4 does not
 		   allow to send ICMP, so that packets will disappear
 		   to blackhole.
@@ -1119,7 +1126,10 @@
 	 * not mrouter) cannot join to more than one interface - it will
 	 * result in receiving multiple packets.
 	 */
-	skb2->dst->output(skb2);
+	if (skb2->len <= rt->u.dst.pmtu)
+		skb2->dst->output(skb2);
+	else
+		ip_fragment(skb2, skb2->dst->output);
 }
 
 int ipmr_find_vif(struct device *dev)
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.2.3/linux/net/ipv4/route.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv4/route.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.61 1999/01/12 14:34:43 davem Exp $
+ * Version:	$Id: route.c,v 1.63 1999/03/21 05:22:45 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -49,6 +49,9 @@
  *		Andi Kleen	:	Load-limit warning messages.
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *	Vitaly E. Lavrov	:	Race condition in ip_route_input_slow.
+ *	Tobias Ringstrom	:	Uninitialized res.type in ip_route_output_slow.
+ *	Vladimir V. Ivanov	:	IP rule info (flowid) is really useful.
+ *		Marc Boucher	:	routing by fwmark
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -108,6 +111,7 @@
 int ip_rt_error_cost = HZ;
 int ip_rt_error_burst = 5*HZ;
 int ip_rt_gc_elasticity = 8;
+int ip_rt_mtu_expires = 10*60*HZ;
 
 static unsigned long rt_deadline = 0;
 
@@ -165,13 +169,14 @@
 	TC_PRIO_FILLER
 };
 
+
 /*
  * Route cache.
  */
 
 struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
 
-static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth);
+static int rt_intern_hash(unsigned hash, struct rtable * rth, struct rtable ** res);
 
 static __inline__ unsigned rt_hash_code(u32 daddr, u32 saddr, u8 tos)
 {
@@ -249,6 +254,12 @@
 	dst_free(&rt->u.dst);
 }
 
+static __inline__ void rt_drop(struct rtable *rt)
+{
+	ip_rt_put(rt);
+	dst_free(&rt->u.dst);
+}
+
 static __inline__ int rt_fast_clean(struct rtable *rth)
 {
 	/* Kill broadcast/multicast entries very aggresively, if they
@@ -257,6 +268,27 @@
 		&& rth->key.iif && rth->u.rt_next);
 }
 
+static __inline__ int rt_valuable(struct rtable *rth)
+{
+	return ((rth->rt_flags&(RTCF_REDIRECTED|RTCF_NOTIFY))
+		|| rth->u.dst.expires);
+}
+
+static __inline__ int rt_may_expire(struct rtable *rth, int tmo1, int tmo2)
+{
+	int age;
+
+	if (atomic_read(&rth->u.dst.use))
+		return 0;
+
+	age = jiffies - rth->u.dst.lastuse;
+	if (age <= tmo1 && !rt_fast_clean(rth))
+		return 0;
+	if (age <= tmo2 && rt_valuable(rth))
+		return 0;
+	return 1;
+}
+
 static void rt_check_expire(unsigned long dummy)
 {
 	int i;
@@ -271,22 +303,27 @@
 		rthp = &rt_hash_table[rover];
 
 		while ((rth = *rthp) != NULL) {
-			/*
-			 * Cleanup aged off entries.
-			 */
-
-			if (!atomic_read(&rth->u.dst.use) &&
-			    (now - rth->u.dst.lastuse > tmo
-			     || rt_fast_clean(rth))) {
-				*rthp = rth->u.rt_next;
-				rt_free(rth);
+			if (rth->u.dst.expires) {
+				/* Entrie is expired even if it is in use */
+				if ((long)(now - rth->u.dst.expires) < tmo) {
+					tmo >>= 1;
+					rthp = &rth->u.rt_next;
+					continue;
+				}
+			} else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) {
+				tmo >>= 1;
+				rthp = &rth->u.rt_next;
 				continue;
 			}
 
-			tmo >>= 1;
-			rthp = &rth->u.rt_next;
+			/*
+			 * Cleanup aged off entries.
+			 */
+			*rthp = rth->u.rt_next;
+			rt_free(rth);
 		}
 
+		/* Fallback loop breaker. */
 		if ((jiffies - now) > 0)
 			break;
 	}
@@ -301,16 +338,21 @@
 
 	rt_deadline = 0;
 
+	net_serialize_enter();
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
 		if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
 			continue;
+		net_serialize_leave();
 
 		for (; rth; rth=next) {
 			next = rth->u.rt_next;
 			rth->u.rt_next = NULL;
 			rt_free(rth);
 		}
+
+		net_serialize_enter();
 	}
+	net_serialize_leave();
 }
   
 void rt_cache_flush(int delay)
@@ -354,60 +396,137 @@
 	end_bh_atomic();
 }
 
+/*
+   Short description of GC goals.
+
+   We want to build algorithm, which will keep routing cache
+   at some equilibrium point, when number of aged off entries
+   is kept approximately equal to newly generated ones.
+
+   Current expiration strength is variable "expire".
+   We try to adjust it dynamically, so that if networking
+   is idle expires is large enough to keep enough of warm entries,
+   and when load increases it reduces to limit cache size.
+ */
+
 static int rt_garbage_collect(void)
 {
-	int i;
-	static unsigned expire = RT_GC_TIMEOUT>>1;
+	static unsigned expire = RT_GC_TIMEOUT;
 	static unsigned long last_gc;
+	static int rover;
+	static int equilibrium;
 	struct rtable *rth, **rthp;
 	unsigned long now = jiffies;
-
-	start_bh_atomic();
+	int goal;
 
 	/*
 	 * Garbage collection is pretty expensive,
-	 * do not make it too frequently, but just increase expire strength.
+	 * do not make it too frequently.
 	 */
-	if (now - last_gc < ip_rt_gc_min_interval)
-		goto out;
+	if (now - last_gc < ip_rt_gc_min_interval &&
+	    atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
+		return 0;
 
-	expire++;
+	/* Calculate number of entries, which we want to expire now. */
+	goal = atomic_read(&ipv4_dst_ops.entries) - RT_HASH_DIVISOR*ip_rt_gc_elasticity;
+	if (goal <= 0) {
+		if (equilibrium < ipv4_dst_ops.gc_thresh)
+			equilibrium = ipv4_dst_ops.gc_thresh;
+		goal = atomic_read(&ipv4_dst_ops.entries) - equilibrium;
+		if (goal > 0) {
+			equilibrium += min(goal/2, RT_HASH_DIVISOR);
+			goal = atomic_read(&ipv4_dst_ops.entries) - equilibrium;
+		}
+	} else {
+		/* We are in dangerous area. Try to reduce cache really
+		 * aggressively.
+		 */
+		goal = max(goal/2, RT_HASH_DIVISOR);
+		equilibrium = atomic_read(&ipv4_dst_ops.entries) - goal;
+	}
 
-	for (i=0; i<RT_HASH_DIVISOR; i++) {
-		unsigned tmo;
-		if (!rt_hash_table[i])
-			continue;
-		tmo = expire;
-		for (rthp=&rt_hash_table[i]; (rth=*rthp); rthp=&rth->u.rt_next)	{
-			if (atomic_read(&rth->u.dst.use) ||
-			    (now - rth->u.dst.lastuse < tmo && !rt_fast_clean(rth))) {
-				tmo >>= 1;
-				continue;
+	if (now - last_gc >= ip_rt_gc_min_interval)
+		last_gc = now;
+
+	if (goal <= 0) {
+		equilibrium += goal;
+		goto work_done;
+	}
+
+	do {
+		int i, k;
+
+		start_bh_atomic();
+		for (i=0, k=rover; i<RT_HASH_DIVISOR; i++) {
+			unsigned tmo = expire;
+
+			k = (k + 1) & (RT_HASH_DIVISOR-1);
+			rthp = &rt_hash_table[k];
+			while ((rth = *rthp) != NULL) {
+				if (!rt_may_expire(rth, tmo, expire)) {
+					tmo >>= 1;
+					rthp = &rth->u.rt_next;
+					continue;
+				}
+				*rthp = rth->u.rt_next;
+				rth->u.rt_next = NULL;
+				rt_free(rth);
+				goal--;
 			}
-			*rthp = rth->u.rt_next;
-			rth->u.rt_next = NULL;
-			rt_free(rth);
-			break;
+			if (goal <= 0)
+				break;
 		}
-		if ((jiffies-now)>0)
+		rover = k;
+		end_bh_atomic();
+
+		if (goal <= 0)
+			goto work_done;
+
+		/* Goal is not achieved. We stop process if:
+
+		   - if expire reduced to zero. Otherwise, expire is halfed.
+		   - if table is not full.
+		   - if we are called from interrupt.
+		   - jiffies check is just fallback/debug loop breaker.
+		     We will not spin here for long time in any case.
+		 */
+
+		if (expire == 0)
 			break;
-	}
 
-	last_gc = now;
-	if (atomic_read(&ipv4_dst_ops.entries) < ipv4_dst_ops.gc_thresh)
-		expire = ip_rt_gc_timeout>>1;
+		expire >>= 1;
+#if RT_CACHE_DEBUG >= 2
+		printk(KERN_DEBUG "expire>> %u %d %d %d\n", expire, atomic_read(&ipv4_dst_ops.entries), goal, i);
+#endif
 
-out:
-	expire -= expire>>ip_rt_gc_elasticity;
-	end_bh_atomic();
-	return (atomic_read(&ipv4_dst_ops.entries) > ip_rt_max_size);
+		if (atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
+			return 0;
+	} while (!in_interrupt() && jiffies - now < 1);
+
+	if (atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
+		return 0;
+	if (net_ratelimit())
+		printk("dst cache overflow\n");
+	return 1;
+
+work_done:
+	expire += ip_rt_gc_min_interval;
+	if (expire > ip_rt_gc_timeout ||
+	    atomic_read(&ipv4_dst_ops.entries) < ipv4_dst_ops.gc_thresh)
+		expire = ip_rt_gc_timeout;
+#if RT_CACHE_DEBUG >= 2
+	printk(KERN_DEBUG "expire++ %u %d %d %d\n", expire, atomic_read(&ipv4_dst_ops.entries), goal, rover);
+#endif
+	return 0;
 }
 
-static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt)
+static int rt_intern_hash(unsigned hash, struct rtable * rt, struct rtable ** rp)
 {
 	struct rtable	*rth, **rthp;
 	unsigned long	now = jiffies;
+	int attempts = !in_interrupt();
 
+restart:
 	start_bh_atomic();
 
 	rthp = &rt_hash_table[hash];
@@ -424,9 +543,9 @@
 			rth->u.dst.lastuse = now;
 			end_bh_atomic();
 
-			ip_rt_put(rt);
-			rt_free(rt);
-			return rth;
+			rt_drop(rt);
+			*rp = rth;
+			return 0;
 		}
 
 		rthp = &rth->u.rt_next;
@@ -435,8 +554,28 @@
 	/* Try to bind route to arp only if it is output
 	   route or unicast forwarding path.
 	 */
-	if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0)
-		arp_bind_neighbour(&rt->u.dst);
+	if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0) {
+		if (!arp_bind_neighbour(&rt->u.dst)) {
+			end_bh_atomic();
+
+			/* Neighbour tables are full and nothing
+			   can be released. Try to shrink route cache,
+			   it is most likely it holds some neighbour records.
+			 */
+			if (attempts-- > 0) {
+				int saved_elasticity = ip_rt_gc_elasticity;
+				ip_rt_gc_elasticity = 1;
+				rt_garbage_collect();
+				ip_rt_gc_elasticity = saved_elasticity;
+				goto restart;
+			}
+
+			rt_drop(rt);
+			if (net_ratelimit())
+				printk("neighbour table overflow\n");
+			return -ENOBUFS;
+		}
+	}
 
 	rt->u.rt_next = rt_hash_table[hash];
 #if RT_CACHE_DEBUG >= 2
@@ -449,9 +588,9 @@
 	}
 #endif
 	rt_hash_table[hash] = rt;
-
 	end_bh_atomic();
-	return rt;
+	*rp = rt;
+	return 0;
 }
 
 void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw,
@@ -537,17 +676,15 @@
 				    !(rt->u.dst.neighbour->nud_state&NUD_VALID)) {
 					if (rt->u.dst.neighbour)
 						neigh_event_send(rt->u.dst.neighbour, NULL);
-					ip_rt_put(rt);
 					ip_rt_put(rth);
-					rt_free(rt);
+					rt_drop(rt);
 					break;
 				}
 
 				*rthp = rth->u.rt_next;
-				rt = rt_intern_hash(hash, rt);
-				ip_rt_put(rt);
-				ip_rt_put(rth);
-				rt_free(rth);
+				if (!rt_intern_hash(hash, rt, &rt))
+					ip_rt_put(rt);
+				rt_drop(rth);
 				break;
 			}
 		}
@@ -573,14 +710,14 @@
 			ip_rt_put(rt);
 			return NULL;
 		}
-		if (rt->rt_flags&RTCF_REDIRECTED) {
+		if ((rt->rt_flags&RTCF_REDIRECTED) || rt->u.dst.expires) {
 			unsigned hash = rt_hash_code(rt->key.dst, rt->key.src^(rt->key.oif<<5), rt->key.tos);
 			struct rtable **rthp;
 #if RT_CACHE_DEBUG >= 1
 			printk(KERN_DEBUG "ip_rt_advice: redirect to %d.%d.%d.%d/%02x dropped\n", NIPQUAD(rt->rt_dst), rt->key.tos);
 #endif
-			ip_rt_put(rt);
 			start_bh_atomic();
+			ip_rt_put(rt);
 			for (rthp = &rt_hash_table[hash]; *rthp; rthp = &(*rthp)->u.rt_next) {
 				if (*rthp == rt) {
 					*rthp = rt->u.rt_next;
@@ -614,6 +751,10 @@
 void ip_rt_send_redirect(struct sk_buff *skb)
 {
 	struct rtable *rt = (struct rtable*)skb->dst;
+	struct in_device *in_dev = (struct in_device*)rt->u.dst.dev->ip_ptr;
+
+	if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev))
+		return;
 
 	/* No redirected packets during ip_rt_redirect_silence;
 	 * reset the algorithm.
@@ -637,7 +778,7 @@
 		rt->u.dst.rate_last = jiffies;
 		++rt->u.dst.rate_tokens;
 #ifdef CONFIG_IP_ROUTE_VERBOSE
-		if (skb->dev->ip_ptr && IN_DEV_LOG_MARTIANS((struct in_device*)skb->dev->ip_ptr) &&
+		if (IN_DEV_LOG_MARTIANS(in_dev) &&
 		    rt->u.dst.rate_tokens == ip_rt_redirect_number && net_ratelimit())
 			printk(KERN_WARNING "host %08x/if%d ignores redirects for %08x to %08x.\n",
 			       rt->rt_src, rt->rt_iif, rt->rt_dst, rt->rt_gateway);
@@ -737,6 +878,7 @@
 					if (mtu < rth->u.dst.pmtu) { 
 						dst_confirm(&rth->u.dst);
 						rth->u.dst.pmtu = mtu;
+						dst_set_expires(&rth->u.dst, ip_rt_mtu_expires);
 					}
 					est_mtu = mtu;
 				}
@@ -760,7 +902,13 @@
 
 static void ipv4_link_failure(struct sk_buff *skb)
 {
+	struct rtable *rt;
+
 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+
+	rt = (struct rtable *) skb->dst;
+	if (rt)
+		dst_set_expires(&rt->u.dst, 0);
 }
 
 static int ip_rt_bug(struct sk_buff *skb)
@@ -794,7 +942,17 @@
 	memcpy(addr, &src, 4);
 }
 
-static void rt_set_nexthop(struct rtable *rt, struct fib_result *res)
+#ifdef CONFIG_NET_CLS_ROUTE
+static void set_class_tag(struct rtable *rt, u32 tag)
+{
+	if (!(rt->u.dst.tclassid&0xFFFF))
+		rt->u.dst.tclassid |= tag&0xFFFF;
+	if (!(rt->u.dst.tclassid&0xFFFF0000))
+		rt->u.dst.tclassid |= tag&0xFFFF0000;
+}
+#endif
+
+static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag)
 {
 	struct fib_info *fi = res->fi;
 
@@ -824,9 +982,11 @@
 		rt->u.dst.window= 0;
 		rt->u.dst.rtt	= TCP_TIMEOUT_INIT;
 	}
-#if defined(CONFIG_NET_CLS_ROUTE) && defined(CONFIG_IP_MULTIPLE_TABLES)
-	if (rt->u.dst.tclassid == 0)
-		rt->u.dst.tclassid = fib_rules_tclass(res);
+#ifdef CONFIG_NET_CLS_ROUTE
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+	set_class_tag(rt, fib_rules_tclass(res));
+#endif
+	set_class_tag(rt, itag);
 #endif
         rt->rt_type = res->type;
 }
@@ -839,6 +999,7 @@
 	struct rtable *rth;
 	u32 spec_dst;
 	struct in_device *in_dev = dev->ip_ptr;
+	u32 itag = 0;
 
 	/* Primary sanity checks. */
 
@@ -850,7 +1011,7 @@
 		if (!LOCAL_MCAST(daddr))
 			return -EINVAL;
 		spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
-	} else if (fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst) < 0)
+	} else if (fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst, &itag) < 0)
 		return -EINVAL;
 
 	rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
@@ -863,12 +1024,18 @@
 	rth->key.dst	= daddr;
 	rth->rt_dst	= daddr;
 	rth->key.tos	= tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	rth->key.fwmark	= skb->fwmark;
+#endif
 	rth->key.src	= saddr;
 	rth->rt_src	= saddr;
 #ifdef CONFIG_IP_ROUTE_NAT
 	rth->rt_dst_map	= daddr;
 	rth->rt_src_map	= saddr;
 #endif
+#ifdef CONFIG_NET_CLS_ROUTE
+	rth->u.dst.tclassid = itag;
+#endif
 	rth->rt_iif	=
 	rth->key.iif	= dev->ifindex;
 	rth->u.dst.dev	= &loopback_dev;
@@ -888,8 +1055,7 @@
 #endif
 
 	hash = rt_hash_code(daddr, saddr^(dev->ifindex<<5), tos);
-	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth);
-	return 0;
+	return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
 }
 
 /*
@@ -910,6 +1076,7 @@
 	struct in_device *in_dev = dev->ip_ptr;
 	struct in_device *out_dev;
 	unsigned	flags = 0;
+	u32		itag = 0;
 	struct rtable * rth;
 	unsigned	hash;
 	u32		spec_dst;
@@ -925,6 +1092,9 @@
 	key.dst = daddr;
 	key.src = saddr;
 	key.tos = tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	key.fwmark = skb->fwmark;
+#endif
 	key.iif = dev->ifindex;
 	key.oif = 0;
 	key.scope = RT_SCOPE_UNIVERSE;
@@ -983,9 +1153,14 @@
 		goto brd_input;
 
 	if (res.type == RTN_LOCAL) {
-		spec_dst = daddr;
-		if (inet_addr_type(saddr) != RTN_UNICAST)
+		int result;
+		result = fib_validate_source(saddr, daddr, tos, loopback_dev.ifindex,
+					     dev, &spec_dst, &itag);
+		if (result < 0)
 			goto martian_source;
+		if (result)
+			flags |= RTCF_DIRECTSRC;
+		spec_dst = daddr;
 		goto local_input;
 	}
 
@@ -1005,7 +1180,7 @@
 		return -EINVAL;
 	}
 
-	err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(res), dev, &spec_dst);
+	err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(res), dev, &spec_dst, &itag);
 	if (err < 0)
 		goto martian_source;
 
@@ -1033,6 +1208,9 @@
 	rth->key.dst	= daddr;
 	rth->rt_dst	= daddr;
 	rth->key.tos	= tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	rth->key.fwmark	= skb->fwmark;
+#endif
 	rth->key.src	= saddr;
 	rth->rt_src	= saddr;
 	rth->rt_gateway	= daddr;
@@ -1051,7 +1229,7 @@
 	rth->u.dst.input = ip_forward;
 	rth->u.dst.output = ip_output;
 
-	rt_set_nexthop(rth, &res);
+	rt_set_nexthop(rth, &res, itag);
 
 	rth->rt_flags = flags;
 
@@ -1066,8 +1244,7 @@
 	}
 #endif
 
-	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth);
-	return 0;
+	return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
 
 brd_input:
 	if (skb->protocol != __constant_htons(ETH_P_IP))
@@ -1076,7 +1253,7 @@
 	if (ZERONET(saddr)) {
 		spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
 	} else {
-		err = fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst);
+		err = fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst, &itag);
 		if (err < 0)
 			goto martian_source;
 		if (err)
@@ -1096,12 +1273,18 @@
 	rth->key.dst	= daddr;
 	rth->rt_dst	= daddr;
 	rth->key.tos	= tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+	rth->key.fwmark	= skb->fwmark;
+#endif
 	rth->key.src	= saddr;
 	rth->rt_src	= saddr;
 #ifdef CONFIG_IP_ROUTE_NAT
 	rth->rt_dst_map	= key.dst;
 	rth->rt_src_map	= key.src;
 #endif
+#ifdef CONFIG_NET_CLS_ROUTE
+	rth->u.dst.tclassid = itag;
+#endif
 	rth->rt_iif	=
 	rth->key.iif	= dev->ifindex;
 	rth->u.dst.dev	= &loopback_dev;
@@ -1116,8 +1299,7 @@
 		rth->rt_flags 	&= ~RTCF_LOCAL;
 	}
 	rth->rt_type	= res.type;
-	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth);
-	return 0;
+	return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
 
 no_route:
 	spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
@@ -1170,6 +1352,9 @@
 		    rth->key.src == saddr &&
 		    rth->key.iif == iif &&
 		    rth->key.oif == 0 &&
+#ifdef CONFIG_IP_ROUTE_FWMARK
+		    rth->key.fwmark == skb->fwmark &&
+#endif
 		    rth->key.tos == tos) {
 			rth->u.dst.lastuse = jiffies;
 			atomic_inc(&rth->u.dst.use);
@@ -1344,43 +1529,33 @@
 	if (res.type == RTN_NAT)
 		return -EINVAL;
 
-
-	if (!key.src) {
-		key.src = FIB_RES_PREFSRC(res);
-
-#ifdef CONFIG_IP_MULTIPLE_TABLES
-		/*
-		 * "Stabilization" of route.
-		 * This step is necessary, if locally originated packets
-		 * are subjected to policy routing, otherwise we could get
-		 * route flapping.
-		 */
-		if (fib_lookup(&key, &res))
-			return -ENETUNREACH;
-#endif
+	if (res.type == RTN_LOCAL) {
+		if (!key.src)
+			key.src = key.dst;
+		dev_out = &loopback_dev;
+		key.oif = dev_out->ifindex;
+		res.fi = NULL;
+		flags |= RTCF_LOCAL;
+		goto make_route;
 	}
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	if (res.fi->fib_nhs > 1 && key.oif == 0)
 		fib_select_multipath(&key, &res);
+	else
 #endif
+	if (res.prefixlen==0 && res.type == RTN_UNICAST && key.oif == 0)
+		fib_select_default(&key, &res);
 
-	dev_out = FIB_RES_DEV(res);
-
-	if (res.type == RTN_LOCAL) {
-		dev_out = &loopback_dev;
-		key.oif = dev_out->ifindex;
-		res.fi = NULL;
-		flags |= RTCF_LOCAL;
-	}
+	if (!key.src)
+		key.src = FIB_RES_PREFSRC(res);
 
+	dev_out = FIB_RES_DEV(res);
 	key.oif = dev_out->ifindex;
 
 make_route:
-	if (LOOPBACK(key.src) && !(dev_out->flags&IFF_LOOPBACK)) {
-		printk(KERN_DEBUG "this guy talks to %08x from loopback\n", key.dst);
+	if (LOOPBACK(key.src) && !(dev_out->flags&IFF_LOOPBACK))
 		return -EINVAL;
-	}
 
 	if (key.dst == 0xFFFFFFFF)
 		res.type = RTN_BROADCAST;
@@ -1449,13 +1624,12 @@
 #endif
 	}
 
-	rt_set_nexthop(rth, &res);
+	rt_set_nexthop(rth, &res, 0);
 
 	rth->rt_flags = flags;
 
 	hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
-	*rp = rt_intern_hash(hash, rth);
-	return 0;
+	return rt_intern_hash(hash, rth, rp);
 }
 
 int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif)
@@ -1507,7 +1681,7 @@
 
 	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
 	r = NLMSG_DATA(nlh);
-	nlh->nlmsg_flags = nowait ? NLM_F_MULTI : 0;
+	nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
 	r->rtm_family = AF_INET;
 	r->rtm_dst_len = 32;
 	r->rtm_src_len = 0;
@@ -1517,6 +1691,8 @@
 	r->rtm_scope = RT_SCOPE_UNIVERSE;
 	r->rtm_protocol = RTPROT_UNSPEC;
 	r->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED;
+	if (rt->rt_flags & RTCF_NOTIFY)
+		r->rtm_flags |= RTM_F_NOTIFY;
 	RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst);
 	if (rt->key.src) {
 		r->rtm_src_len = 32;
@@ -1524,6 +1700,10 @@
 	}
 	if (rt->u.dst.dev)
 		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
+#ifdef CONFIG_NET_CLS_ROUTE
+	if (rt->u.dst.tclassid)
+		RTA_PUT(skb, RTA_FLOW, 4, &rt->u.dst.tclassid);
+#endif
 	if (rt->key.iif)
 		RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst);
 	else if (rt->rt_src != rt->key.src)
@@ -1546,7 +1726,10 @@
 	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
 	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
 	ci.rta_clntref = atomic_read(&rt->u.dst.use);
-	ci.rta_expires = 0;
+	if (rt->u.dst.expires)
+		ci.rta_expires = rt->u.dst.expires - jiffies;
+	else
+		ci.rta_expires = 0;
 	ci.rta_error = rt->u.dst.error;
 #ifdef CONFIG_IP_MROUTE
 	eptr = (struct rtattr*)skb->tail;
@@ -1625,7 +1808,7 @@
 		end_bh_atomic();
 		rt = (struct rtable*)skb->dst;
 		if (!err && rt->u.dst.error)
-			err = rt->u.dst.error;
+			err = -rt->u.dst.error;
 	} else {
 		int oif = 0;
 		if (rta[RTA_OIF-1])
@@ -1667,7 +1850,7 @@
 	for (h=0; h < RT_HASH_DIVISOR; h++) {
 		if (h < s_h) continue;
 		if (h > s_h)
-			memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(int));
+			memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0]));
 		start_bh_atomic();
 		for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
 			if (idx < s_idx)
@@ -1717,7 +1900,7 @@
 
 ctl_table ipv4_route_table[] = {
         {NET_IPV4_ROUTE_FLUSH, "flush",
-         &flush_delay, sizeof(int), 0644, NULL,
+         &flush_delay, sizeof(int), 0200, NULL,
          &ipv4_sysctl_rtcache_flush},
 	{NET_IPV4_ROUTE_MIN_DELAY, "min_delay",
          &ip_rt_min_delay, sizeof(int), 0644, NULL,
@@ -1758,12 +1941,45 @@
 	{NET_IPV4_ROUTE_GC_ELASTICITY, "gc_elasticity",
          &ip_rt_gc_elasticity, sizeof(int), 0644, NULL,
          &proc_dointvec},
+	{NET_IPV4_ROUTE_MTU_EXPIRES, "mtu_expires",
+         &ip_rt_mtu_expires, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
 	 {0}
 };
 #endif
 
+#ifdef CONFIG_NET_CLS_ROUTE
+struct ip_rt_acct ip_rt_acct[256];
+
+#ifdef CONFIG_PROC_FS
+static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
+			   int length, int *eof, void *data)
+{
+	*start=buffer;
+
+	if (offset + length > sizeof(ip_rt_acct)) {
+		length = sizeof(ip_rt_acct) - offset;
+		*eof = 1;
+	}
+	if (length > 0) {
+		start_bh_atomic();
+		memcpy(buffer, ((u8*)&ip_rt_acct)+offset, length);
+		end_bh_atomic();
+		return length;
+	}
+	return 0;
+}
+#endif
+#endif
+
+
 __initfunc(void ip_rt_init(void))
 {
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_NET_CLS_ROUTE
+	struct proc_dir_entry *ent;
+#endif
+#endif
 	devinet_init();
 	ip_fib_init();
 	rt_periodic_timer.function = rt_check_expire;
@@ -1781,5 +1997,9 @@
 		0, &proc_net_inode_operations,
 		rt_cache_get_info
 	});
+#ifdef CONFIG_NET_CLS_ROUTE
+	ent = create_proc_entry("net/rt_acct", 0, 0);
+	ent->read_proc = ip_rt_acct_read;
+#endif
 #endif
 }
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/syncookies.c linux/net/ipv4/syncookies.c
--- v2.2.3/linux/net/ipv4/syncookies.c	Tue Jun 23 10:01:31 1998
+++ linux/net/ipv4/syncookies.c	Tue Mar 16 21:52:05 1999
@@ -9,7 +9,7 @@
  *      as published by the Free Software Foundation; either version
  *      2 of the License, or (at your option) any later version.
  * 
- *  $Id: syncookies.c,v 1.6 1998/06/10 07:29:22 davem Exp $
+ *  $Id: syncookies.c,v 1.7 1999/03/17 02:34:57 davem Exp $
  *
  *  Missing: IPv6 support. 
  */
@@ -147,6 +147,8 @@
 	req->af.v4_req.rmt_addr = skb->nh.iph->saddr;
 	req->class = &or_ipv4; /* for savety */
 
+	req->af.v4_req.opt = NULL;
+
 	/* We throwed the options of the initial SYN away, so we hope
 	 * the ACK carries the same options again (see RFC1122 4.2.3.8)
 	 */
@@ -162,7 +164,6 @@
 		}
 	}
 	
-	req->af.v4_req.opt = NULL;
 	req->snd_wscale = req->rcv_wscale = req->tstamp_ok = 0;
 	req->wscale_ok = 0; 
 	req->expires = 0UL; 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.2.3/linux/net/ipv4/tcp.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/tcp.c	Wed Mar 17 12:39:13 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.136 1999/03/07 13:26:01 davem Exp $
+ * Version:	$Id: tcp.c,v 1.139 1999/03/17 19:30:34 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -735,18 +735,25 @@
  *	Note: must be called with the socket locked.
  */
 
-int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
+int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg)
 {
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	int mss_now;
-	int err = 0;
-	int copied  = 0;
+	struct iovec *iov;
+	struct tcp_opt *tp;
 	struct sk_buff *skb;
+	int iovlen, flags;
+	int mss_now;
+	int err, copied;
+
+	lock_sock(sk);
+
+	err = 0;
+	tp = &(sk->tp_pinfo.af_tcp);
 
 	/* Wait for a connection to finish. */
+	flags = msg->msg_flags;
 	if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
 		if((err = wait_for_tcp_connect(sk, flags)) != 0)
-			return err;
+			goto out;
 
 	/* This should be in poll */
 	sk->socket->flags &= ~SO_NOSPACE; /* clear SIGIO XXX */
@@ -754,6 +761,10 @@
 	mss_now = tcp_current_mss(sk);
 
 	/* Ok commence sending. */
+	iovlen = msg->msg_iovlen;
+	iov = msg->msg_iov;
+	copied = 0;
+	
 	while(--iovlen >= 0) {
 		int seglen=iov->iov_len;
 		unsigned char * from=iov->iov_base;
@@ -844,7 +855,7 @@
 			 * for later rather than sent.
 			 */
 			copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
-			if(copy >= (tp->max_window >> 1))
+			if(copy > (tp->max_window >> 1))
 				copy = min(copy, mss_now);
 			else
 				copy = mss_now;
@@ -926,26 +937,36 @@
 		}
 	}
 	sk->err = 0;
-	return copied;
+	err = copied;
+	goto out;
 
 do_sock_err:
 	if(copied)
-		return copied;
-	return sock_error(sk);
+		err = copied;
+	else
+		err = sock_error(sk);
+	goto out;
 do_shutdown:
 	if(copied)
-		return copied;
-	if (!(flags&MSG_NOSIGNAL))
-		send_sig(SIGPIPE, current, 0);
-	return -EPIPE;
+		err = copied;
+	else {
+		if (!(flags&MSG_NOSIGNAL))
+			send_sig(SIGPIPE, current, 0);
+		err = -EPIPE;
+	}
+	goto out;
 do_interrupted:
 	if(copied)
-		return copied;
-	return err;
+		err = copied;
+	goto out;
 do_fault:
 	kfree_skb(skb);
 do_fault2:
-	return -EFAULT;
+	err = -EFAULT;
+out:
+	tcp_push_pending_frames(sk, tp);
+	release_sock(sk);
+	return err;
 }
 
 #undef PSH_NEEDED
@@ -1681,13 +1702,9 @@
 		} else {
 			sk->nonagle = 0;
 
-			if (tp->send_head) {
-				lock_sock(sk);
-				if (tp->send_head &&
-				    tcp_snd_test (sk, tp->send_head))
-					tcp_write_xmit(sk);
-				release_sock(sk);
-			}
+			lock_sock(sk);
+			tcp_push_pending_frames(sk, tp);
+			release_sock(sk);
 		}
 		return 0;
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.2.3/linux/net/ipv4/tcp_input.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/tcp_input.c	Wed Mar 17 12:39:13 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.157 1999/03/05 09:35:06 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.159 1999/03/17 19:30:39 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -345,6 +345,7 @@
 {
 	unsigned char *ptr;
 	int length=(th->doff*4)-sizeof(struct tcphdr);
+	int saw_mss = 0;
 
 	ptr = (unsigned char *)(th + 1);
 	tp->saw_tstamp = 0;
@@ -373,6 +374,7 @@
 							in_mss = 536;
 						if (tp->mss_clamp > in_mss)
 							tp->mss_clamp = in_mss;
+						saw_mss = 1;
 					}
 					break;
 				case TCPOPT_WINDOW:
@@ -426,6 +428,8 @@
 	  			length-=opsize;
 	  	};
 	}
+	if(th->syn && saw_mss == 0)
+		tp->mss_clamp = 536;
 }
 
 /* Fast parse options. This hopes to only see timestamps.
@@ -662,7 +666,7 @@
 	tp->probes_out = 0;
 	
 	/* Was it a usable window open? */
-	
+
 	/* should always be non-null */
 	if (tp->send_head != NULL &&
 	    !before (ack + tp->snd_wnd, TCP_SKB_CB(tp->send_head)->end_seq)) {
@@ -949,7 +953,9 @@
 		tcp_tw_deschedule(tw);
 		tcp_timewait_kill(tw);
 		sk = af_specific->get_sock(skb, th);
-		if(sk == NULL || !ipsec_sk_policy(sk,skb))
+		if(sk == NULL ||
+		   !ipsec_sk_policy(sk,skb) ||
+		   atomic_read(&sk->sock_readers) != 0)
 			return 0;
 		skb_set_owner_r(skb, sk);
 		af_specific = sk->tp_pinfo.af_tcp.af_specific;
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.2.3/linux/net/ipv4/tcp_ipv4.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/tcp_ipv4.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.166 1999/02/23 08:12:41 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.170 1999/03/21 05:22:47 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -657,7 +657,6 @@
 
 static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
-	struct tcp_opt *tp;
 	int retval = -EINVAL;
 
 	/* Do sanity checking for sendmsg/sendto/send. */
@@ -679,15 +678,7 @@
 		if (addr->sin_addr.s_addr != sk->daddr)
 			goto out;
 	}
-
-	lock_sock(sk);
-	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
-				msg->msg_flags);
-	/* Push out partial tail frames if needed. */
-	tp = &(sk->tp_pinfo.af_tcp);
-	if(tp->send_head && tcp_snd_test(sk, tp->send_head))
-		tcp_write_xmit(sk);
-	release_sock(sk);
+	retval = tcp_do_sendmsg(sk, msg);
 
 out:
 	return retval;
@@ -735,6 +726,9 @@
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
+	if (atomic_read(&sk->sock_readers))
+		return;
+
 	/* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs
 	 * send out by Linux are always <576bytes so they should go through
 	 * unfragmented).
@@ -748,19 +742,18 @@
      	 * There is a small race when the user changes this flag in the
 	 * route, but I think that's acceptable.
 	 */
-	if (sk->ip_pmtudisc != IP_PMTUDISC_DONT && sk->dst_cache) {
-		if (tp->pmtu_cookie > sk->dst_cache->pmtu &&
-		    !atomic_read(&sk->sock_readers)) {
-			tcp_sync_mss(sk, sk->dst_cache->pmtu);
-
-			/* Resend the TCP packet because it's  
-			 * clear that the old packet has been
-			 * dropped. This is the new "fast" path mtu
-			 * discovery.
-			 */
-			tcp_simple_retransmit(sk);
-		} /* else let the usual retransmit timer handle it */
-	}
+	if (sk->dst_cache &&
+	    sk->ip_pmtudisc != IP_PMTUDISC_DONT &&
+	    tp->pmtu_cookie > sk->dst_cache->pmtu) {
+		tcp_sync_mss(sk, sk->dst_cache->pmtu);
+
+		/* Resend the TCP packet because it's  
+		 * clear that the old packet has been
+		 * dropped. This is the new "fast" path mtu
+		 * discovery.
+		 */
+		tcp_simple_retransmit(sk);
+	} /* else let the usual retransmit timer handle it */
 }
 
 /*
@@ -787,6 +780,11 @@
 	struct tcp_opt *tp;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
+#if ICMP_MIN_LENGTH < 14
+	int no_flags = 0;
+#else
+#define no_flags 0
+#endif
 	struct sock *sk;
 	__u32 seq;
 	int err;
@@ -795,6 +793,10 @@
 		icmp_statistics.IcmpInErrors++; 
 		return;
 	}
+#if ICMP_MIN_LENGTH < 14
+	if (len < (iph->ihl << 2) + 14)
+		no_flags = 1;
+#endif
 
 	th = (struct tcphdr*)(dp+(iph->ihl<<2));
 
@@ -861,7 +863,7 @@
 		 * ACK should set the opening flag, but that is too
 		 * complicated right now. 
 		 */ 
-		if (!th->syn && !th->ack)
+		if (!no_flags && !th->syn && !th->ack)
 			return;
 
 		req = tcp_v4_search_req(tp, iph, th, &prev); 
@@ -896,7 +898,7 @@
 		break;
 	case TCP_SYN_SENT:
 	case TCP_SYN_RECV:  /* Cannot happen */ 
-		if (!th->syn)
+		if (!no_flags && !th->syn)
 			return;
 		tcp_statistics.TcpAttemptFails++;
 		sk->err = err;
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.2.3/linux/net/ipv4/tcp_output.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv4/tcp_output.c	Thu Mar 11 23:24:45 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.104 1999/03/07 13:26:04 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.106 1999/03/12 03:43:51 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -233,8 +233,9 @@
 	buff->csum = csum_partial_copy(skb->data + len, skb_put(buff, nsize),
 				       nsize, 0);
 
-	TCP_SKB_CB(skb)->end_seq -= nsize;
-	skb_trim(skb, skb->len - nsize);
+	/* This takes care of the FIN sequence number too. */
+	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq;
+	skb_trim(skb, len);
 
 	/* Rechecksum original buffer. */
 	skb->csum = csum_partial(skb->data, skb->len, 0);
@@ -993,7 +994,7 @@
 		tp->delack_timer.expires = timeout;
 		add_timer(&tp->delack_timer);
         } else {
-		if (timeout < tp->delack_timer.expires)
+		if (time_before(timeout, tp->delack_timer.expires))
 			mod_timer(&tp->delack_timer, timeout);
 	}
 }
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.2.3/linux/net/ipv4/tcp_timer.c	Wed Jan 20 23:14:07 1999
+++ linux/net/ipv4/tcp_timer.c	Mon Mar 15 12:51:30 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.57 1999/01/20 07:20:21 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.58 1999/03/14 19:48:30 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -445,7 +445,6 @@
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, HZ/20);
 		return;
 	}
-	lock_sock(sk);
 
 	/* Clear delay ack timer. */
 	tcp_clear_xmit_timer(sk, TIME_DACK);
@@ -510,8 +509,6 @@
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 
 	tcp_write_timeout(sk);
-
-	release_sock(sk);
 }
 
 /*
diff -u --recursive --new-file v2.2.3/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.2.3/linux/net/ipv4/udp.c	Thu Nov 19 09:56:29 1998
+++ linux/net/ipv4/udp.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.64 1998/11/08 11:17:07 davem Exp $
+ * Version:	$Id: udp.c,v 1.65 1999/03/21 05:22:49 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -570,7 +570,6 @@
 	struct udphdr uh;
 	u32 saddr;
 	u32 daddr;
-	u32 other;
 	struct iovec *iov;
 	u32 wcheck;
 };
@@ -778,7 +777,6 @@
 		ufh.daddr = ipc.addr = rt->rt_dst;
 	ufh.uh.len = htons(ulen);
 	ufh.uh.check = 0;
-	ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256;
 	ufh.iov = msg->msg_iov;
 	ufh.wcheck = 0;
 
@@ -846,7 +844,7 @@
 	return(0);
 }
 
-#if defined(CONFIG_FILTER) || !defined(HAVE_CSUM_COPY_USER) 
+#ifndef HAVE_CSUM_COPY_USER
 #undef CONFIG_UDP_DELAY_CSUM
 #endif
 
@@ -890,11 +888,11 @@
 	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					copied);
 #else
-	if (sk->no_check || skb->ip_summed==CHECKSUM_UNNECESSARY) {
+	if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					      copied);
 	} else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
-		if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) 
+		if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) 
 			goto csum_copy_err;
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					      copied);
@@ -907,7 +905,7 @@
 					     copied, csum, &err);
 		if (err)
 			goto out_free;
-		if (csum_fold(csum)) 
+		if ((unsigned short)csum_fold(csum)) 
 			goto csum_copy_err;
 	}
 #endif
@@ -1030,6 +1028,19 @@
 	 *	Charge it to the socket, dropping if the queue is full.
 	 */
 
+#if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM)
+	if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
+		if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
+			udp_statistics.UdpInErrors++;
+			ip_statistics.IpInDiscards++;
+			ip_statistics.IpInDelivers--;
+			kfree_skb(skb);
+			return -1;
+		}
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+#endif
+
 	if (sock_queue_rcv_skb(sk,skb)<0) {
 		udp_statistics.UdpInErrors++;
 		ip_statistics.IpInDiscards++;
@@ -1179,7 +1190,7 @@
 	if (sk == NULL) {
 #ifdef CONFIG_UDP_DELAY_CSUM
 		if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
-		    csum_fold(csum_partial((char*)uh, ulen, skb->csum))) 
+		    (unsigned short)csum_fold(csum_partial((char*)uh, ulen, skb->csum))) 
 			goto csum_error;
 #endif
   		udp_statistics.UdpNoPorts++;
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.2.3/linux/net/ipv6/addrconf.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv6/addrconf.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.46 1999/01/12 14:34:47 davem Exp $
+ *	$Id: addrconf.c,v 1.47 1999/03/21 05:22:50 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -88,6 +88,34 @@
 	0, 0, addrconf_verify
 };
 
+/* These locks protect only against address deletions,
+   but not against address adds or status updates.
+   It is OK. The only race is when address is selected,
+   which becomes invalid immediately after selection.
+   It is harmless, because this address could be already invalid
+   several usecs ago.
+
+   Its important, that:
+
+   1. The result of inet6_add_addr() is used only inside lock
+      or from bh_atomic context.
+
+   2. inet6_get_lladdr() is used only from bh protected context.
+
+   3. The result of ipv6_chk_addr() is not used outside of bh protected context.
+ */
+
+static __inline__ void addrconf_lock(void)
+{
+	atomic_inc(&addr_list_lock);
+	synchronize_bh();
+}
+
+static __inline__ void addrconf_unlock(void)
+{
+	atomic_dec(&addr_list_lock);
+}
+
 static int addrconf_ifdown(struct device *dev, int how);
 
 static void addrconf_dad_start(struct inet6_ifaddr *ifp);
@@ -188,7 +216,7 @@
 	if (dev->mtu < IPV6_MIN_MTU)
 		return NULL;
 
-	ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
+	ndev = kmalloc(sizeof(struct inet6_dev), GFP_KERNEL);
 
 	if (ndev) {
 		memset(ndev, 0, sizeof(struct inet6_dev));
@@ -227,9 +255,9 @@
 		idev = ipv6_add_dev(dev);
 		if (idev == NULL)
 			return NULL;
+		if (dev->flags&IFF_UP)
+			ipv6_mc_up(idev);
 	}
-	if (dev->flags&IFF_UP)
-		ipv6_mc_up(idev);
 	return idev;
 }
 
@@ -260,13 +288,13 @@
 	return NULL;
 }
 
-struct inet6_ifaddr * ipv6_add_addr(struct inet6_dev *idev, 
-				    struct in6_addr *addr, int scope)
+static struct inet6_ifaddr *
+ipv6_add_addr(struct inet6_dev *idev, struct in6_addr *addr, int scope)
 {
 	struct inet6_ifaddr *ifa;
 	int hash;
 
-	ifa = kmalloc(sizeof(struct inet6_ifaddr), gfp_any());
+	ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
 
 	if (ifa == NULL) {
 		ADBG(("ipv6_add_addr: malloc failed\n"));
@@ -312,7 +340,9 @@
 
 	for (; iter; iter = iter->lst_next) {
 		if (iter == ifp) {
+			net_serialize_enter();
 			*back = ifp->lst_next;
+			net_serialize_leave();
 			ifp->lst_next = NULL;
 			break;
 		}
@@ -324,7 +354,9 @@
 
 	for (; iter; iter = iter->if_next) {
 		if (iter == ifp) {
+			net_serialize_enter();
 			*back = ifp->if_next;
+			net_serialize_leave();
 			ifp->if_next = NULL;
 			break;
 		}
@@ -343,24 +375,23 @@
  *	ii)	see if there is a specific route for the destination and use
  *		an address of the attached interface 
  *	iii)	don't use deprecated addresses
- *
- *	at the moment I believe only iii) is missing.
  */
-struct inet6_ifaddr * ipv6_get_saddr(struct dst_entry *dst,
-				     struct in6_addr *daddr)
+int ipv6_get_saddr(struct dst_entry *dst,
+		   struct in6_addr *daddr, struct in6_addr *saddr)
 {
 	int scope;
 	struct inet6_ifaddr *ifp = NULL;
 	struct inet6_ifaddr *match = NULL;
 	struct device *dev = NULL;
 	struct rt6_info *rt;
+	int err;
 	int i;
 
 	rt = (struct rt6_info *) dst;
 	if (rt)
 		dev = rt->rt6i_dev;
 	
-	atomic_inc(&addr_list_lock);
+	addrconf_lock();
 
 	scope = ipv6_addr_scope(daddr);
 	if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
@@ -388,10 +419,10 @@
 			if (idev->dev == dev) {
 				for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
 					if (ifp->scope == scope) {
-						if (!(ifp->flags & ADDR_STATUS))
+						if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS)))
 							goto out;
 
-						if (!(ifp->flags & ADDR_INVALID))
+						if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS)))
 							match = ifp;
 					}
 				}
@@ -410,10 +441,10 @@
 	for (i=0; i < IN6_ADDR_HSIZE; i++) {
 		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
 			if (ifp->scope == scope) {
-				if (!(ifp->flags & ADDR_STATUS))
+				if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS)))
 					goto out;
 
-				if (!(ifp->flags & ADDR_INVALID))
+				if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS)))
 					match = ifp;
 			}
 		}
@@ -422,28 +453,30 @@
 out:
 	if (ifp == NULL)
 		ifp = match;
-	atomic_dec(&addr_list_lock);
-	return ifp;
+
+	err = -ENETUNREACH;
+	if (ifp) {
+		memcpy(saddr, &ifp->addr, sizeof(struct in6_addr));
+		err = 0;
+	}
+	addrconf_unlock();
+	return err;
 }
 
 struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
 {
-	struct inet6_ifaddr *ifp;
+	struct inet6_ifaddr *ifp = NULL;
 	struct inet6_dev *idev;
-	int hash;
 
-	hash = ipv6_devindex_hash(dev->ifindex);
-
-	for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
-		if (idev->dev == dev) {
-			for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-				if (ifp->scope == IFA_LINK)
-					return ifp;
-			}
-			break;
+	if ((idev = ipv6_get_idev(dev)) != NULL) {
+		addrconf_lock();
+		for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+			if (ifp->scope == IFA_LINK)
+				break;
 		}
+		addrconf_unlock();
 	}
-	return NULL;
+	return ifp;
 }
 
 /*
@@ -461,7 +494,7 @@
 	if (!nd)
 		flags |= DAD_STATUS|ADDR_INVALID;
 
-	atomic_inc(&addr_list_lock);
+	addrconf_lock();
 
 	hash = ipv6_addr_hash(addr);
 	for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
@@ -472,7 +505,7 @@
 		}
 	}
 
-	atomic_dec(&addr_list_lock);
+	addrconf_unlock();
 	return ifp;
 }
 
@@ -665,13 +698,6 @@
 	}
 
 	/*
-	 *	If we where using an "all destinations on link" route
-	 *	delete it
-	 */
-
-	rt6_purge_dflt_routers(RTF_ALLONLINK);
-
-	/*
 	 *	Two things going on here:
 	 *	1) Add routes for on-link prefixes
 	 *	2) Configure prefixes with the auto flag set
@@ -845,14 +871,17 @@
 
 	scope = ipv6_addr_scope(pfx);
 
-	if ((ifp = ipv6_add_addr(idev, pfx, scope)) == NULL)
-		return -ENOMEM;
-
-	ifp->prefix_len = plen;
-	ifp->flags |= ADDR_PERMANENT;
+	addrconf_lock();
+	if ((ifp = ipv6_add_addr(idev, pfx, scope)) != NULL) {
+		ifp->prefix_len = plen;
+		ifp->flags |= ADDR_PERMANENT;
+		addrconf_dad_start(ifp);
+		addrconf_unlock();
+		return 0;
+	}
+	addrconf_unlock();
 
-	addrconf_dad_start(ifp);
-	return 0;
+	return -ENOBUFS;
 }
 
 static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
@@ -870,20 +899,22 @@
 
 	scope = ipv6_addr_scope(pfx);
 
+	start_bh_atomic();
 	for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
 		if (ifp->scope == scope && ifp->prefix_len == plen &&
 		    (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) {
 			ipv6_del_addr(ifp);
+			end_bh_atomic();
 
 			/* If the last address is deleted administratively,
 			   disable IPv6 on this interface.
 			 */
-			   
 			if (idev->addr_list == NULL)
 				addrconf_ifdown(idev->dev, 1);
 			return 0;
 		}
 	}
+	end_bh_atomic();
 	return -EADDRNOTAVAIL;
 }
 
@@ -940,12 +971,14 @@
 	}
 
 	if (addr.s6_addr32[3]) {
+		addrconf_lock();
 		ifp = ipv6_add_addr(idev, &addr, scope);
 		if (ifp) {
 			ifp->flags |= ADDR_PERMANENT;
 			ifp->prefix_len = 128;
 			ipv6_ifa_notify(RTM_NEWADDR, ifp);
 		}
+		addrconf_unlock();
 		return;
 	}
 
@@ -967,17 +1000,17 @@
 					flag |= IFA_HOST;
 				}
 
+				addrconf_lock();
 				ifp = ipv6_add_addr(idev, &addr, flag);
-			
-				if (ifp == NULL)
-					continue;
-
-				if (idev->dev->flags&IFF_POINTOPOINT)
-					ifp->prefix_len = 10;
-				else
-					ifp->prefix_len = 96;
-				ifp->flags |= ADDR_PERMANENT;
-				ipv6_ifa_notify(RTM_NEWADDR, ifp);
+				if (ifp) {
+					if (idev->dev->flags&IFF_POINTOPOINT)
+						ifp->prefix_len = 10;
+					else
+						ifp->prefix_len = 96;
+					ifp->flags |= ADDR_PERMANENT;
+					ipv6_ifa_notify(RTM_NEWADDR, ifp);
+				}
+				addrconf_unlock();
 			}
 		}
         }
@@ -999,31 +1032,29 @@
 		return;
 	}
 
+	addrconf_lock();
 	ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
 
-	if (ifp == NULL) {
-		printk(KERN_DEBUG "init_loopback: add_addr failed\n");
-		return;
+	if (ifp) {
+		ifp->flags |= ADDR_PERMANENT;
+		ifp->prefix_len = 128;
+		ipv6_ifa_notify(RTM_NEWADDR, ifp);
 	}
-
-	ifp->flags |= ADDR_PERMANENT;
-	ifp->prefix_len = 128;
-
-	ipv6_ifa_notify(RTM_NEWADDR, ifp);
+	addrconf_unlock();
 }
 
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
 
+	addrconf_lock();
 	ifp = ipv6_add_addr(idev, addr, IFA_LINK);
-	if (ifp == NULL)
-		return;
-
-	ifp->flags = ADDR_PERMANENT;
-	ifp->prefix_len = 10;
-
-	addrconf_dad_start(ifp);
+	if (ifp) {
+		ifp->flags = ADDR_PERMANENT;
+		ifp->prefix_len = 10;
+		addrconf_dad_start(ifp);
+	}
+	addrconf_unlock();
 }
 
 static void addrconf_dev_config(struct device *dev)
@@ -1375,8 +1406,12 @@
 	struct inet6_ifaddr *ifp;
 	int i;
 	int len = 0;
+	off_t pos=0;
+	off_t begin=0;
+
+	addrconf_lock();
 
-	for (i=0; i < IN6_ADDR_HSIZE; i++)
+	for (i=0; i < IN6_ADDR_HSIZE; i++) {
 		for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
 			int j;
 
@@ -1393,14 +1428,25 @@
 				       ifp->scope,
 				       ifp->flags,
 				       ifp->idev->dev->name);
+			pos=begin+len;
+			if(pos<offset) {
+				len=0;
+				begin=pos;
+			}
+			if(pos>offset+length)
+				goto done;
 		}
+	}
 
-	*start = buffer + offset;
-
-	len -= offset;
+done:
+	addrconf_unlock();
 
-	if (len > length)
-		len = length;
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	if(len<0)
+		len=0;
 	return len;
 }
 
@@ -1422,6 +1468,12 @@
 	struct inet6_ifaddr *ifp;
 	unsigned long now = jiffies;
 	int i;
+
+	if (atomic_read(&addr_list_lock)) {
+		addr_chk_timer.expires = jiffies + 1*HZ;
+		add_timer(&addr_chk_timer);
+		return;
+	}
 
 	for (i=0; i < IN6_ADDR_HSIZE; i++) {
 		for (ifp=inet6_addr_lst[i]; ifp;) {
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.2.3/linux/net/ipv6/icmp.c	Mon Oct  5 13:13:49 1998
+++ linux/net/ipv6/icmp.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.20 1998/10/03 09:38:31 davem Exp $
+ *	$Id: icmp.c,v 1.21 1999/03/21 05:22:51 davem Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
@@ -200,9 +200,11 @@
 	 * this lookup should be more aggressive (not longer than timeout).
 	 */
 	dst = ip6_route_output(sk, fl);
-	if (dst->error)
+	if (dst->error) {
 		ipv6_statistics.Ip6OutNoRoutes++;
-	else {
+	} else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
+		res = 1;
+	} else {
 		struct rt6_info *rt = (struct rt6_info *)dst;
 		int tmo = sysctl_icmpv6_time;
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.2.3/linux/net/ipv6/ip6_fib.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/ip6_fib.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fib.c,v 1.15 1998/08/26 12:04:55 davem Exp $
+ *	$Id: ip6_fib.c,v 1.16 1999/03/21 05:22:52 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -103,8 +103,8 @@
 static __inline__ u32 fib6_new_sernum(void)
 {
 	u32 n = ++rt_sernum;
-	if (n == 0)
-		n = ++rt_sernum;
+	if ((__s32)n <= 0)
+		rt_sernum = n = 1;
 	return n;
 }
 
@@ -1157,7 +1157,6 @@
 			return -1;
 		}
 		gc_args.more++;
-		return 0;
 	}
 
 	/*
@@ -1171,7 +1170,6 @@
 			return -1;
 		}
 		gc_args.more++;
-		return 0;
 	}
 
 	return 0;
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.2.3/linux/net/ipv6/ip6_output.c	Mon Oct  5 13:13:49 1998
+++ linux/net/ipv6/ip6_output.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.15 1998/10/03 09:38:34 davem Exp $
+ *	$Id: ip6_output.c,v 1.16 1999/03/21 05:22:54 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -77,11 +77,14 @@
 		/* Alpha has disguisting memcpy. Help it. */
 	        u64 *aligned_hdr = (u64*)(skb->data - 16);
 		u64 *aligned_hdr0 = hh->hh_data;
+		read_lock_irq(&hh->hh_lock);
 		aligned_hdr[0] = aligned_hdr0[0];
 		aligned_hdr[1] = aligned_hdr0[1];
 #else
+		read_lock_irq(&hh->hh_lock);
 		memcpy(skb->data - 16, hh->hh_data, 16);
 #endif
+		read_unlock_irq(&hh->hh_lock);
 	        skb_push(skb, dev->hard_header_len);
 		return hh->hh_output(skb);
 	} else if (dst->neighbour)
@@ -164,7 +167,9 @@
 	}
 
 	printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
+	start_bh_atomic();
 	icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+	end_bh_atomic();
 	kfree_skb(skb);
 	return -EMSGSIZE;
 }
@@ -427,6 +432,7 @@
 	struct dst_entry *dst;
 	int err = 0;
 	unsigned int pktlength, jumbolen, mtu;
+	struct in6_addr saddr;
 
 	if (opt && opt->srcrt) {
 		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
@@ -481,19 +487,16 @@
 	}
 
 	if (fl->nl_u.ip6_u.saddr == NULL) {
-		struct inet6_ifaddr *ifa;
-		
-		ifa = ipv6_get_saddr(dst, fl->nl_u.ip6_u.daddr);
+		err = ipv6_get_saddr(dst, fl->nl_u.ip6_u.daddr, &saddr);
 
-		if (ifa == NULL) {
+		if (err) {
 #if IP6_DEBUG >= 2
 			printk(KERN_DEBUG "ip6_build_xmit: "
 			       "no availiable source address\n");
 #endif
-			err = -ENETUNREACH;
 			goto out;
 		}
-		fl->nl_u.ip6_u.saddr = &ifa->addr;
+		fl->nl_u.ip6_u.saddr = &saddr;
 	}
 	pktlength = length;
 
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.2.3/linux/net/ipv6/ipv6_sockglue.c	Mon Oct  5 13:13:49 1998
+++ linux/net/ipv6/ipv6_sockglue.c	Sun Mar 21 07:22:00 1999
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.24 1998/10/03 09:38:37 davem Exp $
+ *	$Id: ipv6_sockglue.c,v 1.25 1999/03/21 05:22:54 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -86,7 +86,9 @@
 					kfree(new_ra);
 				return -EADDRINUSE;
 			}
+			net_serialize_enter();
 			*rap = ra->next;
+			net_serialize_leave();
 			if (ra->destructor)
 				ra->destructor(sk);
 			kfree(ra);
@@ -136,15 +138,16 @@
 			if (sk->protocol != IPPROTO_UDP &&
 			    sk->protocol != IPPROTO_TCP)
 				goto out;
-			
+
+			lock_sock(sk);
 			if (sk->state != TCP_ESTABLISHED) {
 				retv = ENOTCONN;
-				goto out;
+				goto addrform_done;
 			}
 
 			if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
 				retv = -EADDRNOTAVAIL;
-				goto out;
+				goto addrform_done;
 			}
 
 			if (sk->protocol == IPPROTO_TCP) {
@@ -166,6 +169,9 @@
 			if (pktopt)
 				kfree_skb(pktopt);
 			retv = 0;
+
+addrform_done:
+			release_sock(sk);
 		} else {
 			retv = -EINVAL;
 		}
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.2.3/linux/net/ipv6/mcast.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/mcast.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.17 1998/08/26 12:05:06 davem Exp $
+ *	$Id: mcast.c,v 1.18 1999/03/21 05:22:55 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -132,7 +132,9 @@
 		if (mc_lst->ifindex == ifindex &&
 		    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
 			struct device *dev;
+			net_serialize_enter();
 			*lnk = mc_lst->next;
+			net_serialize_leave();
 			if ((dev = dev_get_by_index(ifindex)) != NULL)
 				ipv6_dev_mc_dec(dev, &mc_lst->addr);
 			sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
@@ -252,7 +254,9 @@
 		
 		for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
 			if (iter == ma) {
+				net_serialize_enter();
 				*lnk = iter->if_next;
+				net_serialize_leave();
 				return;
 			}
 		}
@@ -273,7 +277,9 @@
 		if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
 			if (atomic_dec_and_test(&ma->mca_users)) {
 				igmp6_group_dropped(ma);
+				net_serialize_enter();
 				*lnk = ma->next;
+				net_serialize_leave();
 				ipv6_mca_remove(dev, ma);
 				kfree(ma);
 			}
@@ -496,10 +502,10 @@
 	if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
 		return;
 
+	start_bh_atomic();
 	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
 
 	delay = net_random() % IGMP6_UNSOLICITED_IVAL;
-	start_bh_atomic();
 	if (del_timer(&ma->mca_timer))
 		delay = ma->mca_timer.expires - jiffies;
 
@@ -519,11 +525,13 @@
 	if ((addr_type & IPV6_ADDR_LINKLOCAL))
 		return;
 
+	start_bh_atomic();
 	if (ma->mca_flags & MAF_LAST_REPORTER)
 		igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
 
 	if (ma->mca_flags & MAF_TIMER_RUNNING)
 		del_timer(&ma->mca_timer);
+	end_bh_atomic();
 }
 
 void igmp6_timer_handler(unsigned long data)
@@ -577,10 +585,22 @@
 
 void ipv6_mc_destroy_dev(struct inet6_dev *idev)
 {
-	struct ifmcaddr6 *i;
+	int hash;
+	struct ifmcaddr6 *i, **lnk;
 
 	while ((i = idev->mc_list) != NULL) {
 		idev->mc_list = i->if_next;
+
+		hash = ipv6_addr_hash(&i->mca_addr);
+
+		for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) {
+			if (*lnk == i) {
+				net_serialize_enter();
+				*lnk = i->next;
+				net_serialize_leave();
+				break;
+			}
+		}
 		igmp6_group_dropped(i);
 		kfree(i);
 	}
@@ -631,6 +651,8 @@
 	len-=(offset-begin);
 	if(len>length)
 		len=length;
+	if (len<0)
+		len=0;
 	return len;
 }
 #endif
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.2.3/linux/net/ipv6/ndisc.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv6/ndisc.c	Sun Mar 21 07:22:00 1999
@@ -335,7 +335,7 @@
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+        msg->icmph.icmp6_override  = !!override;
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -497,7 +497,7 @@
 	 *	"The sender MUST return an ICMP
 	 *	 destination unreachable"
 	 */
-	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+	dst_link_failure(skb);
 	kfree_skb(skb);
 }
 
@@ -604,6 +604,13 @@
 			return;
 		}
 		neigh->flags |= NTF_ROUTER;
+
+		/*
+		 *	If we where using an "all destinations on link" route
+		 *	delete it
+		 */
+
+		rt6_purge_dflt_routers(RTF_ALLONLINK);
 	}
 
 	if (rt)
@@ -989,7 +996,7 @@
 
 					if (neigh) {
 						ndisc_send_na(dev, neigh, saddr, &msg->target,
-							      0, 0, inc, inc);
+							      0, 1, 0, inc);
 						neigh_release(neigh);
 					}
 				} else {
@@ -1173,7 +1180,6 @@
 	sk = ndisc_socket->sk;
 	sk->allocation = GFP_ATOMIC;
 	sk->net_pinfo.af_inet6.hop_limit = 255;
-	sk->net_pinfo.af_inet6.priority  = 15;
 	/* Do not loopback ndisc messages */
 	sk->net_pinfo.af_inet6.mc_loop = 0;
 	sk->num = 256;
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.2.3/linux/net/ipv6/route.c	Mon Oct  5 13:13:49 1998
+++ linux/net/ipv6/route.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.34 1998/10/03 09:38:43 davem Exp $
+ *	$Id: route.c,v 1.35 1999/03/21 05:22:57 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -71,6 +71,7 @@
 int ip6_rt_gc_timeout = 60*HZ;
 int ip6_rt_gc_interval = 30*HZ;
 int ip6_rt_gc_elasticity = 9;
+int ip6_rt_mtu_expires = 10*60*HZ;
 
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
 static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
@@ -97,7 +98,7 @@
 
 struct rt6_info ip6_null_entry = {
 	{{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), &loopback_dev,
-	  -1, 0, 0, 0, 0, 0, 0, 0,
+	  -1, 0, 0, 0, 0, 0, 0, 0, 0,
 	  -ENETUNREACH, NULL, NULL,
 	  ip6_pkt_discard, ip6_pkt_discard,
 #ifdef CONFIG_NET_CLS_ROUTE
@@ -105,7 +106,7 @@
 #endif
 	  &ip6_dst_ops}},
 	NULL, {{{0}}}, RTF_REJECT|RTF_NONEXTHOP, ~0U,
-	255, 0, ATOMIC_INIT(1), {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
+	255, ATOMIC_INIT(1), {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
 };
 
 struct fib6_node ip6_routing_table = {
@@ -515,13 +516,30 @@
 
 static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
 {
-	dst_release(dst);
+	struct rt6_info *rt = (struct rt6_info *) dst;
+
+	if (rt) {
+		if (rt->rt6i_flags & RTF_CACHE)
+			ip6_del_rt(rt);
+		dst_release(dst);
+	}
 	return NULL;
 }
 
 static void ip6_link_failure(struct sk_buff *skb)
 {
+	struct rt6_info *rt;
+
 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+
+	rt = (struct rt6_info *) skb->dst;
+	if (rt) {
+		if (rt->rt6i_flags&RTF_CACHE) {
+			dst_set_expires(&rt->u.dst, 0);
+			rt->rt6i_flags |= RTF_EXPIRES;
+		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
+			rt->rt6i_node->fn_sernum = -1;
+	}
 }
 
 static int ip6_dst_gc()
@@ -1009,12 +1027,10 @@
 	   when cache entry will expire old pmtu
 	   would return automatically.
 	 */
-	if (rt->rt6i_dst.plen == 128) {
-		/*
-		 *	host route
-		 */
+	if (rt->rt6i_flags & RTF_CACHE) {
 		rt->u.dst.pmtu = pmtu;
-		rt->rt6i_flags |= RTF_MODIFIED;
+		dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+		rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
 		goto out;
 	}
 
@@ -1025,9 +1041,12 @@
 	 */
 	if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
 		nrt = rt6_cow(rt, daddr, saddr);
-		nrt->u.dst.pmtu = pmtu;
-		nrt->rt6i_flags |= RTF_DYNAMIC;
-		dst_release(&nrt->u.dst);
+		if (!nrt->u.dst.error) {
+			nrt->u.dst.pmtu = pmtu;
+			dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+			nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
+			dst_release(&nrt->u.dst);
+		}
 	} else {
 		nrt = ip6_rt_copy(rt);
 		if (nrt == NULL)
@@ -1035,7 +1054,8 @@
 		ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr);
 		nrt->rt6i_dst.plen = 128;
 		nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
-		nrt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
+		dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+		nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES;
 		nrt->u.dst.pmtu = pmtu;
 		rt6_ins(nrt);
 	}
@@ -1069,7 +1089,7 @@
 
 		ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
 		rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
-		rt->rt6i_metric = ort->rt6i_metric;
+		rt->rt6i_metric = 0;
 
 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
 #ifdef CONFIG_IPV6_SUBTREES
@@ -1521,9 +1541,9 @@
 	if (iif)
 		RTA_PUT(skb, RTA_IIF, 4, &iif);
 	else if (dst) {
-		struct inet6_ifaddr *ifp = ipv6_get_saddr(&rt->u.dst, dst);
-		if (ifp)
-			RTA_PUT(skb, RTA_PREFSRC, 16, &ifp->addr);
+		struct in6_addr saddr_buf;
+		if (ipv6_get_saddr(&rt->u.dst, dst, &saddr_buf))
+			RTA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
 	}
 	mx = (struct rtattr*)skb->tail;
 	RTA_PUT(skb, RTA_METRICS, 0, NULL);
@@ -1722,7 +1742,7 @@
 	struct sk_buff *skb;
 	int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
 
-	skb = alloc_skb(size, GFP_ATOMIC);
+	skb = alloc_skb(size, gfp_any());
 	if (!skb) {
 		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
 		return;
@@ -1733,7 +1753,7 @@
 		return;
 	}
 	NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_ROUTE;
-	netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_ROUTE, GFP_ATOMIC);
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_ROUTE, gfp_any());
 }
 
 #endif
@@ -1915,6 +1935,9 @@
          &proc_dointvec_jiffies},
 	{NET_IPV6_ROUTE_GC_ELASTICITY, "gc_elasticity",
          &ip6_rt_gc_elasticity, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV6_ROUTE_MTU_EXPIRES, "mtu_expires",
+         &ip6_rt_mtu_expires, sizeof(int), 0644, NULL,
          &proc_dointvec_jiffies},
 	 {0}
 };
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.2.3/linux/net/ipv6/sit.c	Mon Oct  5 13:13:49 1998
+++ linux/net/ipv6/sit.c	Sun Mar 21 07:22:00 1999
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.29 1998/10/03 09:38:47 davem Exp $
+ *	$Id: sit.c,v 1.30 1999/03/21 05:22:58 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -94,6 +94,48 @@
 	return NULL;
 }
 
+static struct ip_tunnel ** ipip6_bucket(struct ip_tunnel *t)
+{
+	u32 remote = t->parms.iph.daddr;
+	u32 local = t->parms.iph.saddr;
+	unsigned h = 0;
+	int prio = 0;
+
+	if (remote) {
+		prio |= 2;
+		h ^= HASH(remote);
+	}
+	if (local) {
+		prio |= 1;
+		h ^= HASH(local);
+	}
+	return &tunnels[prio][h];
+}
+
+static void ipip6_tunnel_unlink(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp;
+
+	for (tp = ipip6_bucket(t); *tp; tp = &(*tp)->next) {
+		if (t == *tp) {
+			net_serialize_enter();
+			*tp = t->next;
+			net_serialize_leave();
+			break;
+		}
+	}
+}
+
+static void ipip6_tunnel_link(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp = ipip6_bucket(t);
+
+	net_serialize_enter();
+	t->next = *tp;
+	*tp = t;
+	net_serialize_leave();
+}
+
 struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int create)
 {
 	u32 remote = parms->iph.daddr;
@@ -145,10 +187,7 @@
 	if (register_netdevice(dev) < 0)
 		goto failed;
 
-	start_bh_atomic();
-	nt->next = t;
-	*tp = nt;
-	end_bh_atomic();
+	ipip6_tunnel_link(nt);
 	/* Do not decrement MOD_USE_COUNT here. */
 	return nt;
 
@@ -160,37 +199,18 @@
 
 static void ipip6_tunnel_destroy(struct device *dev)
 {
-	struct ip_tunnel *t, **tp;
-	struct ip_tunnel *t0 = (struct ip_tunnel*)dev->priv;
-	u32 remote = t0->parms.iph.daddr;
-	u32 local = t0->parms.iph.saddr;
-	unsigned h = 0;
-	int prio = 0;
-
 	if (dev == &ipip6_fb_tunnel_dev) {
+		net_serialize_enter();
 		tunnels_wc[0] = NULL;
+		net_serialize_leave();
 		return;
-	}
-
-	if (remote) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	if (local) {
-		prio |= 1;
-		h ^= HASH(local);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
-		if (t == t0) {
-			*tp = t->next;
-			kfree(dev);
-			MOD_DEC_USE_COUNT;
-			break;
-		}
+	} else {
+		ipip6_tunnel_unlink((struct ip_tunnel*)dev->priv);
+		kfree(dev);
+		MOD_DEC_USE_COUNT;
 	}
 }
 
-
 void ipip6_err(struct sk_buff *skb, unsigned char *dp, int len)
 {
 #ifndef I_WISH_WORLD_WERE_PERFECT
@@ -570,6 +590,32 @@
 			p.iph.frag_off |= __constant_htons(IP_DF);
 
 		t = ipip6_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+
+		if (dev != &ipip6_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
+		    t != &ipip6_fb_tunnel) {
+			if (t != NULL) {
+				if (t->dev != dev) {
+					err = -EEXIST;
+					break;
+				}
+			} else {
+				if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
+				    (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
+					err = -EINVAL;
+					break;
+				}
+				t = (struct ip_tunnel*)dev->priv;
+				start_bh_atomic();
+				ipip6_tunnel_unlink(t);
+				t->parms.iph.saddr = p.iph.saddr;
+				t->parms.iph.daddr = p.iph.daddr;
+				memcpy(dev->dev_addr, &p.iph.saddr, 4);
+				memcpy(dev->broadcast, &p.iph.daddr, 4);
+				ipip6_tunnel_link(t);
+				end_bh_atomic();
+				netdev_state_change(dev);
+			}
+		}
 
 		if (t) {
 			err = 0;
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.2.3/linux/net/ipv6/tcp_ipv6.c	Wed Mar 10 15:29:52 1999
+++ linux/net/ipv6/tcp_ipv6.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.96 1999/03/05 13:23:13 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.100 1999/03/21 05:22:59 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -376,12 +376,13 @@
 	struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	struct inet6_ifaddr *ifa;
 	struct in6_addr *saddr = NULL;
+	struct in6_addr saddr_buf;
 	struct flowi fl;
 	struct dst_entry *dst;
 	struct sk_buff *buff;
 	int addr_type;
+	int err;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -428,7 +429,6 @@
 	if (addr_type == IPV6_ADDR_MAPPED) {
 		u32 exthdrlen = tp->ext_header_len;
 		struct sockaddr_in sin;
-		int err;
 
 		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
 
@@ -472,9 +472,9 @@
 
 	dst = ip6_route_output(sk, &fl);
 
-	if (dst->error) {
+	if ((err = dst->error) != 0) {
 		dst_release(dst);
-		return dst->error;
+		return err;
 	}
 
 	if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) {
@@ -489,18 +489,17 @@
 	ip6_dst_store(sk, dst, NULL);
 
 	if (saddr == NULL) {
-		ifa = ipv6_get_saddr(dst, &np->daddr);
-
-		if (ifa == NULL)
-			return -ENETUNREACH;
-		
-		saddr = &ifa->addr;
+		err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf);
+		if (err)
+			return err;
 
-		/* set the source address */
-		ipv6_addr_copy(&np->rcv_saddr, saddr);
-		ipv6_addr_copy(&np->saddr, saddr);
+		saddr = &saddr_buf;
 	}
 
+	/* set the source address */
+	ipv6_addr_copy(&np->rcv_saddr, saddr);
+	ipv6_addr_copy(&np->saddr, saddr);
+
 	tp->ext_header_len = 0;
 	if (np->opt)
 		tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen;
@@ -535,7 +534,6 @@
 
 static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
-	struct tcp_opt *tp;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	int retval = -EINVAL;
 
@@ -564,14 +562,7 @@
 			goto out;
 	}
 
-	lock_sock(sk);
-	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, 
-				msg->msg_flags);
-	/* Push out partial tail frames if needed. */
-	tp = &(sk->tp_pinfo.af_tcp);
-	if(tp->send_head && tcp_snd_test(sk, tp->send_head))
-		tcp_write_xmit(sk);
-	release_sock(sk);
+	retval = tcp_do_sendmsg(sk, msg);
 
 out:
 	return retval;
@@ -610,11 +601,14 @@
 	np = &sk->net_pinfo.af_inet6;
 	if (type == ICMPV6_PKT_TOOBIG) {
 		struct dst_entry *dst = NULL;
-		/* icmp should have updated the destination cache entry */
+
+		if (atomic_read(&sk->sock_readers))
+			return;
 
 		if (sk->state == TCP_LISTEN)
 			return;
 
+		/* icmp should have updated the destination cache entry */
 		if (sk->dst_cache)
 			dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
@@ -639,8 +633,7 @@
 
 		if (dst->error) {
 			sk->err_soft = -dst->error;
-		} else if (tp->pmtu_cookie > dst->pmtu
-			   && !atomic_read(&sk->sock_readers)) {
+		} else if (tp->pmtu_cookie > dst->pmtu) {
 			tcp_sync_mss(sk, dst->pmtu);
 			tcp_simple_retransmit(sk);
 		} /* else let the usual retransmit timer handle it */
@@ -1201,6 +1194,11 @@
 	if (skb->protocol == __constant_htons(ETH_P_IP))
 		return tcp_v4_do_rcv(sk, skb);
 
+#ifdef CONFIG_FILTER
+	if (sk->filter && sk_filter(skb, sk->filter))
+		goto discard;
+#endif /* CONFIG_FILTER */
+
 	/*
 	 *	socket locking is here for SMP purposes as backlog rcv
 	 *	is currently called with bh processing disabled.
@@ -1428,6 +1426,9 @@
 {
 	struct in6_addr *saddr;
 	struct in6_addr *daddr;
+
+	if (skb->protocol == __constant_htons(ETH_P_IP))
+		return ipv4_specific.get_sock(skb, th);
 
 	saddr = &skb->nh.ipv6h->saddr;
 	daddr = &skb->nh.ipv6h->daddr;
diff -u --recursive --new-file v2.2.3/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.2.3/linux/net/ipv6/udp.c	Thu Nov 19 09:56:29 1998
+++ linux/net/ipv6/udp.c	Sun Mar 21 07:22:00 1999
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.37 1998/11/08 11:17:10 davem Exp $
+ *	$Id: udp.c,v 1.38 1999/03/21 05:23:00 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -201,8 +201,8 @@
 	struct sockaddr_in6	*usin = (struct sockaddr_in6 *) uaddr;
 	struct ipv6_pinfo      	*np = &sk->net_pinfo.af_inet6;
 	struct in6_addr		*daddr;
+	struct in6_addr		saddr;
 	struct dst_entry	*dst;
-	struct inet6_ifaddr	*ifa;
 	struct flowi		fl;
 	int			addr_type;
 	int			err;
@@ -284,28 +284,29 @@
 
 	dst = ip6_route_output(sk, &fl);
 
-	if (dst->error) {
+	if ((err = dst->error) != 0) {
 		dst_release(dst);
-		return dst->error;
+		return err;
 	}
 
 	ip6_dst_store(sk, dst, fl.nl_u.ip6_u.daddr);
 
 	/* get the source adddress used in the apropriate device */
 
-	ifa = ipv6_get_saddr(dst, daddr);
+	err = ipv6_get_saddr(dst, daddr, &saddr);
 
-	if(ipv6_addr_any(&np->saddr))
-		ipv6_addr_copy(&np->saddr, &ifa->addr);
+	if (err == 0) {
+		if(ipv6_addr_any(&np->saddr))
+			ipv6_addr_copy(&np->saddr, &saddr);
 
-	if(ipv6_addr_any(&np->rcv_saddr)) {
-		ipv6_addr_copy(&np->rcv_saddr, &ifa->addr);
-		sk->rcv_saddr = 0xffffffff;
+		if(ipv6_addr_any(&np->rcv_saddr)) {
+			ipv6_addr_copy(&np->rcv_saddr, &saddr);
+			sk->rcv_saddr = 0xffffffff;
+		}
+		sk->state = TCP_ESTABLISHED;
 	}
 
-	sk->state = TCP_ESTABLISHED;
-
-	return(0);
+	return err;
 }
 
 static void udpv6_close(struct sock *sk, long timeout)
@@ -317,7 +318,7 @@
 	destroy_sock(sk);
 }
 
-#if defined(CONFIG_FILTER) || !defined(HAVE_CSUM_COPY_USER)
+#ifndef HAVE_CSUM_COPY_USER
 #undef CONFIG_UDP_DELAY_CSUM
 #endif
 
@@ -352,11 +353,11 @@
 	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), 
 				      msg->msg_iov, copied);
 #else
-	if (sk->no_check || skb->ip_summed==CHECKSUM_UNNECESSARY) {
+	if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					      copied);
 	} else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
-		if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) {
+		if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
 			/* Error for blocking case is chosen to masquerade
 			   as some normal condition.
 			 */
@@ -373,7 +374,7 @@
 		csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err);
 		if (err)
 			goto out_free;
-		if (csum_fold(csum)) {
+		if ((unsigned short)csum_fold(csum)) {
 			/* Error for blocking case is chosen to masquerade
 			   as some normal condition.
 			 */
@@ -454,6 +455,17 @@
 
 static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
+#if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM)
+	if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
+		if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
+			udp_stats_in6.UdpInErrors++;
+			ipv6_statistics.Ip6InDiscards++;
+			kfree_skb(skb);
+			return 0;
+		}
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+#endif
 	if (sock_queue_rcv_skb(sk,skb)<0) {
 		udp_stats_in6.UdpInErrors++;
 		ipv6_statistics.Ip6InDiscards++;
@@ -627,14 +639,13 @@
 	if (sk == NULL) {
 #ifdef CONFIG_UDP_DELAY_CSUM
 		if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
-		    csum_fold(csum_partial((char*)uh, len, skb->csum)))
+		    (unsigned short)csum_fold(csum_partial((char*)uh, len, skb->csum)))
 			goto discard;
 #endif
-		
 		udp_stats_in6.UdpNoPorts++;
 
 		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
-		
+
 		kfree_skb(skb);
 		return(0);
 	}
diff -u --recursive --new-file v2.2.3/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- v2.2.3/linux/net/netlink/af_netlink.c	Sun Nov  8 14:03:15 1998
+++ linux/net/netlink/af_netlink.c	Sun Mar 21 07:22:00 1999
@@ -55,6 +55,13 @@
 static int netlink_dump(struct sock *sk);
 static void netlink_destroy_callback(struct netlink_callback *cb);
 
+/* Netlink table lock. It protects against sk list changes
+   during uninterruptible sleeps in netlink_broadcast.
+
+   These lock MUST NOT be used from bh/irq on SMP kernels, because
+   It would result in race in netlink_wait_on_table.
+ */
+
 extern __inline__ void
 netlink_wait_on_table(int protocol)
 {
@@ -69,16 +76,16 @@
 }
 
 extern __inline__ void
-netlink_unlock_table(int protocol, int wakeup)
+netlink_unlock_table(int protocol)
 {
 #if 0
 	/* F...g gcc does not eat it! */
 
-	if (atomic_dec_and_test(&nl_table_lock[protocol]) && wakeup)
+	if (atomic_dec_and_test(&nl_table_lock[protocol]))
 		wake_up(&nl_table_wait);
 #else
 	atomic_dec(&nl_table_lock[protocol]);
-	if (atomic_read(&nl_table_lock[protocol]) && wakeup)
+	if (!atomic_read(&nl_table_lock[protocol]))
 		wake_up(&nl_table_wait);
 #endif
 }
@@ -125,7 +132,9 @@
 	struct sock **skp;
 	for (skp = &nl_table[sk->protocol]; *skp; skp = &((*skp)->next)) {
 		if (*skp == sk) {
+			start_bh_atomic();
 			*skp = sk->next;
+			end_bh_atomic();
 			return;
 		}
 	}
@@ -186,7 +195,7 @@
 	   transport (and AF_UNIX datagram, when it will be repaired).
 	   
 	   Someone could wait on our sock->wait now.
-	   We cannot release socket until waiter will remove yourself
+	   We cannot release socket until waiter will remove itself
 	   from wait queue. I choose the most conservetive way of solving
 	   the problem.
 
@@ -218,8 +227,6 @@
 	struct sock *sk = sock->sk;
 	struct sock *osk;
 
-	netlink_wait_on_table(sk->protocol);
-
 	sk->protinfo.af_netlink.groups = 0;
 	sk->protinfo.af_netlink.pid = current->pid;
 
@@ -264,8 +271,6 @@
 		return 0;
 	}
 
-	netlink_wait_on_table(sk->protocol);
-
 	for (osk=nl_table[sk->protocol]; osk; osk=osk->next) {
 		if (osk->protinfo.af_netlink.pid == nladdr->nl_pid)
 			return -EADDRINUSE;
@@ -332,7 +337,7 @@
 retry:
 	for (sk = nl_table[protocol]; sk; sk = sk->next) {
 		if (sk->protinfo.af_netlink.pid != pid)
-				continue;
+			continue;
 
 		netlink_lock(sk);
 
@@ -416,7 +421,8 @@
 
 	/* While we sleep in clone, do not allow to change socket list */
 
-	netlink_lock_table(protocol);
+	if (allocation == GFP_KERNEL)
+		netlink_lock_table(protocol);
 
 	for (sk = nl_table[protocol]; sk; sk = sk->next) {
 		if (ssk == sk)
@@ -454,7 +460,8 @@
 		netlink_unlock(sk);
 	}
 
-	netlink_unlock_table(protocol, allocation == GFP_KERNEL);
+	if (allocation == GFP_KERNEL)
+		netlink_unlock_table(protocol);
 
 	if (skb2)
 		kfree_skb(skb2);
@@ -475,7 +482,7 @@
 		    !(sk->protinfo.af_netlink.groups&group))
 			continue;
 
-		sk->err = -code;
+		sk->err = code;
 		sk->state_change(sk);
 	}
 }
@@ -739,15 +746,20 @@
 void netlink_detach(int unit)
 {
 	struct socket *sock = netlink_kernel[unit];
+
+	net_serialize_enter();
 	netlink_kernel[unit] = NULL;
+	net_serialize_leave();
 	sock_release(sock);
 }
 
 int netlink_post(int unit, struct sk_buff *skb)
 {
-	if (netlink_kernel[unit]) {
+	struct socket *sock = netlink_kernel[unit];
+	barrier();
+	if (sock) {
 		memset(skb->cb, 0, sizeof(skb->cb));
-		netlink_broadcast(netlink_kernel[unit]->sk, skb, 0, ~0, GFP_ATOMIC);
+		netlink_broadcast(sock->sk, skb, 0, ~0, GFP_ATOMIC);
 		return 0;
 	}
 	return -EUNATCH;;
@@ -800,6 +812,8 @@
 	len-=(offset-begin);
 	if(len>length)
 		len=length;
+	if(len<0)
+		len=0;
 	return len;
 }
 #endif
diff -u --recursive --new-file v2.2.3/linux/net/netsyms.c linux/net/netsyms.c
--- v2.2.3/linux/net/netsyms.c	Tue Feb 23 15:21:36 1999
+++ linux/net/netsyms.c	Sun Mar 21 07:22:00 1999
@@ -428,6 +428,7 @@
 EXPORT_SYMBOL(unregister_netdevice);
 EXPORT_SYMBOL(register_netdev);
 EXPORT_SYMBOL(unregister_netdev);
+EXPORT_SYMBOL(netdev_state_change);
 EXPORT_SYMBOL(ether_setup);
 EXPORT_SYMBOL(dev_new_index);
 EXPORT_SYMBOL(dev_get_by_index);
@@ -482,6 +483,7 @@
 EXPORT_SYMBOL(qdisc_create_dflt);
 EXPORT_SYMBOL(noop_qdisc);
 #ifdef CONFIG_NET_SCHED
+PSCHED_EXPORTLIST;
 EXPORT_SYMBOL(pfifo_qdisc_ops);
 EXPORT_SYMBOL(register_qdisc);
 EXPORT_SYMBOL(unregister_qdisc);
diff -u --recursive --new-file v2.2.3/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.2.3/linux/net/packet/af_packet.c	Mon Oct  5 13:13:49 1998
+++ linux/net/packet/af_packet.c	Sun Mar 21 07:22:00 1999
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- * Version:	$Id: af_packet.c,v 1.18 1998/10/03 15:55:24 freitag Exp $
+ * Version:	$Id: af_packet.c,v 1.19 1999/03/21 05:23:03 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -831,7 +831,7 @@
 	 *	Free or return the buffer as appropriate. Again this
 	 *	hides all the races and re-entrancy issues from us.
 	 */
-	err = copied;
+	err = (flags&MSG_TRUNC) ? skb->len : copied;
 
 out_free:
 	skb_free_datagram(sk, skb);
diff -u --recursive --new-file v2.2.3/linux/net/sched/Config.in linux/net/sched/Config.in
--- v2.2.3/linux/net/sched/Config.in	Thu May 14 19:47:45 1998
+++ linux/net/sched/Config.in	Sun Mar 21 07:22:00 1999
@@ -18,10 +18,11 @@
 fi
 bool 'Packet classifier API' CONFIG_NET_CLS
 if [ "$CONFIG_NET_CLS" = "y" ]; then
-  bool 'Routing tables based classifier' CONFIG_NET_CLS_ROUTE
-  if [ "$CONFIG_IP_FIREWALL" = "y" ]; then
-    bool 'Firewall based classifier' CONFIG_NET_CLS_FW
+  tristate 'Routing table based classifier' CONFIG_NET_CLS_ROUTE4
+  if [ "$CONFIG_NET_CLS_ROUTE4" != "n" ]; then
+    define_bool CONFIG_NET_CLS_ROUTE y
   fi
+  tristate 'Firewall based classifier' CONFIG_NET_CLS_FW
   tristate 'U32 classifier' CONFIG_NET_CLS_U32
   if [ "$CONFIG_NET_QOS" = "y" ]; then
     tristate 'Special RSVP classifier' CONFIG_NET_CLS_RSVP
diff -u --recursive --new-file v2.2.3/linux/net/sched/Makefile linux/net/sched/Makefile
--- v2.2.3/linux/net/sched/Makefile	Sat May  2 14:19:55 1998
+++ linux/net/sched/Makefile	Sun Mar 21 07:22:00 1999
@@ -125,12 +125,20 @@
   endif
 endif
 
-ifeq ($(CONFIG_NET_CLS_ROUTE), y)
+ifeq ($(CONFIG_NET_CLS_ROUTE4), y)
 O_OBJS += cls_route.o
+else
+  ifeq ($(CONFIG_NET_CLS_ROUTE4), m)
+	M_OBJS += cls_route.o
+  endif
 endif
 
 ifeq ($(CONFIG_NET_CLS_FW), y)
 O_OBJS += cls_fw.o
+else
+  ifeq ($(CONFIG_NET_CLS_FW), m)
+	M_OBJS += cls_fw.o
+  endif
 endif
 
 endif
diff -u --recursive --new-file v2.2.3/linux/net/sched/cls_api.c linux/net/sched/cls_api.c
--- v2.2.3/linux/net/sched/cls_api.c	Sat Sep  5 16:46:42 1998
+++ linux/net/sched/cls_api.c	Sun Mar 21 07:22:00 1999
@@ -7,6 +7,10 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ *
+ * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
  */
 
 #include <asm/uaccess.h>
@@ -27,6 +31,7 @@
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
+#include <linux/kmod.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
@@ -87,21 +92,13 @@
 
 /* Select new prio value from the range, managed by kernel. */
 
-static __inline__ u32 tcf_auto_prio(struct tcf_proto *tp, u32 prio)
+static __inline__ u32 tcf_auto_prio(struct tcf_proto *tp)
 {
 	u32 first = TC_H_MAKE(0xC0000000U,0U);
 
-	if (!tp || tp->next == NULL)
-		return first;
-
-	if (prio == TC_H_MAKE(0xFFFF0000U,0U))
-		first = tp->prio+1; 
-	else
+	if (tp)
 		first = tp->prio-1;
 
-	if (first == prio)
-		first = tp->prio;
-
 	return first;
 }
 
@@ -129,10 +126,7 @@
 		/* If no priority is given, user wants we allocated it. */
 		if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE))
 			return -ENOENT;
-		if (n->nlmsg_flags&NLM_F_APPEND)
-			prio = TC_H_MAKE(0xFFFF0000U,0U);
-		else
-			prio = TC_H_MAKE(0x80000000U,0U);
+		prio = TC_H_MAKE(0x80000000U,0U);
 	}
 
 	/* Find head of filter chain. */
@@ -194,6 +188,18 @@
 		if ((tp = kmalloc(sizeof(*tp), GFP_KERNEL)) == NULL)
 			goto errout;
 		tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND-1]);
+#ifdef CONFIG_KMOD
+		if (tp_ops==NULL && tca[TCA_KIND-1] != NULL) {
+			struct rtattr *kind = tca[TCA_KIND-1];
+			char module_name[4 + IFNAMSIZ + 1];
+
+			if (RTA_PAYLOAD(kind) <= IFNAMSIZ) {
+				sprintf(module_name, "cls_%s", (char*)RTA_DATA(kind));
+				request_module (module_name);
+				tp_ops = tcf_proto_lookup_ops(kind);
+			}
+		}
+#endif
 		if (tp_ops == NULL) {
 			err = -EINVAL;
 			kfree(tp);
@@ -202,7 +208,7 @@
 		memset(tp, 0, sizeof(*tp));
 		tp->ops = tp_ops;
 		tp->protocol = protocol;
-		tp->prio = nprio ? : tcf_auto_prio(*back, prio);
+		tp->prio = nprio ? : tcf_auto_prio(*back);
 		tp->q = q;
 		tp->classify = tp_ops->classify;
 		tp->classid = parent;
@@ -220,7 +226,9 @@
 
 	if (fh == 0) {
 		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
+			net_serialize_enter();
 			*back = tp->next;
+			net_serialize_leave();
 			tp->ops->destroy(tp);
 			kfree(tp);
 			err = 0;
@@ -249,7 +257,7 @@
 		}
 	}
 
-	err = tp->ops->change(tp, t->tcm_handle, tca, &fh);
+	err = tp->ops->change(tp, cl, t->tcm_handle, tca, &fh);
 	if (err == 0)
 		tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);
 
@@ -336,12 +344,16 @@
 		return skb->len;
 	if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)
 		return skb->len;
-	if ((q = qdisc_lookup(dev, tcm->tcm_parent)) == NULL)
+	if (!tcm->tcm_parent)
+		q = dev->qdisc_sleeping;
+	else
+		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
+	if (q == NULL)
 		return skb->len;
-	cops = q->ops->cl_ops;
+	if ((cops = q->ops->cl_ops) == NULL)
+		goto errout;
 	if (TC_H_MIN(tcm->tcm_parent)) {
-		if (cops)
-			cl = cops->get(q, tcm->tcm_parent);
+		cl = cops->get(q, tcm->tcm_parent);
 		if (cl == 0)
 			goto errout;
 	}
@@ -360,7 +372,7 @@
 		    TC_H_MIN(tcm->tcm_info) != tp->protocol)
 			continue;
 		if (t > s_t)
-			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
 		if (cb->args[1] == 0) {
 			if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).pid,
 					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER) <= 0) {
@@ -418,8 +430,8 @@
 #ifdef CONFIG_NET_CLS_U32
 	INIT_TC_FILTER(u32);
 #endif
-#ifdef CONFIG_NET_CLS_ROUTE
-	INIT_TC_FILTER(route);
+#ifdef CONFIG_NET_CLS_ROUTE4
+	INIT_TC_FILTER(route4);
 #endif
 #ifdef CONFIG_NET_CLS_FW
 	INIT_TC_FILTER(fw);
diff -u --recursive --new-file v2.2.3/linux/net/sched/cls_fw.c linux/net/sched/cls_fw.c
--- v2.2.3/linux/net/sched/cls_fw.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/cls_fw.c	Sun Mar 21 07:22:00 1999
@@ -1,5 +1,5 @@
 /*
- * net/sched/cls_fw.c	Routing table based packet classifier.
+ * net/sched/cls_fw.c	Classifier mapping ipchains' fwmark to traffic class.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -9,6 +9,7 @@
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -34,15 +35,56 @@
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
+struct fw_head
+{
+	struct fw_filter *ht[256];
+};
+
+struct fw_filter
+{
+	struct fw_filter	*next;
+	u32			id;
+	struct tcf_result	res;
+#ifdef CONFIG_NET_CLS_POLICE
+	struct tcf_police	*police;
+#endif
+};
+
+static __inline__ int fw_hash(u32 handle)
+{
+	return handle&0xFF;
+}
 
 static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
 			  struct tcf_result *res)
 {
-	u32 clid = skb->fwmark;
+	struct fw_head *head = (struct fw_head*)tp->root;
+	struct fw_filter *f;
+#ifdef CONFIG_IP_FIREWALL
+	u32 id = skb->fwmark;
+#else
+	u32 id = 0;
+#endif
+
+	if (head == NULL)
+		goto old_method;
+
+	for (f=head->ht[fw_hash(id)]; f; f=f->next) {
+		if (f->id == id) {
+			*res = f->res;
+#ifdef CONFIG_NET_CLS_POLICE
+			if (f->police)
+				return tcf_police(skb, f->police);
+#endif
+			return 0;
+		}
+	}
+	return -1;
 
-	if (clid && (TC_H_MAJ(clid) == 0 ||
-		     !(TC_H_MAJ(clid^tp->q->handle)))) {
-		res->classid = clid;
+old_method:
+	if (id && (TC_H_MAJ(id) == 0 ||
+		     !(TC_H_MAJ(id^tp->q->handle)))) {
+		res->classid = id;
 		res->class = 0;
 		return 0;
 	}
@@ -51,6 +93,16 @@
 
 static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
 {
+	struct fw_head *head = (struct fw_head*)tp->root;
+	struct fw_filter *f;
+
+	if (head == NULL)
+		return 0;
+
+	for (f=head->ht[fw_hash(handle)]; f; f=f->next) {
+		if (f->id == handle)
+			return (unsigned long)f;
+	}
 	return 0;
 }
 
@@ -60,25 +112,233 @@
 
 static int fw_init(struct tcf_proto *tp)
 {
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
 static void fw_destroy(struct tcf_proto *tp)
 {
+	struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
+	struct fw_filter *f;
+	int h;
+
+	if (head == NULL) {
+		MOD_DEC_USE_COUNT;
+		return;
+	}
+
+	for (h=0; h<256; h++) {
+		while ((f=head->ht[h]) != NULL) {
+			unsigned long cl;
+			head->ht[h] = f->next;
+
+			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
+#ifdef CONFIG_NET_CLS_POLICE
+			tcf_police_release(f->police);
+#endif
+			kfree(f);
+		}
+	}
+	kfree(head);
+	MOD_DEC_USE_COUNT;
 }
 
 static int fw_delete(struct tcf_proto *tp, unsigned long arg)
 {
+	struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
+	struct fw_filter *f = (struct fw_filter*)arg;
+	struct fw_filter **fp;
+
+	if (head == NULL || f == NULL)
+		return -EINVAL;
+
+	for (fp=&head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
+		if (*fp == f) {
+			unsigned long cl;
+
+			net_serialize_enter();
+			*fp = f->next;
+			net_serialize_leave();
+
+			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
+#ifdef CONFIG_NET_CLS_POLICE
+			tcf_police_release(f->police);
+#endif
+			kfree(f);
+			return 0;
+		}
+	}
 	return -EINVAL;
 }
 
-static int fw_change(struct tcf_proto *tp, u32 handle,
-			struct rtattr **tca,
-			unsigned long *arg)
+static int fw_change(struct tcf_proto *tp, unsigned long base,
+		     u32 handle,
+		     struct rtattr **tca,
+		     unsigned long *arg)
 {
-	return handle ? -EINVAL : 0;
+	struct fw_head *head = (struct fw_head*)tp->root;
+	struct fw_filter *f;
+	struct rtattr *opt = tca[TCA_OPTIONS-1];
+	struct rtattr *tb[TCA_FW_MAX];
+	int err;
+
+	if (!opt)
+		return handle ? -EINVAL : 0;
+
+	if (rtattr_parse(tb, TCA_FW_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)) < 0)
+		return -EINVAL;
+
+	if ((f = (struct fw_filter*)*arg) != NULL) {
+		/* Node exists: adjust only classid */
+
+		if (f->id != handle && handle)
+			return -EINVAL;
+		if (tb[TCA_FW_CLASSID-1]) {
+			unsigned long cl;
+
+			f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
+			cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid);
+			cl = cls_set_class(&f->res.class, cl);
+			if (cl)
+				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
+		}
+#ifdef CONFIG_NET_CLS_POLICE
+		if (tb[TCA_FW_POLICE-1]) {
+			struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
+			net_serialize_enter();
+			police = xchg(&f->police, police);
+			net_serialize_leave();
+			tcf_police_release(police);
+		}
+#endif
+		return 0;
+	}
+
+	if (!handle)
+		return -EINVAL;
+
+	if (head == NULL) {
+		head = kmalloc(sizeof(struct fw_head), GFP_KERNEL);
+		if (head == NULL)
+			return -ENOBUFS;
+		memset(head, 0, sizeof(*head));
+		net_serialize_enter();
+		tp->root = head;
+		net_serialize_leave();
+	}
+
+	f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
+	if (f == NULL)
+		return -ENOBUFS;
+	memset(f, 0, sizeof(*f));
+
+	f->id = handle;
+
+	if (tb[TCA_FW_CLASSID-1]) {
+		err = -EINVAL;
+		if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != 4)
+			goto errout;
+		f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
+		cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+	}
+
+#ifdef CONFIG_NET_CLS_POLICE
+	if (tb[TCA_FW_POLICE-1])
+		f->police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
+#endif
+
+	f->next = head->ht[fw_hash(handle)];
+	net_serialize_enter();
+	head->ht[fw_hash(handle)] = f;
+	net_serialize_leave();
+	*arg = (unsigned long)f;
+	return 0;
+
+errout:
+	if (f)
+		kfree(f);
+	return err;
 }
 
+static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+{
+	struct fw_head *head = (struct fw_head*)tp->root;
+	int h;
+
+	if (head == NULL)
+		arg->stop = 1;
+
+	if (arg->stop)
+		return;
+
+	for (h = 0; h <= 256; h++) {
+		struct fw_filter *f;
+
+		for (f = head->ht[h]; f; f = f->next) {
+			if (arg->count < arg->skip) {
+				arg->count++;
+				continue;
+			}
+			if (arg->fn(tp, (unsigned long)f, arg) < 0) {
+				arg->stop = 1;
+				break;
+			}
+			arg->count++;
+		}
+	}
+}
+
+#ifdef CONFIG_RTNETLINK
+static int fw_dump(struct tcf_proto *tp, unsigned long fh,
+		   struct sk_buff *skb, struct tcmsg *t)
+{
+	struct fw_filter *f = (struct fw_filter*)fh;
+	unsigned char	 *b = skb->tail;
+	struct rtattr *rta;
+
+	if (f == NULL)
+		return skb->len;
+
+	t->tcm_handle = f->id;
+
+	if (!f->res.classid && !f->police)
+		return skb->len;
+
+	rta = (struct rtattr*)b;
+	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+
+	if (f->res.classid)
+		RTA_PUT(skb, TCA_FW_CLASSID, 4, &f->res.classid);
+#ifdef CONFIG_NET_CLS_POLICE
+	if (f->police) {
+		struct rtattr * p_rta = (struct rtattr*)skb->tail;
+
+		RTA_PUT(skb, TCA_FW_POLICE, 0, NULL);
+
+		if (tcf_police_dump(skb, f->police) < 0)
+			goto rtattr_failure;
+
+		p_rta->rta_len = skb->tail - (u8*)p_rta;
+	}
+#endif
+
+	rta->rta_len = skb->tail - b;
+#ifdef CONFIG_NET_CLS_POLICE
+	if (f->police) {
+		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+	}
+#endif
+	return skb->len;
+
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+#endif
+
+
 struct tcf_proto_ops cls_fw_ops = {
 	NULL,
 	"fw",
@@ -90,5 +350,22 @@
 	fw_put,
 	fw_change,
 	fw_delete,
-	NULL,
+	fw_walk,
+#ifdef CONFIG_RTNETLINK
+	fw_dump
+#else
+	NULL
+#endif
 };
+
+#ifdef MODULE
+int init_module(void)
+{
+	return register_tcf_proto_ops(&cls_fw_ops);
+}
+
+void cleanup_module(void) 
+{
+	unregister_tcf_proto_ops(&cls_fw_ops);
+}
+#endif
diff -u --recursive --new-file v2.2.3/linux/net/sched/cls_route.c linux/net/sched/cls_route.c
--- v2.2.3/linux/net/sched/cls_route.c	Sat May  2 14:19:55 1998
+++ linux/net/sched/cls_route.c	Sun Mar 21 07:22:00 1999
@@ -1,5 +1,5 @@
 /*
- * net/sched/cls_route.c	Routing table based packet classifier.
+ * net/sched/cls_route.c	ROUTE4 classifier.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -10,6 +10,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/config.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -34,65 +35,597 @@
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
+/*
+   1. For now we assume that route tags < 256.
+      It allows to use direct table lookups, instead of hash tables.
+   2. For now we assume that "from TAG" and "fromdev DEV" statements
+      are mutually  exclusive.
+   3. "to TAG from ANY" has higher priority, than "to ANY from XXX"
+ */
+
+struct route4_fastmap
+{
+	struct route4_filter	*filter;
+	u32			id;
+	int			iif;
+};
 
-static int route_classify(struct sk_buff *skb, struct tcf_proto *tp,
-			  struct tcf_result *res)
+struct route4_head
 {
-	struct dst_entry *dst = skb->dst;
+	struct route4_fastmap	fastmap[16];
+	struct route4_bucket	*table[256+1];
+};
+
+struct route4_bucket
+{
+	struct route4_filter	*ht[16+16+1];
+};
 
-	if (dst) {
-		u32 clid = dst->tclassid;
+struct route4_filter
+{
+	struct route4_filter	*next;
+	u32			id;
+	int			iif;
+
+	struct tcf_result	res;
+#ifdef CONFIG_NET_CLS_POLICE
+	struct tcf_police	*police;
+#endif
+
+	u32			handle;
+	struct route4_bucket	*bkt;
+};
+
+#define ROUTE4_FAILURE ((struct route4_filter*)(-1L))
+
+static __inline__ int route4_fastmap_hash(u32 id, int iif)
+{
+	return id&0xF;
+}
 
-		if (clid && (TC_H_MAJ(clid) == 0 ||
-			     !(TC_H_MAJ(clid^tp->q->handle)))) {
-			res->classid = clid;
-			res->class = 0;
+static void route4_reset_fastmap(struct route4_head *head, u32 id)
+{
+	start_bh_atomic();
+	memset(head->fastmap, 0, sizeof(head->fastmap));
+	end_bh_atomic();
+}
+
+static void __inline__
+route4_set_fastmap(struct route4_head *head, u32 id, int iif,
+		   struct route4_filter *f)
+{
+	int h = route4_fastmap_hash(id, iif);
+	head->fastmap[h].id = id;
+	head->fastmap[h].iif = iif;
+	head->fastmap[h].filter = f;
+}
+
+static __inline__ int route4_hash_to(u32 id)
+{
+	return id&0xFF;
+}
+
+static __inline__ int route4_hash_from(u32 id)
+{
+	return (id>>16)&0xF;
+}
+
+static __inline__ int route4_hash_iif(int iif)
+{
+	return 16 + ((iif>>16)&0xF);
+}
+
+static __inline__ int route4_hash_wild(void)
+{
+	return 32;
+}
+
+#ifdef CONFIG_NET_CLS_POLICE
+#define IF_ROUTE_POLICE \
+if (f->police) { \
+	int pol_res = tcf_police(skb, f->police); \
+	if (pol_res >= 0) return pol_res; \
+	dont_cache = 1; \
+	continue; \
+} \
+if (!dont_cache)
+#else
+#define IF_ROUTE_POLICE
+#endif
+
+
+static int route4_classify(struct sk_buff *skb, struct tcf_proto *tp,
+			   struct tcf_result *res)
+{
+	struct route4_head *head = (struct route4_head*)tp->root;
+	struct dst_entry *dst;
+	struct route4_bucket *b;
+	struct route4_filter *f;
+#ifdef CONFIG_NET_CLS_POLICE
+	int dont_cache = 0;
+#endif
+	u32 id, h;
+	int iif;
+
+	if ((dst = skb->dst) == NULL)
+		goto failure;
+
+	id = dst->tclassid;
+	if (head == NULL)
+		goto old_method;
+
+	iif = ((struct rtable*)dst)->key.iif;
+
+	h = route4_fastmap_hash(id, iif);
+	if (id == head->fastmap[h].id &&
+	    iif == head->fastmap[h].iif &&
+	    (f = head->fastmap[h].filter) != NULL) {
+		if (f == ROUTE4_FAILURE)
+			goto failure;
+
+		*res = f->res;
+		return 0;
+	}
+
+	h = route4_hash_to(id);
+
+restart:
+	if ((b = head->table[h]) != NULL) {
+		f = b->ht[route4_hash_from(id)];
+
+		for ( ; f; f = f->next) {
+			if (f->id == id) {
+				*res = f->res;
+				IF_ROUTE_POLICE route4_set_fastmap(head, id, iif, f);
+				return 0;
+			}
+		}
+
+		for (f = b->ht[route4_hash_iif(iif)]; f; f = f->next) {
+			if (f->iif == iif) {
+				*res = f->res;
+				IF_ROUTE_POLICE route4_set_fastmap(head, id, iif, f);
+				return 0;
+			}
+		}
+
+		for (f = b->ht[route4_hash_wild()]; f; f = f->next) {
+			*res = f->res;
+			IF_ROUTE_POLICE route4_set_fastmap(head, id, iif, f);
 			return 0;
 		}
+
+	}
+	if (h < 256) {
+		h = 256;
+		id &= ~0xFFFF;
+		goto restart;
+	}
+
+#ifdef CONFIG_NET_CLS_POLICE
+	if (!dont_cache)
+#endif
+		route4_set_fastmap(head, id, iif, ROUTE4_FAILURE);
+failure:
+	return -1;
+
+old_method:
+	if (id && (TC_H_MAJ(id) == 0 ||
+		   !(TC_H_MAJ(id^tp->q->handle)))) {
+		res->classid = id;
+		res->class = 0;
+		return 0;
 	}
 	return -1;
 }
 
-static unsigned long route_get(struct tcf_proto *tp, u32 handle)
+static u32 to_hash(u32 id)
+{
+	u32 h = id&0xFF;
+	if (id&0x8000)
+		h += 256;
+	return h;
+}
+
+static u32 from_hash(u32 id)
+{
+	id &= 0xFFFF;
+	if (id == 0xFFFF)
+		return 32;
+	if (!(id & 0x8000)) {
+		if (id > 255)
+			return 256;
+		return id&0xF;
+	}
+	return 16 + (id&0xF);
+}
+
+static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
 {
+	struct route4_head *head = (struct route4_head*)tp->root;
+	struct route4_bucket *b;
+	struct route4_filter *f;
+	unsigned h1, h2;
+
+	if (!head)
+		return 0;
+
+	h1 = to_hash(handle);
+	if (h1 > 256)
+		return 0;
+
+	h2 = from_hash(handle>>16);
+	if (h2 > 32)
+		return 0;
+
+	if ((b = head->table[h1]) != NULL) {
+		for (f = b->ht[h2]; f; f = f->next)
+			if (f->handle == handle)
+				return (unsigned long)f;
+	}
 	return 0;
 }
 
-static void route_put(struct tcf_proto *tp, unsigned long f)
+static void route4_put(struct tcf_proto *tp, unsigned long f)
 {
 }
 
-static int route_init(struct tcf_proto *tp)
+static int route4_init(struct tcf_proto *tp)
 {
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
-static void route_destroy(struct tcf_proto *tp)
+static void route4_destroy(struct tcf_proto *tp)
 {
+	struct route4_head *head = xchg(&tp->root, NULL);
+	int h1, h2;
+
+	if (head == NULL) {
+		MOD_DEC_USE_COUNT;
+		return;
+	}
+
+	for (h1=0; h1<=256; h1++) {
+		struct route4_bucket *b;
+
+		if ((b = head->table[h1]) != NULL) {
+			for (h2=0; h2<=32; h2++) {
+				struct route4_filter *f;
+
+				while ((f = b->ht[h2]) != NULL) {
+					unsigned long cl;
+
+					b->ht[h2] = f->next;
+					if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+						tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
+#ifdef CONFIG_NET_CLS_POLICE
+					tcf_police_release(f->police);
+#endif
+					kfree(f);
+				}
+			}
+			kfree(b);
+		}
+	}
+	kfree(head);
+	MOD_DEC_USE_COUNT;
 }
 
-static int route_delete(struct tcf_proto *tp, unsigned long arg)
+static int route4_delete(struct tcf_proto *tp, unsigned long arg)
 {
-	return -EINVAL;
+	struct route4_head *head = (struct route4_head*)tp->root;
+	struct route4_filter **fp, *f = (struct route4_filter*)arg;
+	unsigned h = f->handle;
+	struct route4_bucket *b;
+	int i;
+
+	if (!head || !f)
+		return -EINVAL;
+
+	b = f->bkt;
+
+	for (fp = &b->ht[from_hash(h>>16)]; *fp; fp = &(*fp)->next) {
+		if (*fp == f) {
+			unsigned long cl;
+
+			net_serialize_enter();
+			*fp = f->next;
+			net_serialize_leave();
+			route4_reset_fastmap(head, f->id);
+
+			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
+
+#ifdef CONFIG_NET_CLS_POLICE
+			tcf_police_release(f->police);
+#endif
+			kfree(f);
+
+			/* Strip tree */
+
+			for (i=0; i<=32; i++)
+				if (b->ht[i])
+					return 0;
+
+			/* OK, session has no flows */
+			net_serialize_enter();
+			head->table[to_hash(h)] = NULL;
+			net_serialize_leave();
+			kfree(b);
+			return 0;
+		}
+	}
+	return 0;
 }
 
-static int route_change(struct tcf_proto *tp, u32 handle,
-			struct rtattr **tca,
-			unsigned long *arg)
+static int route4_change(struct tcf_proto *tp, unsigned long base,
+		       u32 handle,
+		       struct rtattr **tca,
+		       unsigned long *arg)
 {
-	return handle ? -EINVAL : 0;
+	struct route4_head *head = tp->root;
+	struct route4_filter *f, *f1, **ins_f;
+	struct route4_bucket *b;
+	struct rtattr *opt = tca[TCA_OPTIONS-1];
+	struct rtattr *tb[TCA_ROUTE4_MAX];
+	unsigned h1, h2;
+	int err;
+
+	if (opt == NULL)
+		return handle ? -EINVAL : 0;
+
+	if (rtattr_parse(tb, TCA_ROUTE4_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt)) < 0)
+		return -EINVAL;
+
+	if ((f = (struct route4_filter*)*arg) != NULL) {
+		/* Node exists: adjust only classid */
+
+		if (f->handle != handle && handle)
+			return -EINVAL;
+		if (tb[TCA_ROUTE4_CLASSID-1]) {
+			unsigned long cl;
+
+			f->res.classid = *(u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID-1]);
+			cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+			if (cl)
+				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
+		}
+#ifdef CONFIG_NET_CLS_POLICE
+		if (tb[TCA_ROUTE4_POLICE-1]) {
+			struct tcf_police *police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
+			net_serialize_enter();
+			police = xchg(&f->police, police);
+			net_serialize_leave();
+			tcf_police_release(police);
+		}
+#endif
+		return 0;
+	}
+
+	/* Now more serious part... */
+
+	if (head == NULL) {
+		head = kmalloc(sizeof(struct route4_head), GFP_KERNEL);
+		if (head == NULL)
+			return -ENOBUFS;
+		memset(head, 0, sizeof(struct route4_head));
+		net_serialize_enter();
+		tp->root = head;
+		net_serialize_leave();
+	}
+
+	f = kmalloc(sizeof(struct route4_filter), GFP_KERNEL);
+	if (f == NULL)
+		return -ENOBUFS;
+
+	memset(f, 0, sizeof(*f));
+
+	err = -EINVAL;
+	f->handle = 0x8000;
+	if (tb[TCA_ROUTE4_TO-1]) {
+		if (handle&0x8000)
+			goto errout;
+		if (RTA_PAYLOAD(tb[TCA_ROUTE4_TO-1]) < 4)
+			goto errout;
+		f->id = *(u32*)RTA_DATA(tb[TCA_ROUTE4_TO-1]);
+		if (f->id > 0xFF)
+			goto errout;
+		f->handle = f->id;
+	}
+	if (tb[TCA_ROUTE4_FROM-1]) {
+		u32 sid;
+		if (tb[TCA_ROUTE4_IIF-1])
+			goto errout;
+		if (RTA_PAYLOAD(tb[TCA_ROUTE4_FROM-1]) < 4)
+			goto errout;
+		sid = (*(u32*)RTA_DATA(tb[TCA_ROUTE4_FROM-1]));
+		if (sid > 0xFF)
+			goto errout;
+		f->handle |= sid<<16;
+		f->id |= sid<<16;
+	} else if (tb[TCA_ROUTE4_IIF-1]) {
+		if (RTA_PAYLOAD(tb[TCA_ROUTE4_IIF-1]) < 4)
+			goto errout;
+		f->iif = *(u32*)RTA_DATA(tb[TCA_ROUTE4_IIF-1]);
+		if (f->iif > 0x7FFF)
+			goto errout;
+		f->handle |= (f->iif|0x8000)<<16;
+	} else
+		f->handle |= 0xFFFF<<16;
+
+	if (handle) {
+		f->handle |= handle&0x7F00;
+		if (f->handle != handle)
+			goto errout;
+	}
+
+	if (tb[TCA_ROUTE4_CLASSID-1]) {
+		if (RTA_PAYLOAD(tb[TCA_ROUTE4_CLASSID-1]) < 4)
+			goto errout;
+		f->res.classid = *(u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID-1]);
+	}
+
+	h1 = to_hash(f->handle);
+	if ((b = head->table[h1]) == NULL) {
+		err = -ENOBUFS;
+		b = kmalloc(sizeof(struct route4_bucket), GFP_KERNEL);
+		if (b == NULL)
+			goto errout;
+		memset(b, 0, sizeof(*b));
+		net_serialize_enter();
+		head->table[h1] = b;
+		net_serialize_leave();
+	}
+	f->bkt = b;
+
+	err = -EEXIST;
+	h2 = from_hash(f->handle>>16);
+	for (ins_f = &b->ht[h2]; (f1=*ins_f) != NULL; ins_f = &f1->next) {
+		if (f->handle < f1->handle)
+			break;
+		if (f1->handle == f->handle)
+			goto errout;
+	}
+
+	cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+#ifdef CONFIG_NET_CLS_POLICE
+	if (tb[TCA_ROUTE4_POLICE-1])
+		f->police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
+#endif
+
+	f->next = f1;
+	net_serialize_enter();
+	*ins_f = f;
+	net_serialize_leave();
+	route4_reset_fastmap(head, f->id);
+	*arg = (unsigned long)f;
+	return 0;
+
+errout:
+	if (f)
+		kfree(f);
+	return err;
 }
 
-struct tcf_proto_ops cls_route_ops = {
+static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+{
+	struct route4_head *head = tp->root;
+	unsigned h, h1;
+
+	if (head == NULL)
+		arg->stop = 1;
+
+	if (arg->stop)
+		return;
+
+	for (h = 0; h <= 256; h++) {
+		struct route4_bucket *b = head->table[h];
+
+		if (b) {
+			for (h1 = 0; h1 <= 32; h1++) {
+				struct route4_filter *f;
+
+				for (f = b->ht[h1]; f; f = f->next) {
+					if (arg->count < arg->skip) {
+						arg->count++;
+						continue;
+					}
+					if (arg->fn(tp, (unsigned long)f, arg) < 0) {
+						arg->stop = 1;
+						break;
+					}
+					arg->count++;
+				}
+			}
+		}
+	}
+}
+
+#ifdef CONFIG_RTNETLINK
+static int route4_dump(struct tcf_proto *tp, unsigned long fh,
+		       struct sk_buff *skb, struct tcmsg *t)
+{
+	struct route4_filter *f = (struct route4_filter*)fh;
+	unsigned char	 *b = skb->tail;
+	struct rtattr *rta;
+	u32 id;
+
+	if (f == NULL)
+		return skb->len;
+
+	t->tcm_handle = f->handle;
+
+	rta = (struct rtattr*)b;
+	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+
+	if (!(f->handle&0x8000)) {
+		id = f->id&0xFF;
+		RTA_PUT(skb, TCA_ROUTE4_TO, sizeof(id), &id);
+	}
+	if (f->handle&0x80000000) {
+		if ((f->handle>>16) != 0xFFFF)
+			RTA_PUT(skb, TCA_ROUTE4_IIF, sizeof(f->iif), &f->iif);
+	} else {
+		id = f->id>>16;
+		RTA_PUT(skb, TCA_ROUTE4_FROM, sizeof(id), &id);
+	}
+	if (f->res.classid)
+		RTA_PUT(skb, TCA_ROUTE4_CLASSID, 4, &f->res.classid);
+#ifdef CONFIG_NET_CLS_POLICE
+	if (f->police) {
+		struct rtattr * p_rta = (struct rtattr*)skb->tail;
+
+		RTA_PUT(skb, TCA_ROUTE4_POLICE, 0, NULL);
+
+		if (tcf_police_dump(skb, f->police) < 0)
+			goto rtattr_failure;
+
+		p_rta->rta_len = skb->tail - (u8*)p_rta;
+	}
+#endif
+
+	rta->rta_len = skb->tail - b;
+#ifdef CONFIG_NET_CLS_POLICE
+	if (f->police) {
+		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+	}
+#endif
+	return skb->len;
+
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+#endif
+
+struct tcf_proto_ops cls_route4_ops = {
 	NULL,
 	"route",
-	route_classify,
-	route_init,
-	route_destroy,
-
-	route_get,
-	route_put,
-	route_change,
-	route_delete,
-	NULL,
+	route4_classify,
+	route4_init,
+	route4_destroy,
+
+	route4_get,
+	route4_put,
+	route4_change,
+	route4_delete,
+	route4_walk,
+#ifdef CONFIG_RTNETLINK
+	route4_dump
+#else
+	NULL
+#endif
 };
+
+#ifdef MODULE
+int init_module(void)
+{
+	return register_tcf_proto_ops(&cls_route4_ops);
+}
+
+void cleanup_module(void)
+{
+	unregister_tcf_proto_ops(&cls_route4_ops);
+}
+#endif
diff -u --recursive --new-file v2.2.3/linux/net/sched/cls_rsvp.h linux/net/sched/cls_rsvp.h
--- v2.2.3/linux/net/sched/cls_rsvp.h	Sat Sep  5 16:46:42 1998
+++ linux/net/sched/cls_rsvp.h	Sun Mar 21 07:22:00 1999
@@ -120,6 +120,18 @@
 	return h & 0xF;
 }
 
+#ifdef CONFIG_NET_CLS_POLICE
+#define RSVP_POLICE() \
+if (f->police) { \
+	int pol_res = tcf_police(skb, f->police); \
+	if (pol_res < 0) continue; \
+	if (pol_res) return pol_res; \
+}
+#else
+#define RSVP_POLICE()
+#endif
+
+
 static int rsvp_classify(struct sk_buff *skb, struct tcf_proto *tp,
 			 struct tcf_result *res)
 {
@@ -137,7 +149,7 @@
 	struct iphdr *nhptr = skb->nh.iph;
 #endif
 
-#ifndef __i386__
+#if !defined( __i386__) && !defined(__m68k__)
 	if ((unsigned long)nhptr & 3)
 		return -1;
 #endif
@@ -181,13 +193,12 @@
 				    && src[2] == f->src[2]
 #endif
 				    ) {
+
+					RSVP_POLICE();
+
 matched:
 					if (f->tunnelhdr == 0) {
 						*res = f->res;
-#ifdef CONFIG_NET_CLS_POLICE
-						if (f->police)
-							return tcf_police(skb, f->police);
-#endif
 						return 0;
 					} else {
 						tunnelid = f->res.classid;
@@ -198,8 +209,10 @@
 			}
 
 			/* And wildcard bucket... */
-			if ((f = s->ht[16]) != NULL)
+			for (f = s->ht[16]; f; f = f->next) {
+				RSVP_POLICE();
 				goto matched;
+			}
 			return -1;
 		}
 	}
@@ -260,7 +273,6 @@
 		struct rsvp_session *s;
 
 		while ((s = sht[h1]) != NULL) {
-
 			sht[h1] = s->next;
 
 			for (h2=0; h2<=16; h2++) {
@@ -270,7 +282,7 @@
 					unsigned long cl;
 
 					s->ht[h2] = f->next;
-					if ((cl = xchg(&f->res.class, 0)) != 0)
+					if ((cl = cls_set_class(&f->res.class, 0)) != 0)
 						tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 					tcf_police_release(f->police);
@@ -297,8 +309,11 @@
 		if (*fp == f) {
 			unsigned long cl;
 
+			net_serialize_enter();
 			*fp = f->next;
-			if ((cl = xchg(&f->res.class, 0)) != 0)
+			net_serialize_leave();
+
+			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -317,12 +332,14 @@
 			for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF];
 			     *sp; sp = &(*sp)->next) {
 				if (*sp == s) {
+					net_serialize_enter();
 					*sp = s->next;
+					net_serialize_leave();
 					kfree(s);
 					return 0;
 				}
 			}
-			
+
 			return 0;
 		}
 	}
@@ -399,7 +416,8 @@
 	return 0;
 }
 
-static int rsvp_change(struct tcf_proto *tp, u32 handle,
+static int rsvp_change(struct tcf_proto *tp, unsigned long base,
+		       u32 handle,
 		       struct rtattr **tca,
 		       unsigned long *arg)
 {
@@ -425,17 +443,20 @@
 		if (f->handle != handle && handle)
 			return -EINVAL;
 		if (tb[TCA_RSVP_CLASSID-1]) {
-			unsigned long cl = xchg(&f->res.class, 0);
+			unsigned long cl;
+
+			f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]);
+			cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 			if (cl)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
-			f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]);
-			f->res.class = tp->q->ops->cl_ops->bind_tcf(tp->q, f->res.classid);
 		}
 #ifdef CONFIG_NET_CLS_POLICE
 		if (tb[TCA_RSVP_POLICE-1]) {
-			struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1]);
-
-			tcf_police_release(xchg(&f->police, police));
+			struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
+			net_serialize_enter();
+			police = xchg(&f->police, police);
+			net_serialize_leave();
+			tcf_police_release(police);
 		}
 #endif
 		return 0;
@@ -514,17 +535,19 @@
 
 			f->sess = s;
 			if (f->tunnelhdr == 0)
-				f->res.class = tp->q->ops->cl_ops->bind_tcf(tp->q, f->res.classid);
+				cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 #ifdef CONFIG_NET_CLS_POLICE
 			if (tb[TCA_RSVP_POLICE-1])
-				f->police = tcf_police_locate(tb[TCA_RSVP_POLICE-1]);
+				f->police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
 #endif
 
 			for (fp = &s->ht[h2]; *fp; fp = &(*fp)->next)
 				if (((*fp)->spi.mask&f->spi.mask) != f->spi.mask)
 					break;
 			f->next = *fp;
+			net_serialize_enter();
 			*fp = f;
+			net_serialize_leave();
 			*arg = (unsigned long)f;
 			return 0;
 		}
@@ -546,7 +569,10 @@
 			break;
 	}
 	s->next = *sp;
+	net_serialize_enter();
 	*sp = s;
+	net_serialize_leave();
+	
 	goto insert;
 
 errout:
@@ -631,6 +657,11 @@
 #endif
 
 	rta->rta_len = skb->tail - b;
+#ifdef CONFIG_NET_CLS_POLICE
+	if (f->police) {
+		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+	}
+#endif
 	return skb->len;
 
 rtattr_failure:
diff -u --recursive --new-file v2.2.3/linux/net/sched/cls_u32.c linux/net/sched/cls_u32.c
--- v2.2.3/linux/net/sched/cls_u32.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/cls_u32.c	Sun Mar 21 07:22:00 1999
@@ -114,7 +114,7 @@
 	int sel = 0;
 	int i;
 
-#ifndef __i386__
+#if !defined(__i386__) && !defined(__m68k__)
 	if ((unsigned long)ptr & 3)
 		return -1;
 #endif
@@ -137,10 +137,13 @@
 			if (n->sel.flags&TC_U32_TERMINAL) {
 				*res = n->res;
 #ifdef CONFIG_NET_CLS_POLICE
-				if (n->police)
-					return tcf_police(skb, n->police);
+				if (n->police) {
+					int pol_res = tcf_police(skb, n->police);
+					if (pol_res >= 0)
+						return pol_res;
+				} else
 #endif
-				return 0;
+					return 0;
 			}
 			n = n->next;
 			goto next_knode;
@@ -304,7 +307,7 @@
 {
 	unsigned long cl;
 
-	if ((cl = xchg(&n->res.class, 0)) != 0)
+	if ((cl = cls_set_class(&n->res.class, 0)) != 0)
 		tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 	tcf_police_release(n->police);
@@ -323,7 +326,10 @@
 	if (ht) {
 		for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) {
 			if (*kp == key) {
+				net_serialize_enter();
 				*kp = key->next;
+				net_serialize_leave();
+
 				u32_destroy_key(tp, key);
 				return 0;
 			}
@@ -340,7 +346,9 @@
 
 	for (h=0; h<=ht->divisor; h++) {
 		while ((n = ht->ht[h]) != NULL) {
+			net_serialize_enter();
 			ht->ht[h] = n->next;
+			net_serialize_leave();
 			u32_destroy_key(tp, n);
 		}
 	}
@@ -402,6 +410,7 @@
 		kfree(tp_c);
 	}
 
+	MOD_DEC_USE_COUNT;
 	tp->data = NULL;
 }
 
@@ -437,8 +446,10 @@
 	return handle|(i>0xFFF ? 0xFFF : i);
 }
 
-static int u32_set_parms(struct Qdisc *q, struct tc_u_hnode *ht,
-			 struct tc_u_knode *n, struct rtattr **tb)
+static int u32_set_parms(struct Qdisc *q, unsigned long base,
+			 struct tc_u_hnode *ht,
+			 struct tc_u_knode *n, struct rtattr **tb,
+			 struct rtattr *est)
 {
 	if (tb[TCA_U32_LINK-1]) {
 		u32 handle = *(u32*)RTA_DATA(tb[TCA_U32_LINK-1]);
@@ -455,29 +466,34 @@
 			ht_down->refcnt++;
 		}
 
+		net_serialize_enter();
 		ht_down = xchg(&n->ht_down, ht_down);
+		net_serialize_leave();
 
 		if (ht_down)
 			ht_down->refcnt--;
 	}
 	if (tb[TCA_U32_CLASSID-1]) {
-		unsigned long cl = xchg(&n->res.class, 0);
+		unsigned long cl;
+
+		n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]);
+		cl = cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid));
 		if (cl)
 			q->ops->cl_ops->unbind_tcf(q, cl);
-		n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]);
-		n->res.class = q->ops->cl_ops->bind_tcf(q, n->res.classid);
 	}
 #ifdef CONFIG_NET_CLS_POLICE
 	if (tb[TCA_U32_POLICE-1]) {
-		struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1]);
-
-		tcf_police_release(xchg(&n->police, police));
+		struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1], est);
+		net_serialize_enter();
+		police = xchg(&n->police, police);
+		net_serialize_leave();
+		tcf_police_release(police);
 	}
 #endif
 	return 0;
 }
 
-static int u32_change(struct tcf_proto *tp, u32 handle,
+static int u32_change(struct tcf_proto *tp, unsigned long base, u32 handle,
 		      struct rtattr **tca,
 		      unsigned long *arg)
 {
@@ -500,7 +516,7 @@
 		if (TC_U32_KEY(n->handle) == 0)
 			return -EINVAL;
 
-		return u32_set_parms(tp->q, n->ht_up, n, tb);
+		return u32_set_parms(tp->q, base, n->ht_up, n, tb, tca[TCA_RATE-1]);
 	}
 
 	if (tb[TCA_U32_DIVISOR-1]) {
@@ -531,7 +547,7 @@
 
 	if (tb[TCA_U32_HASH-1]) {
 		htid = *(unsigned*)RTA_DATA(tb[TCA_U32_HASH-1]);
-		if (TC_U32_HTID(handle) == TC_U32_ROOT) {
+		if (TC_U32_HTID(htid) == TC_U32_ROOT) {
 			ht = tp->root;
 			htid = ht->handle;
 		} else {
@@ -550,8 +566,6 @@
 	if (handle) {
 		if (TC_U32_HTID(handle) && TC_U32_HTID(handle^htid))
 			return -EINVAL;
-		if (TC_U32_HASH(handle) && TC_U32_HASH(handle^htid))
-			return -EINVAL;
 		handle = htid | TC_U32_NODE(handle);
 	} else
 		handle = gen_new_kid(ht, htid);
@@ -568,14 +582,16 @@
 	memcpy(&n->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key));
 	n->ht_up = ht;
 	n->handle = handle;
-	err = u32_set_parms(tp->q, ht, n, tb);
+	err = u32_set_parms(tp->q, base, ht, n, tb, tca[TCA_RATE-1]);
 	if (err == 0) {
 		struct tc_u_knode **ins;
 		for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next)
-			if (TC_U32_NODE(handle) >= TC_U32_NODE((*ins)->handle))
+			if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle))
 				break;
+		net_serialize_enter();
 		n->next = *ins;
 		*ins = n;
+		net_serialize_leave();
 		*arg = (unsigned long)n;
 		return 0;
 	}
@@ -664,6 +680,11 @@
 	}
 
 	rta->rta_len = skb->tail - b;
+#ifdef CONFIG_NET_CLS_POLICE
+	if (TC_U32_KEY(n->handle) && n->police) {
+		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &n->police->stats);
+	}
+#endif
 	return skb->len;
 
 rtattr_failure:
diff -u --recursive --new-file v2.2.3/linux/net/sched/estimator.c linux/net/sched/estimator.c
--- v2.2.3/linux/net/sched/estimator.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/estimator.c	Sun Mar 21 07:22:00 1999
@@ -171,8 +171,9 @@
 				pest = &est->next;
 				continue;
 			}
-			/* ATOMIC_SET */
+			net_serialize_enter();
 			*pest = est->next;
+			net_serialize_leave();
 			kfree(est);
 			killed++;
 		}
diff -u --recursive --new-file v2.2.3/linux/net/sched/police.c linux/net/sched/police.c
--- v2.2.3/linux/net/sched/police.c	Sat May  2 14:19:55 1998
+++ linux/net/sched/police.c	Sun Mar 21 07:22:00 1999
@@ -74,6 +74,9 @@
 	for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->next) {
 		if (*p1p == p) {
 			*p1p = p->next;
+#ifdef CONFIG_NET_ESTIMATOR
+			qdisc_kill_estimator(&p->stats);
+#endif
 			if (p->R_tab)
 				qdisc_put_rtab(p->R_tab);
 			if (p->P_tab)
@@ -85,7 +88,7 @@
 	BUG_TRAP(0);
 }
 
-struct tcf_police * tcf_police_locate(struct rtattr *rta)
+struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est)
 {
 	unsigned h;
 	struct tcf_police *p;
@@ -111,20 +114,35 @@
 
 	memset(p, 0, sizeof(*p));
 	p->refcnt = 1;
-	if ((p->R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1])) == NULL)
-		goto failure;
-	if (parm->peakrate.rate &&
-	    (p->P_tab = qdisc_get_rtab(&parm->peakrate, tb[TCA_POLICE_PEAKRATE-1])) == NULL)
-		goto failure;
+	if (parm->rate.rate) {
+		if ((p->R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1])) == NULL)
+			goto failure;
+		if (parm->peakrate.rate &&
+		    (p->P_tab = qdisc_get_rtab(&parm->peakrate, tb[TCA_POLICE_PEAKRATE-1])) == NULL)
+			goto failure;
+	}
+	if (tb[TCA_POLICE_RESULT-1])
+		p->result = *(int*)RTA_DATA(tb[TCA_POLICE_RESULT-1]);
+#ifdef CONFIG_NET_ESTIMATOR
+	if (tb[TCA_POLICE_AVRATE-1])
+		p->ewma_rate = *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]);
+#endif
 	p->toks = p->burst = parm->burst;
 	p->mtu = parm->mtu;
-	if (p->mtu == 0)
-		p->mtu = 255<<p->R_tab->rate.cell_log;
+	if (p->mtu == 0) {
+		p->mtu = ~0;
+		if (p->R_tab)
+			p->mtu = 255<<p->R_tab->rate.cell_log;
+	}
 	if (p->P_tab)
 		p->ptoks = L2T_P(p, p->mtu);
 	PSCHED_GET_TIME(p->t_c);
 	p->index = parm->index ? : tcf_police_new_index();
 	p->action = parm->action;
+#ifdef CONFIG_NET_ESTIMATOR
+	if (est)
+		qdisc_new_estimator(&p->stats, est);
+#endif
 	h = tcf_police_hash(p->index);
 	p->next = tcf_police_ht[h];
 	tcf_police_ht[h] = p;
@@ -143,7 +161,20 @@
 	long toks;
 	long ptoks = 0;
 
+	p->stats.bytes += skb->len;
+	p->stats.packets++;
+
+#ifdef CONFIG_NET_ESTIMATOR
+	if (p->ewma_rate && p->stats.bps >= p->ewma_rate) {
+		p->stats.overlimits++;
+		return p->action;
+	}
+#endif
+
 	if (skb->len <= p->mtu) {
+		if (p->R_tab == NULL)
+			return p->result;
+
 		PSCHED_GET_TIME(now);
 
 		toks = PSCHED_TDIFF_SAFE(now, p->t_c, p->burst, 0);
@@ -163,10 +194,11 @@
 			p->t_c = now;
 			p->toks = toks;
 			p->ptoks = ptoks;
-			return TC_POLICE_OK;
+			return p->result;
 		}
 	}
 
+	p->stats.overlimits++;
 	return p->action;
 }
 
@@ -180,12 +212,21 @@
 	opt.action = p->action;
 	opt.mtu = p->mtu;
 	opt.burst = p->burst;
-	opt.rate = p->R_tab->rate;
+	if (p->R_tab)
+		opt.rate = p->R_tab->rate;
+	else
+		memset(&opt.rate, 0, sizeof(opt.rate));
 	if (p->P_tab)
 		opt.peakrate = p->P_tab->rate;
 	else
 		memset(&opt.peakrate, 0, sizeof(opt.peakrate));
 	RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt);
+	if (p->result)
+		RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), &p->result);
+#ifdef CONFIG_NET_ESTIMATOR
+	if (p->ewma_rate)
+		RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &p->ewma_rate);
+#endif
 	return skb->len;
 
 rtattr_failure:
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_api.c linux/net/sched/sch_api.c
--- v2.2.3/linux/net/sched/sch_api.c	Fri Jan  8 22:36:27 1999
+++ linux/net/sched/sch_api.c	Sun Mar 21 07:22:00 1999
@@ -11,6 +11,7 @@
  * Fixes:
  *
  * Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired.
+ * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
  */
 
 #include <linux/config.h>
@@ -29,6 +30,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <linux/kmod.h>
 
 #include <net/sock.h>
 #include <net/pkt_sched.h>
@@ -41,7 +43,7 @@
 #define BUG_TRAP(x) if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); }
 
 #ifdef CONFIG_RTNETLINK
-static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n,
+static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n, u32 clid,
 			struct Qdisc *old, struct Qdisc *new);
 static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,
 			 struct Qdisc *q, unsigned long cl, int event);
@@ -116,6 +118,10 @@
    ---destroy
 
    destroys resources allocated by init and during lifetime of qdisc.
+
+   ---change
+
+   changes qdisc parameters.
  */
 
 /************************************************
@@ -177,22 +183,22 @@
 	return NULL;
 }
 
-/* We know classid. Find qdisc among all qdisc's attached to device
-   (root qdisc, all its children, children of children etc.)
- */
-
-struct Qdisc *qdisc_lookup_class(struct device *dev, u32 classid)
+struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
 {
-	struct Qdisc *q;
+	unsigned long cl;
+	struct Qdisc *leaf;
+	struct Qdisc_class_ops *cops = p->ops->cl_ops;
 
-	for (q = dev->qdisc_list; q; q = q->next) {
-		if (q->classid == classid)
-			return q;
-	}
-	return NULL;
+	if (cops == NULL)
+		return NULL;
+	cl = cops->get(p, classid);
+	if (cl == 0)
+		return NULL;
+	leaf = cops->leaf(p, cl);
+	cops->put(p, cl);
+	return leaf;
 }
 
-
 /* Find queueing discipline by name */
 
 struct Qdisc_ops *qdisc_lookup_ops(struct rtattr *kind)
@@ -268,6 +274,37 @@
 	return i>0 ? autohandle : 0;
 }
 
+/* Attach toplevel qdisc to device dev */
+
+static struct Qdisc *
+dev_graft_qdisc(struct device *dev, struct Qdisc *qdisc)
+{
+	struct Qdisc *oqdisc;
+
+	if (dev->flags & IFF_UP)
+		dev_deactivate(dev);
+
+	start_bh_atomic();
+	oqdisc = dev->qdisc_sleeping;
+
+	/* Prune old scheduler */
+	if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1)
+		qdisc_reset(oqdisc);
+
+	/* ... and graft new one */
+	if (qdisc == NULL)
+		qdisc = &noop_qdisc;
+	dev->qdisc_sleeping = qdisc;
+	dev->qdisc = &noop_qdisc;
+	end_bh_atomic();
+
+	if (dev->flags & IFF_UP)
+		dev_activate(dev);
+
+	return oqdisc;
+}
+
+
 /* Graft qdisc "new" to class "classid" of qdisc "parent" or
    to device "dev".
 
@@ -280,17 +317,10 @@
 	int err = 0;
 
 	if (parent == NULL) {
-		BUG_TRAP(classid == TC_H_ROOT);
-		if (new) {
-			new->parent = NULL;
-			new->classid = TC_H_ROOT;
-		}
-		*old = dev_set_scheduler(dev, new);
+		*old = dev_graft_qdisc(dev, new);
 	} else {
 		struct Qdisc_class_ops *cops = parent->ops->cl_ops;
 
-		BUG_TRAP(classid != TC_H_ROOT);
-
 		err = -EINVAL;
 
 		if (cops) {
@@ -313,22 +343,30 @@
  */
 
 static struct Qdisc *
-qdisc_create(struct device *dev, struct Qdisc_ops *ops, u32 handle,
-	     u32 parentid, struct rtattr **tca, int *errp)
+qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp)
 {
 	int err;
 	struct rtattr *kind = tca[TCA_KIND-1];
 	struct Qdisc *sch = NULL;
+	struct Qdisc_ops *ops;
 	int size;
-	int new = 0;
 
-	if (ops == NULL) {
-		ops = qdisc_lookup_ops(kind);
-		err = -EINVAL;
-		if (ops == NULL)
-			goto err_out;
-		new = 1;
+	ops = qdisc_lookup_ops(kind);
+#ifdef CONFIG_KMOD
+	if (ops==NULL && tca[TCA_KIND-1] != NULL) {
+		char module_name[4 + IFNAMSIZ + 1];
+
+		if (RTA_PAYLOAD(kind) <= IFNAMSIZ) {
+			sprintf(module_name, "sch_%s", (char*)RTA_DATA(kind));
+			request_module (module_name);
+			ops = qdisc_lookup_ops(kind);
+		}
 	}
+#endif
+
+	err = -EINVAL;
+	if (ops == NULL)
+		goto err_out;
 
 	size = sizeof(*sch) + ops->priv_size;
 
@@ -340,13 +378,8 @@
 	/* Grrr... Resolve race condition with module unload */
 	
 	err = -EINVAL;
-	if (new) {
-		if (ops != qdisc_lookup_ops(kind))
-			goto err_out;
-	} else if (kind) {
-		if (rtattr_strcmp(kind, ops->id))
-			goto err_out;
-	}
+	if (ops != qdisc_lookup_ops(kind))
+		goto err_out;
 
 	memset(sch, 0, size);
 
@@ -355,6 +388,7 @@
 	sch->enqueue = ops->enqueue;
 	sch->dequeue = ops->dequeue;
 	sch->dev = dev;
+	atomic_set(&sch->refcnt, 1);
 	if (handle == 0) {
 		handle = qdisc_alloc_handle(dev);
 		err = -ENOMEM;
@@ -362,9 +396,8 @@
 			goto err_out;
 	}
 	sch->handle = handle;
-	sch->classid = parentid;
 
-	if (ops->init && (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) {
+	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) {
 		sch->next = dev->qdisc_list;
 		dev->qdisc_list = sch;
 #ifdef CONFIG_NET_ESTIMATOR
@@ -381,135 +414,241 @@
 	return NULL;
 }
 
+static int qdisc_change(struct Qdisc *sch, struct rtattr **tca)
+{
+	if (tca[TCA_OPTIONS-1]) {
+		int err;
+
+		if (sch->ops->change == NULL)
+			return -EINVAL;
+		err = sch->ops->change(sch, tca[TCA_OPTIONS-1]);
+		if (err)
+			return err;
+	}
+#ifdef CONFIG_NET_ESTIMATOR
+	if (tca[TCA_RATE-1]) {
+		qdisc_kill_estimator(&sch->stats);
+		qdisc_new_estimator(&sch->stats, tca[TCA_RATE-1]);
+	}
+#endif
+	return 0;
+}
+
+struct check_loop_arg
+{
+	struct qdisc_walker 	w;
+	struct Qdisc		*p;
+	int			depth;
+};
+
+static int check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w);
+
+static int check_loop(struct Qdisc *q, struct Qdisc *p, int depth)
+{
+	struct check_loop_arg	arg;
+
+	if (q->ops->cl_ops == NULL)
+		return 0;
+
+	arg.w.stop = arg.w.skip = arg.w.count = 0;
+	arg.w.fn = check_loop_fn;
+	arg.depth = depth;
+	arg.p = p;
+	q->ops->cl_ops->walk(q, &arg.w);
+	return arg.w.stop ? -ELOOP : 0;
+}
+
+static int
+check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w)
+{
+	struct Qdisc *leaf;
+	struct Qdisc_class_ops *cops = q->ops->cl_ops;
+	struct check_loop_arg *arg = (struct check_loop_arg *)w;
+
+	leaf = cops->leaf(q, cl);
+	if (leaf) {
+		if (leaf == arg->p || arg->depth > 7)
+			return -ELOOP;
+		return check_loop(leaf, arg->p, arg->depth + 1);
+	}
+	return 0;
+}
 
 /*
-   Create/delete/change/get qdisc.
+ * Delete/get qdisc.
  */
 
-static int tc_ctl_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 {
 	struct tcmsg *tcm = NLMSG_DATA(n);
 	struct rtattr **tca = arg;
 	struct device *dev;
 	u32 clid = tcm->tcm_parent;
-	struct Qdisc *old_q;
 	struct Qdisc *q = NULL;
 	struct Qdisc *p = NULL;
-	struct Qdisc *leaf = NULL;
-	struct Qdisc_ops *qops = NULL;
 	int err;
 
-	/* Find device */
 	if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)
 		return -ENODEV;
 
-	/* If parent is specified, it must exist
-	   and tcm_parent selects a class in parent which
-	   new qdisc will be attached to.
-
-	   The place may be already busy by another qdisc,
-	   remember this fact, if it was not auto-created discipline.
-	 */
 	if (clid) {
 		if (clid != TC_H_ROOT) {
-			p = qdisc_lookup(dev, TC_H_MAJ(clid));
-			if (p == NULL)
+			if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
 				return -ENOENT;
-			leaf = qdisc_lookup_class(dev, clid);
+			q = qdisc_leaf(p, clid);
 		} else
-			leaf = dev->qdisc_sleeping;
-
-		if (leaf && leaf->flags&TCQ_F_DEFAULT && n->nlmsg_type == RTM_NEWQDISC)
-			leaf = NULL;
-
-		/*
-		   Also, leaf may be exactly that qdisc, which we want
-		   to control. Remember this to avoid one more qdisc_lookup.
-		 */
+			q = dev->qdisc_sleeping;
 
-		if (leaf && leaf->handle == tcm->tcm_handle)
-			q = leaf;
-	}
+		if (!q)
+			return -ENOENT;
 
-	/* Try to locate the discipline */
-	if (tcm->tcm_handle && q == NULL) {
-		if (TC_H_MIN(tcm->tcm_handle))
+		if (tcm->tcm_handle && q->handle != tcm->tcm_handle)
 			return -EINVAL;
-		q = qdisc_lookup(dev, tcm->tcm_handle);
+	} else {
+		if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)
+			return -ENOENT;
 	}
 
-	/* If discipline already exists, check that its real parent
-	   matches to one selected by tcm_parent.
-	 */
-	   
-	if (q) {
-		if (clid && p != q->parent)
-			return -EINVAL;
-		BUG_TRAP(!leaf || leaf == q);
-		if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
+	if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
+		return -EINVAL;
+
+	if (n->nlmsg_type == RTM_DELQDISC) {
+		if (!clid)
 			return -EINVAL;
-		clid = q->classid;
-		goto process_existing;
+		if (q->handle == 0)
+			return -ENOENT;
+		if ((err = qdisc_graft(dev, p, clid, NULL, &q)) != 0)
+			return err;
+		if (q) {
+			qdisc_notify(skb, n, clid, q, NULL);
+			qdisc_destroy(q);
+		}
+	} else {
+		qdisc_notify(skb, n, clid, NULL, q);
 	}
+	return 0;
+}
 
-	/* The discipline is known not to exist.
-	   If parent was not selected too, return error.
-	 */
-	if (clid == 0)
-		return tcm->tcm_handle ? -ENOENT : -EINVAL;
+/*
+   Create/change qdisc.
+ */
 
-	/* Check for the case when leaf is exactly the thing,
-	   that you want.
-	 */
+static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+{
+	struct tcmsg *tcm = NLMSG_DATA(n);
+	struct rtattr **tca = arg;
+	struct device *dev;
+	u32 clid = tcm->tcm_parent;
+	struct Qdisc *q = NULL;
+	struct Qdisc *p = NULL;
+	int err;
 
-	if (leaf && tcm->tcm_handle == 0) {
-		q = leaf;
-		if (!tca[TCA_KIND-1] || rtattr_strcmp(tca[TCA_KIND-1], q->ops->id) == 0)
-			goto process_existing;
+	if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)
+		return -ENODEV;
+
+	if (clid) {
+		if (clid != TC_H_ROOT) {
+			if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
+				return -ENOENT;
+			q = qdisc_leaf(p, clid);
+		} else {
+			q = dev->qdisc_sleeping;
+		}
+
+		/* It may be default qdisc, ignore it */
+		if (q && q->handle == 0)
+			q = NULL;
+
+		if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
+			if (tcm->tcm_handle) {
+				if (q && !(n->nlmsg_flags&NLM_F_REPLACE))
+					return -EEXIST;
+				if (TC_H_MIN(tcm->tcm_handle))
+					return -EINVAL;
+				if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)
+					goto create_n_graft;
+				if (n->nlmsg_flags&NLM_F_EXCL)
+					return -EEXIST;
+				if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
+					return -EINVAL;
+				if (q == p ||
+				    (p && check_loop(q, p, 0)))
+					return -ELOOP;
+				atomic_inc(&q->refcnt);
+				goto graft;
+			} else {
+				if (q == NULL)
+					goto create_n_graft;
+
+				/* This magic test requires explanation.
+				 *
+				 *   We know, that some child q is already
+				 *   attached to this parent and have choice:
+				 *   either to change it or to create/graft new one.
+				 *
+				 *   1. We are allowed to create/graft only
+				 *   if CREATE and REPLACE flags are set.
+				 *
+				 *   2. If EXCL is set, requestor wanted to say,
+				 *   that qdisc tcm_handle is not expected
+				 *   to exist, so that we choose create/graft too.
+				 *
+				 *   3. The last case is when no flags are set.
+				 *   Alas, it is sort of hole in API, we
+				 *   cannot decide what to do unambiguously.
+				 *   For now we select create/graft, if
+				 *   user gave KIND, which does not match existing.
+				 */
+				if ((n->nlmsg_flags&NLM_F_CREATE) &&
+				    (n->nlmsg_flags&NLM_F_REPLACE) &&
+				    ((n->nlmsg_flags&NLM_F_EXCL) ||
+				     (tca[TCA_KIND-1] &&
+				      rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))))
+					goto create_n_graft;
+			}
+		}
+	} else {
+		if (!tcm->tcm_handle)
+			return -EINVAL;
+		q = qdisc_lookup(dev, tcm->tcm_handle);
 	}
 
-	if (n->nlmsg_type != RTM_NEWQDISC || !(n->nlmsg_flags&NLM_F_CREATE))
+	/* Change qdisc parameters */
+	if (q == NULL)
 		return -ENOENT;
-	if (leaf && n->nlmsg_flags&NLM_F_EXCL)
+	if (n->nlmsg_flags&NLM_F_EXCL)
 		return -EEXIST;
+	if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
+		return -EINVAL;
+	err = qdisc_change(q, tca);
+	if (err == 0)
+		qdisc_notify(skb, n, clid, NULL, q);
+	return err;
 
-create_and_graft:
-	q = qdisc_create(dev, qops, tcm->tcm_handle, clid, tca, &err);
+create_n_graft:
+	if (!(n->nlmsg_flags&NLM_F_CREATE))
+		return -ENOENT;
+	q = qdisc_create(dev, tcm->tcm_handle, tca, &err);
 	if (q == NULL)
 		return err;
 
 graft:
-	err = qdisc_graft(dev, p, clid, q, &old_q);
-	if (err) {
-		if (q)
-			qdisc_destroy(q);
-		return err;
+	if (1) {
+		struct Qdisc *old_q = NULL;
+		err = qdisc_graft(dev, p, clid, q, &old_q);
+		if (err) {
+			if (q)
+				qdisc_destroy(q);
+			return err;
+		}
+		qdisc_notify(skb, n, clid, old_q, q);
+		if (old_q)
+			qdisc_destroy(old_q);
 	}
-	qdisc_notify(skb, n, old_q, q);
-	if (old_q)
-		qdisc_destroy(old_q);
 	return 0;
-
-process_existing:
-
-	switch (n->nlmsg_type) {
-	case RTM_NEWQDISC:
-		if (n->nlmsg_flags&NLM_F_EXCL)
-			return -EEXIST;
-		qops = q->ops;
-		goto create_and_graft;
-	case RTM_GETQDISC:	
-		qdisc_notify(skb, n, NULL, q);
-		return 0;
-	case RTM_DELQDISC:
-		q = NULL;
-		goto graft;
-	default:
-		return -EINVAL;
-	}
 }
 
-static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q,
+static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
 			 u32 pid, u32 seq, unsigned flags, int event)
 {
 	struct tcmsg *tcm;
@@ -521,9 +660,9 @@
 	tcm = NLMSG_DATA(nlh);
 	tcm->tcm_family = AF_UNSPEC;
 	tcm->tcm_ifindex = q->dev ? q->dev->ifindex : 0;
-	tcm->tcm_parent = q->classid;
+	tcm->tcm_parent = clid;
 	tcm->tcm_handle = q->handle;
-	tcm->tcm_info = 0;
+	tcm->tcm_info = atomic_read(&q->refcnt);
 	RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id);
 	if (q->ops->dump && q->ops->dump(q, skb) < 0)
 		goto rtattr_failure;
@@ -539,7 +678,7 @@
 }
 
 static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n,
-			 struct Qdisc *old, struct Qdisc *new)
+			u32 clid, struct Qdisc *old, struct Qdisc *new)
 {
 	struct sk_buff *skb;
 	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
@@ -548,12 +687,12 @@
 	if (!skb)
 		return -ENOBUFS;
 
-	if (old && !(old->flags&TCQ_F_DEFAULT)) {
-		if (tc_fill_qdisc(skb, old, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0)
+	if (old && old->handle) {
+		if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0)
 			goto err_out;
 	}
 	if (new) {
-		if (tc_fill_qdisc(skb, new, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
+		if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
 			goto err_out;
 	}
 
@@ -583,7 +722,7 @@
 		     q = q->next, q_idx++) {
 			if (q_idx < s_q_idx)
 				continue;
-			if (tc_fill_qdisc(skb, q, NETLINK_CB(cb->skb).pid,
+			if (tc_fill_qdisc(skb, q, 0, NETLINK_CB(cb->skb).pid,
 					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)
 				goto done;
 		}
@@ -797,11 +936,10 @@
 	for (q=dev->qdisc_list, t=0; q; q = q->next, t++) {
 		if (t < s_t) continue;
 		if (!q->ops->cl_ops) continue;
-		if (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle
-		    && (tcm->tcm_parent != TC_H_ROOT || q->parent != NULL))
+		if (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)
 			continue;
 		if (t > s_t)
-			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
 		arg.w.fn = qdisc_class_dump;
 		arg.skb = skb;
 		arg.cb = cb;
@@ -846,6 +984,20 @@
 }
 #endif
 
+#if PSCHED_CLOCK_SOURCE == PSCHED_GETTIMEOFDAY
+int psched_tod_diff(int delta_sec, int bound)
+{
+	int delta;
+
+	if (bound <= 1000000 || delta_sec > (0x7FFFFFFF/1000000)-1)
+		return bound;
+	delta = delta_sec * 1000000;
+	if (delta > bound)
+		delta = bound;
+	return delta;
+}
+#endif
+
 psched_time_t psched_time_base;
 
 #if PSCHED_CLOCK_SOURCE == PSCHED_CPU
@@ -866,7 +1018,8 @@
 #if PSCHED_CLOCK_SOURCE == PSCHED_CPU
 	psched_time_t dummy_stamp;
 	PSCHED_GET_TIME(dummy_stamp);
-	psched_timer.expires = jiffies + 4*HZ;
+	/* It is OK up to 4GHz cpu */
+	psched_timer.expires = jiffies + 1*HZ;
 #else
 	unsigned long now = jiffies;
 	psched_time_base = ((u64)now)<<PSCHED_JSCALE;
@@ -891,7 +1044,6 @@
 		return -1;
 #endif
 
-	start_bh_atomic();
 #ifdef PSCHED_WATCHER
 	psched_tick(0);
 #endif
@@ -902,7 +1054,6 @@
 		barrier();
 	PSCHED_GET_TIME(stamp1);
 	do_gettimeofday(&tv1);
-	end_bh_atomic();
 
 	delay = PSCHED_TDIFF(stamp1, stamp);
 	rdelay = tv1.tv_usec - tv.tv_usec;
@@ -921,6 +1072,9 @@
 
 __initfunc(int pktsched_init(void))
 {
+#ifdef CONFIG_RTNETLINK
+	struct rtnetlink_link *link_p;
+#endif
 #ifdef CONFIG_PROC_FS
 	struct proc_dir_entry *ent;
 #endif
@@ -931,19 +1085,22 @@
 #elif PSCHED_CLOCK_SOURCE == PSCHED_JIFFIES
 	psched_tick_per_us = HZ<<PSCHED_JSCALE;
 	psched_us_per_tick = 1000000;
+#ifdef PSCHED_WATCHER
+	psched_tick(0);
+#endif
 #endif
 
 #ifdef CONFIG_RTNETLINK
-	struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC];
+	link_p = rtnetlink_links[PF_UNSPEC];
 
 	/* Setup rtnetlink links. It is made here to avoid
 	   exporting large number of public symbols.
 	 */
 
 	if (link_p) {
-		link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_ctl_qdisc;
-		link_p[RTM_DELQDISC-RTM_BASE].doit = tc_ctl_qdisc;
-		link_p[RTM_GETQDISC-RTM_BASE].doit = tc_ctl_qdisc;
+		link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc;
+		link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc;
+		link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc;
 		link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc;
 		link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;
 		link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;
@@ -974,6 +1131,12 @@
 #endif
 #ifdef CONFIG_NET_SCH_RED
 	INIT_QDISC(red);
+#endif
+#ifdef CONFIG_NET_SCH_GRED
+       INIT_QDISC(gred);
+#endif
+#ifdef CONFIG_NET_SCH_DSMARK
+       INIT_QDISC(dsmark);
 #endif
 #ifdef CONFIG_NET_SCH_SFQ
 	INIT_QDISC(sfq);
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_cbq.c linux/net/sched/sch_cbq.c
--- v2.2.3/linux/net/sched/sch_cbq.c	Mon Oct  5 13:13:49 1998
+++ linux/net/sched/sch_cbq.c	Sun Mar 21 07:22:00 1999
@@ -37,6 +37,7 @@
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
+
 /*	Class-Based Queueing (CBQ) algorithm.
 	=======================================
 
@@ -169,6 +170,9 @@
 	struct cbq_class	*active[TC_CBQ_MAXPRIO+1];	/* List of all classes
 								   with backlog */
 
+#ifdef CONFIG_NET_CLS_POLICE
+	struct cbq_class	*rx_class;
+#endif
 	struct cbq_class	*tx_class;
 	struct cbq_class	*tx_borrowed;
 	int			tx_len;
@@ -269,17 +273,21 @@
 			else if ((cl = defmap[res.classid&TC_PRIO_MAX]) == NULL)
 				cl = defmap[TC_PRIO_BESTEFFORT];
 
-			if (cl == NULL)
+			if (cl == NULL || cl->level >= head->level)
 				goto fallback;
 		}
 
-		if (cl->level == 0) {
 #ifdef CONFIG_NET_CLS_POLICE
-			if (result)
-				return cbq_reclassify(skb, cl);
+		switch (result) {
+		case TC_POLICE_RECLASSIFY:
+			return cbq_reclassify(skb, cl);
+		case TC_POLICE_SHOT:
+			return NULL;
+		default:
+		}
 #endif
+		if (cl->level == 0)
 			return cl;
-		}
 
 		/*
 		 * Step 3+n. If classifier selected a link sharing class,
@@ -321,11 +329,9 @@
 	if (cl_tail != NULL) {
 		cl->next_alive = cl_tail->next_alive;
 		cl_tail->next_alive = cl;
-		cl->deficit = 0;
 	} else {
 		cl->next_alive = cl;
 		q->activemask |= (1<<prio);
-		cl->deficit = cl->quantum;
 	}
 }
 
@@ -358,31 +364,28 @@
 			}
 
 			cl = cl_prev->next_alive;
-			cl->deficit += cl->quantum;
 			return;
 		}
 	} while ((cl_prev = cl) != q->active[prio]);
 }
 
-static __inline__ void
+static void
 cbq_mark_toplevel(struct cbq_sched_data *q, struct cbq_class *cl)
 {
-	if (q->toplevel > 0) {
+	int toplevel = q->toplevel;
+
+	if (toplevel > cl->level && !(cl->q->flags&TCQ_F_THROTTLED)) {
 		psched_time_t now;
 		PSCHED_GET_TIME(now);
 		if (PSCHED_TLESS(now, q->now))
 			now = q->now;
-		if (PSCHED_TLESS(cl->undertime, now)) {
-			q->toplevel = 0;
-			return;
-		}
-		while ((cl = cl->borrow) != NULL
-		       && q->toplevel > cl->level) {
-			if (PSCHED_TLESS(cl->borrow->undertime, now)) {
+
+		do {
+			if (PSCHED_TLESS(cl->undertime, now)) {
 				q->toplevel = cl->level;
 				return;
 			}
-		}
+		} while ((cl=cl->borrow) != NULL && toplevel > cl->level);
 	}
 }
 
@@ -393,23 +396,31 @@
 	struct cbq_class *cl = cbq_classify(skb, sch);
 	int len = skb->len;
 
-	if (cl && cl->q->enqueue(skb, cl->q) == 1) {
-		sch->q.qlen++;
-		sch->stats.packets++;
-		cl->stats.packets++;
-		sch->stats.bytes+=len;
-		cl->stats.bytes+=len;
-		cbq_mark_toplevel(q, cl);
-		if (!cl->next_alive)
-			cbq_activate_class(cl);
-		return 1;
+#ifdef CONFIG_NET_CLS_POLICE
+	q->rx_class = cl;
+#endif
+	if (cl) {
+#ifdef CONFIG_NET_CLS_POLICE
+		cl->q->__parent = sch;
+#endif
+		if (cl->q->enqueue(skb, cl->q) == 1) {
+			sch->q.qlen++;
+			sch->stats.packets++;
+			sch->stats.bytes+=len;
+			cbq_mark_toplevel(q, cl);
+			if (!cl->next_alive)
+				cbq_activate_class(cl);
+			return 1;
+		}
 	}
 
 	sch->stats.drops++;
 	if (cl == NULL)
 		kfree_skb(skb);
-	else
+	else {
+		cbq_mark_toplevel(q, cl);
 		cl->stats.drops++;
+	}
 	return 0;
 }
 
@@ -426,9 +437,14 @@
 	}
 	q->tx_class = NULL;
 
+	cbq_mark_toplevel(q, cl);
+
+#ifdef CONFIG_NET_CLS_POLICE
+	q->rx_class = cl;
+	cl->q->__parent = sch;
+#endif
 	if (cl->q->ops->requeue(skb, cl->q) == 1) {
 		sch->q.qlen++;
-		cbq_mark_toplevel(q, cl);
 		if (!cl->next_alive)
 			cbq_activate_class(cl);
 		return 1;
@@ -445,11 +461,9 @@
 static void cbq_ovl_classic(struct cbq_class *cl)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)cl->qdisc->data;
+	psched_tdiff_t delay = PSCHED_TDIFF(cl->undertime, q->now);
 
 	if (!cl->delayed) {
-		psched_tdiff_t delay;
-
-		delay = PSCHED_TDIFF(cl->undertime, q->now);
 		delay += cl->offtime;
 
 		/* 
@@ -463,15 +477,35 @@
 			delay -= (-cl->avgidle) - ((-cl->avgidle) >> cl->ewma_log);
 		if (cl->avgidle < cl->minidle)
 			cl->avgidle = cl->minidle;
-		if (delay < 0)
-			delay = 0;
+		if (delay <= 0)
+			delay = 1;
 		PSCHED_TADD2(q->now, delay, cl->undertime);
 
-		if (q->wd_expires == 0 || q->wd_expires > delay)
-			q->wd_expires = delay;
 		cl->xstats.overactions++;
 		cl->delayed = 1;
 	}
+	if (q->wd_expires == 0 || q->wd_expires > delay)
+		q->wd_expires = delay;
+
+	/* Dirty work! We must schedule wakeups based on
+	   real available rate, rather than leaf rate,
+	   which may be tiny (even zero).
+	 */
+	if (q->toplevel == TC_CBQ_MAXLEVEL) {
+		struct cbq_class *b;
+		psched_tdiff_t base_delay = q->wd_expires;
+
+		for (b = cl->borrow; b; b = b->borrow) {
+			delay = PSCHED_TDIFF(b->undertime, q->now);
+			if (delay < base_delay) {
+				if (delay <= 0)
+					delay = 1;
+				base_delay = delay;
+			}
+		}
+
+		q->wd_expires = delay;
+	}
 }
 
 /* TC_CBQ_OVL_RCLASSIC: penalize by offtime classes in hierarchy, when
@@ -481,15 +515,18 @@
 static void cbq_ovl_rclassic(struct cbq_class *cl)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)cl->qdisc->data;
+	struct cbq_class *this = cl;
 
-	while (cl && cl->delayed) {
-		cl = cl->borrow;
-		if (cl->level > q->toplevel)
-			return;
-	}
+	do {
+		if (cl->level > q->toplevel) {
+			cl = NULL;
+			break;
+		}
+	} while ((cl = cl->borrow) != NULL);
 
-	if (cl)
-		cbq_ovl_classic(cl);
+	if (cl == NULL)
+		cl = this;
+	cbq_ovl_classic(cl);
 }
 
 /* TC_CBQ_OVL_DELAY: delay until it will go to underlimit */
@@ -497,12 +534,11 @@
 static void cbq_ovl_delay(struct cbq_class *cl)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)cl->qdisc->data;
+	psched_tdiff_t delay = PSCHED_TDIFF(cl->undertime, q->now);
 
 	if (!cl->delayed) {
-		psched_tdiff_t delay;
 		unsigned long sched = jiffies;
 
-		delay = PSCHED_TDIFF(cl->undertime, q->now);
 		delay += cl->offtime;
 		if (cl->avgidle < 0)
 			delay -= (-cl->avgidle) - ((-cl->avgidle) >> cl->ewma_log);
@@ -521,8 +557,12 @@
 			add_timer(&q->delay_timer);
 			cl->delayed = 1;
 			cl->xstats.overactions++;
+			return;
 		}
+		delay = 1;
 	}
+	if (q->wd_expires == 0 || q->wd_expires > delay)
+		q->wd_expires = delay;
 }
 
 /* TC_CBQ_OVL_LOWPRIO: penalize class by lowering its priority band */
@@ -555,6 +595,7 @@
 static void cbq_watchdog(unsigned long arg)
 {
 	struct Qdisc *sch = (struct Qdisc*)arg;
+	sch->flags &= ~TCQ_F_THROTTLED;
 	qdisc_wakeup(sch->dev);
 }
 
@@ -622,6 +663,7 @@
 		add_timer(&q->delay_timer);
 	}
 
+	sch->flags &= ~TCQ_F_THROTTLED;
 	qdisc_wakeup(sch->dev);
 }
 
@@ -631,18 +673,23 @@
 static int cbq_reshape_fail(struct sk_buff *skb, struct Qdisc *child)
 {
 	int len = skb->len;
-	struct Qdisc *sch = child->parent;
+	struct Qdisc *sch = child->__parent;
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
-	struct cbq_class *cl = cbq_class_lookup(q, child->classid);
+	struct cbq_class *cl = q->rx_class;
+
+	q->rx_class = NULL;
 
 	if (cl && (cl = cbq_reclassify(skb, cl)) != NULL) {
+
+		cbq_mark_toplevel(q, cl);
+
+		q->rx_class = cl;
+		cl->q->__parent = sch;
+
 		if (cl->q->enqueue(skb, cl->q) == 1) {
 			sch->q.qlen++;
 			sch->stats.packets++;
-			cl->stats.packets++;
 			sch->stats.bytes+=len;
-			cl->stats.bytes+=len;
-			cbq_mark_toplevel(q, cl);
 			if (!cl->next_alive)
 				cbq_activate_class(cl);
 			return 0;
@@ -656,21 +703,42 @@
 }
 #endif
 
+/* 
+   It is mission critical procedure.
+
+   We "regenerate" toplevel cutoff, if transmitting class
+   has backlog and it is not regulated. It is not part of
+   original CBQ description, but looks more reasonable.
+   Probably, it is wrong. This question needs further investigation.
+*/
+
 static __inline__ void
-cbq_update_toplevel(struct cbq_sched_data *q, struct cbq_class *cl)
+cbq_update_toplevel(struct cbq_sched_data *q, struct cbq_class *cl,
+		    struct cbq_class *borrowed)
 {
-	if (cl && q->toplevel >= cl->level) {
-		if (cl->q->q.qlen <= 1 || PSCHED_TLESS(q->now, cl->undertime))
-			q->toplevel = TC_CBQ_MAXLEVEL;
-		else /* BUGGGG? if (cl != this) */
-			q->toplevel = cl->level;
+	if (cl && q->toplevel >= borrowed->level) {
+		if (cl->q->q.qlen > 1) {
+			do {
+				if (PSCHED_IS_PASTPERFECT(borrowed->undertime)) {
+					q->toplevel = borrowed->level;
+					return;
+				}
+			} while ((borrowed=borrowed->borrow) != NULL);
+		}
+#if 0	
+	/* It is not necessary now. Uncommenting it
+	   will save CPU cycles, but decrease fairness.
+	 */
+		q->toplevel = TC_CBQ_MAXLEVEL;
+#endif
 	}
 }
 
-static __inline__ void
+static void
 cbq_update(struct cbq_sched_data *q)
 {
-	struct cbq_class *cl = q->tx_class;
+	struct cbq_class *this = q->tx_class;
+	struct cbq_class *cl = this;
 	int len = q->tx_len;
 
 	q->tx_class = NULL;
@@ -679,6 +747,9 @@
 		long avgidle = cl->avgidle;
 		long idle;
 
+		cl->stats.packets++;
+		cl->stats.bytes += len;
+
 		/*
 		   (now - last) is total time between packet right edges.
 		   (last_pktlen/rate) is "virtual" busy time, so that
@@ -697,6 +768,10 @@
 
 		if (avgidle <= 0) {
 			/* Overlimit or at-limit */
+
+			if (avgidle < cl->minidle)
+				avgidle = cl->minidle;
+
 			cl->avgidle = avgidle;
 
 			/* Calculate expected time, when this class
@@ -732,12 +807,11 @@
 				cl->avgidle = cl->maxidle;
 			else
 				cl->avgidle = avgidle;
-
 		}
 		cl->last = q->now;
 	}
 
-	cbq_update_toplevel(q, q->tx_borrowed);
+	cbq_update_toplevel(q, this, q->tx_borrowed);
 }
 
 static __inline__ struct cbq_class *
@@ -750,21 +824,33 @@
 		return cl;
 
 	if (PSCHED_IS_PASTPERFECT(cl->undertime) ||
-	    PSCHED_TLESS(cl->undertime, q->now)) {
+	    !PSCHED_TLESS(q->now, cl->undertime)) {
 		cl->delayed = 0;
 		return cl;
 	}
 
-	while (!PSCHED_IS_PASTPERFECT(cl->undertime) &&
-	       PSCHED_TLESS(q->now, cl->undertime)) {
-		if ((cl = cl->borrow) == NULL || cl->level > q->toplevel) {
+	do {
+		/* It is very suspicious place. Now overlimit
+		   action is generated for not bounded classes
+		   only if link is completely congested.
+		   Though it is in agree with ancestor-only paradigm,
+		   it looks very stupid. Particularly,
+		   it means that this chunk of code will either
+		   never be called or result in strong amplification
+		   of burstiness. Dangerous, silly, and, however,
+		   no another solution exists.
+		 */
+		if ((cl = cl->borrow) == NULL) {
 			this_cl->stats.overlimits++;
 			this_cl->overlimit(this_cl);
 			return NULL;
 		}
-	}
-	this_cl->xstats.borrows++;
-	cl->xstats.borrows++;
+		if (cl->level > q->toplevel)
+			return NULL;
+	} while (!PSCHED_IS_PASTPERFECT(cl->undertime) &&
+		 PSCHED_TLESS(q->now, cl->undertime));
+
+	cl->delayed = 0;
 	return cl;
 }
 
@@ -784,27 +870,26 @@
 
 		/* Start round */
 		do {
-			struct cbq_class *borrow;
+			struct cbq_class *borrow = NULL;
 
-			/* Class is empty */
-			if (cl->q->q.qlen == 0)
-				goto skip_class;
-
-			if ((borrow = cbq_under_limit(cl)) == NULL)
+			if (cl->q->q.qlen &&
+			    (borrow = cbq_under_limit(cl)) == NULL)
 				goto skip_class;
 
 			if (cl->deficit <= 0) {
-				/* Class exhausted its allotment per this
-				   round.
+				/* Class exhausted its allotment per
+				   this round. Switch to the next one.
 				 */
 				deficit = 1;
+				cl->deficit += cl->quantum;
 				goto next_class;
 			}
 
 			skb = cl->q->dequeue(cl->q);
 
 			/* Class did not give us any skb :-(
-			   It could occur if cl->q == "tbf"
+			   It could occur even if cl->q->q.qlen != 0 
+			   f.e. if cl->q == "tbf"
 			 */
 			if (skb == NULL)
 				goto skip_class;
@@ -812,6 +897,15 @@
 			cl->deficit -= skb->len;
 			q->tx_class = cl;
 			q->tx_borrowed = borrow;
+			if (borrow != cl) {
+#ifndef CBQ_XSTATS_BORROWS_BYTES
+				borrow->xstats.borrows++;
+				cl->xstats.borrows++;
+#else
+				borrow->xstats.borrows += skb->len;
+				cl->xstats.borrows += skb->len;
+#endif
+			}
 			q->tx_len = skb->len;
 
 			if (cl->deficit <= 0) {
@@ -822,8 +916,6 @@
 			return skb;
 
 skip_class:
-			cl->deficit = 0;
-
 			if (cl->q->q.qlen == 0 || prio != cl->cpriority) {
 				/* Class is empty or penalized.
 				   Unlink it from active chain.
@@ -857,7 +949,6 @@
 next_class:
 			cl_prev = cl;
 			cl = cl->next_alive;
-			cl->deficit += cl->quantum;
 		} while (cl_prev != cl_tail);
 	} while (deficit);
 
@@ -914,6 +1005,7 @@
 		skb = cbq_dequeue_1(sch);
 		if (skb) {
 			sch->q.qlen--;
+			sch->flags &= ~TCQ_F_THROTTLED;
 			return skb;
 		}
 
@@ -955,6 +1047,7 @@
 				delay = 1;
 			q->wd_timer.expires = jiffies + delay;
 			add_timer(&q->wd_timer);
+			sch->flags |= TCQ_F_THROTTLED;
 		}
 	}
 	return NULL;
@@ -1129,14 +1222,18 @@
 static int cbq_drop(struct Qdisc* sch)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
-	struct cbq_class *cl;
-	int h;
+	struct cbq_class *cl, *cl_head;
+	int prio;
 
-	for (h = TC_CBQ_MAXPRIO; h >= 0; h++) {
-		for (cl = q->classes[h]; cl; cl = cl->next) {
+	for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio++) {
+		if ((cl_head = q->active[prio]) == NULL)
+			continue;
+
+		cl = cl_head;
+		do {
 			if (cl->q->ops->drop && cl->q->ops->drop(cl->q))
 				return 1;
-		}
+		} while ((cl = cl->next_alive) != cl_head);
 	}
 	return 0;
 }
@@ -1166,8 +1263,8 @@
 
 			cl->next_alive = NULL;
 			PSCHED_SET_PASTPERFECT(cl->undertime);
-			cl->avgidle = 0;
-			cl->deficit = 0;
+			cl->avgidle = cl->maxidle;
+			cl->deficit = cl->quantum;
 			cl->cpriority = cl->priority;
 		}
 	}
@@ -1187,8 +1284,10 @@
 		cl->avpkt = lss->avpkt;
 	if (lss->change&TCF_CBQ_LSS_MINIDLE)
 		cl->minidle = -(long)lss->minidle;
-	if (lss->change&TCF_CBQ_LSS_MAXIDLE)
+	if (lss->change&TCF_CBQ_LSS_MAXIDLE) {
 		cl->maxidle = lss->maxidle;
+		cl->avgidle = lss->maxidle;
+	}
 	if (lss->change&TCF_CBQ_LSS_OFFTIME)
 		cl->offtime = lss->offtime;
 	return 0;
@@ -1261,7 +1360,7 @@
 {
 	cl->police = p->police;
 
-	if (!(cl->q->flags&TCQ_F_DEFAULT)) {
+	if (cl->q->handle) {
 		if (p->police == TC_POLICE_RECLASSIFY)
 			cl->q->reshape_fail = cbq_reshape_fail;
 		else
@@ -1300,6 +1399,7 @@
 		return -EINVAL;
 	}
 
+	q->link.refcnt = 1;
 	q->link.sibling = &q->link;
 	q->link.classid = sch->handle;
 	q->link.qdisc = sch;
@@ -1493,6 +1593,7 @@
 	else
 		tcm->tcm_parent = TC_H_ROOT;
 	tcm->tcm_handle = cl->classid;
+	tcm->tcm_info = cl->q->handle;
 
 	rta = (struct rtattr*)b;
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
@@ -1533,12 +1634,20 @@
 		}
 		if ((*old = xchg(&cl->q, new)) != NULL)
 			qdisc_reset(*old);
-			
+
 		return 0;
 	}
 	return -ENOENT;
 }
 
+static struct Qdisc *
+cbq_leaf(struct Qdisc *sch, unsigned long arg)
+{
+	struct cbq_class *cl = (struct cbq_class*)arg;
+
+	return cl ? cl->q : NULL;
+}
+
 static unsigned long cbq_get(struct Qdisc *sch, u32 classid)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
@@ -1569,6 +1678,7 @@
 #ifdef CONFIG_NET_ESTIMATOR
 	qdisc_kill_estimator(&cl->stats);
 #endif
+	kfree(cl);
 }
 
 static void
@@ -1578,6 +1688,9 @@
 	struct cbq_class *cl;
 	unsigned h;
 
+#ifdef CONFIG_NET_CLS_POLICE
+	q->rx_class = NULL;
+#endif
 	for (h = 0; h < 16; h++) {
 		for (cl = q->classes[h]; cl; cl = cl->next)
 			cbq_destroy_filters(cl);
@@ -1590,20 +1703,29 @@
 	}
 
 	qdisc_put_rtab(q->link.R_tab);
+	MOD_DEC_USE_COUNT;
 }
 
-static void cbq_put(struct Qdisc *q, unsigned long arg)
+static void cbq_put(struct Qdisc *sch, unsigned long arg)
 {
+	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
 	struct cbq_class *cl = (struct cbq_class*)arg;
 
-	if (--cl->refcnt == 0)
+	start_bh_atomic();
+	if (--cl->refcnt == 0) {
+#ifdef CONFIG_NET_CLS_POLICE
+		if (q->rx_class == cl)
+			q->rx_class = NULL;
+#endif
 		cbq_destroy_class(cl);
+	}
+	end_bh_atomic();
 	return;
 }
 
 static int
-cbq_change(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **tca,
-	   unsigned long *arg)
+cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **tca,
+		 unsigned long *arg)
 {
 	int err;
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
@@ -1763,6 +1885,7 @@
 	cl->borrow = cl->tparent;
 	if (cl->tparent != &q->link)
 		cl->share = cl->tparent;
+	cbq_adjust_levels(parent);
 	cl->minidle = -0x7FFFFFFF;
 	cbq_set_lss(cl, RTA_DATA(tb[TCA_CBQ_LSSOPT-1]));
 	cbq_set_wrr(cl, RTA_DATA(tb[TCA_CBQ_WRROPT-1]));
@@ -1781,7 +1904,6 @@
 #endif
 	if (tb[TCA_CBQ_FOPT-1])
 		cbq_set_fopt(cl, RTA_DATA(tb[TCA_CBQ_FOPT-1]));
-	cbq_adjust_levels(parent);
 	end_bh_atomic();
 
 #ifdef CONFIG_NET_ESTIMATOR
@@ -1810,10 +1932,16 @@
 	if (cl->next_alive)
 		cbq_deactivate_class(cl);
 
-	if (q->tx_class == cl)
-		q->tx_class = cl->borrow;
 	if (q->tx_borrowed == cl)
 		q->tx_borrowed = q->tx_class;
+	if (q->tx_class == cl) {
+		q->tx_class = NULL;
+		q->tx_borrowed = NULL;
+	}
+#ifdef CONFIG_NET_CLS_POLICE
+	if (q->rx_class == cl)
+		q->rx_class = NULL;
+#endif
 
 	cbq_unlink_class(cl);
 	cbq_adjust_levels(cl->tparent);
@@ -1841,12 +1969,16 @@
 	return &cl->filter_list;
 }
 
-static unsigned long cbq_bind_filter(struct Qdisc *sch, u32 classid)
+static unsigned long cbq_bind_filter(struct Qdisc *sch, unsigned long parent,
+				     u32 classid)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
+	struct cbq_class *p = (struct cbq_class*)parent;
 	struct cbq_class *cl = cbq_class_lookup(q, classid);
 
 	if (cl) {
+		if (p && p->level <= cl->level)
+			return 0;
 		cl->filters++;
 		return (unsigned long)cl;
 	}
@@ -1878,7 +2010,7 @@
 			}
 			if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
 				arg->stop = 1;
-				break;
+				return;
 			}
 			arg->count++;
 		}
@@ -1888,9 +2020,10 @@
 static struct Qdisc_class_ops cbq_class_ops =
 {
 	cbq_graft,
+	cbq_leaf,
 	cbq_get,
 	cbq_put,
-	cbq_change,
+	cbq_change_class,
 	cbq_delete,
 	cbq_walk,
 
@@ -1918,6 +2051,7 @@
 	cbq_init,
 	cbq_reset,
 	cbq_destroy,
+	NULL /* cbq_change */,
 
 #ifdef CONFIG_RTNETLINK
 	cbq_dump,
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_csz.c linux/net/sched/sch_csz.c
--- v2.2.3/linux/net/sched/sch_csz.c	Fri Jan  8 22:36:27 1999
+++ linux/net/sched/sch_csz.c	Sun Mar 21 07:22:00 1999
@@ -826,6 +826,12 @@
 	return -EINVAL;
 }
 
+static struct Qdisc * csz_leaf(struct Qdisc *sch, unsigned long cl)
+{
+	return NULL;
+}
+
+
 static unsigned long csz_get(struct Qdisc *sch, u32 classid)
 {
 	struct csz_sched_data *q = (struct csz_sched_data *)sch->data;
@@ -840,6 +846,12 @@
 	return band+1;
 }
 
+static unsigned long csz_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
+{
+	return csz_get(sch, classid);
+}
+
+
 static void csz_put(struct Qdisc *sch, unsigned long cl)
 {
 	return;
@@ -1006,6 +1018,8 @@
 struct Qdisc_class_ops csz_class_ops =
 {
 	csz_graft,
+	csz_leaf,
+
 	csz_get,
 	csz_put,
 	csz_change,
@@ -1013,7 +1027,7 @@
 	csz_walk,
 
 	csz_find_tcf,
-	csz_get,
+	csz_bind,
 	csz_put,
 
 #ifdef CONFIG_RTNETLINK
@@ -1036,6 +1050,7 @@
 	csz_init,
 	csz_reset,
 	csz_destroy,
+	NULL /* csz_change */,
 
 #ifdef CONFIG_RTNETLINK
 	csz_dump,
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_fifo.c linux/net/sched/sch_fifo.c
--- v2.2.3/linux/net/sched/sch_fifo.c	Sat May  2 14:19:55 1998
+++ linux/net/sched/sch_fifo.c	Sun Mar 21 07:22:00 1999
@@ -97,10 +97,7 @@
 static void
 fifo_reset(struct Qdisc* sch)
 {
-	struct sk_buff *skb;
-
-	while ((skb=__skb_dequeue(&sch->q)) != NULL)
-		kfree_skb(skb);
+	skb_queue_purge(&sch->q);
 	sch->stats.backlog = 0;
 }
 
@@ -137,15 +134,15 @@
 	return __skb_dequeue(&sch->q);
 }
 
-
 static int fifo_init(struct Qdisc *sch, struct rtattr *opt)
 {
 	struct fifo_sched_data *q = (void*)sch->data;
 
 	if (opt == NULL) {
-		q->limit = sch->dev->tx_queue_len;
 		if (sch->ops == &bfifo_qdisc_ops)
-			q->limit *= sch->dev->mtu;
+			q->limit = sch->dev->tx_queue_len*sch->dev->mtu;
+		else	
+			q->limit = sch->dev->tx_queue_len;
 	} else {
 		struct tc_fifo_qopt *ctl = RTA_DATA(opt);
 		if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
@@ -188,6 +185,8 @@
 	fifo_init,
 	fifo_reset,
 	NULL,
+	fifo_init,
+
 #ifdef CONFIG_RTNETLINK
 	fifo_dump,
 #endif
@@ -208,6 +207,7 @@
 	fifo_init,
 	fifo_reset,
 	NULL,
+	fifo_init,
 #ifdef CONFIG_RTNETLINK
 	fifo_dump,
 #endif
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_generic.c linux/net/sched/sch_generic.c
--- v2.2.3/linux/net/sched/sch_generic.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/sch_generic.c	Sun Mar 21 07:22:00 1999
@@ -184,7 +184,7 @@
         { NULL }, 
 	noop_enqueue,
 	noop_dequeue,
-	TCQ_F_DEFAULT|TCQ_F_BUILTIN,
+	TCQ_F_BUILTIN,
 	&noop_qdisc_ops,	
 };
 
@@ -207,7 +207,7 @@
         { NULL }, 
 	NULL,
 	NULL,
-	TCQ_F_DEFAULT|TCQ_F_BUILTIN,
+	TCQ_F_BUILTIN,
 	&noqueue_qdisc_ops,
 };
 
@@ -322,8 +322,8 @@
 	sch->enqueue = ops->enqueue;
 	sch->dequeue = ops->dequeue;
 	sch->dev = dev;
-	sch->flags |= TCQ_F_DEFAULT;
-	if (ops->init && ops->init(sch, NULL) == 0)
+	atomic_set(&sch->refcnt, 1);
+	if (!ops->init || ops->init(sch, NULL) == 0)
 		return sch;
 
 	kfree(sch);
@@ -342,6 +342,10 @@
 void qdisc_destroy(struct Qdisc *qdisc)
 {
 	struct Qdisc_ops *ops = qdisc->ops;
+
+	if (!atomic_dec_and_test(&qdisc->refcnt))
+		return;
+
 #ifdef CONFIG_NET_SCHED
 	if (qdisc->dev) {
 		struct Qdisc *q, **qp;
@@ -442,32 +446,5 @@
 	BUG_TRAP(dev->qdisc_list == NULL);
 	dev->qdisc_list = NULL;
 	end_bh_atomic();
-}
-
-struct Qdisc * dev_set_scheduler(struct device *dev, struct Qdisc *qdisc)
-{
-	struct Qdisc *oqdisc;
-
-	if (dev->flags & IFF_UP)
-		dev_deactivate(dev);
-
-	start_bh_atomic();
-	oqdisc = dev->qdisc_sleeping;
-
-	/* Prune old scheduler */
-	if (oqdisc)
-		qdisc_reset(oqdisc);
-
-	/* ... and graft new one */
-	if (qdisc == NULL)
-		qdisc = &noop_qdisc;
-	dev->qdisc_sleeping = qdisc;
-	dev->qdisc = &noop_qdisc;
-	end_bh_atomic();
-
-	if (dev->flags & IFF_UP)
-		dev_activate(dev);
-
-	return oqdisc;
 }
 
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_prio.c linux/net/sched/sch_prio.c
--- v2.2.3/linux/net/sched/sch_prio.c	Sat May  2 14:19:55 1998
+++ linux/net/sched/sch_prio.c	Sun Mar 21 07:22:00 1999
@@ -49,17 +49,19 @@
 {
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
 	struct tcf_result res;
+	u32 band;
 
-	res.classid = skb->priority;
-	if (TC_H_MAJ(res.classid) != sch->handle) {
+	band = skb->priority;
+	if (TC_H_MAJ(skb->priority) != sch->handle) {
 		if (!q->filter_list || tc_classify(skb, q->filter_list, &res)) {
-			if (TC_H_MAJ(res.classid))
-				res.classid = 0;
-			res.classid = q->prio2band[res.classid&TC_PRIO_MAX] + 1;
+			if (TC_H_MAJ(band))
+				band = 0;
+			return q->prio2band[band&TC_PRIO_MAX];
 		}
+		band = res.classid;
 	}
-
-	return res.classid - 1;
+	band = TC_H_MIN(band) - 1;
+	return band < q->bands ? band : q->prio2band[0];
 }
 
 static int
@@ -160,38 +162,74 @@
 	MOD_DEC_USE_COUNT;
 }
 
+static int prio_tune(struct Qdisc *sch, struct rtattr *opt)
+{
+	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
+	struct tc_prio_qopt *qopt = RTA_DATA(opt);
+	int i;
+
+	if (opt->rta_len < RTA_LENGTH(sizeof(*qopt)))
+		return -EINVAL;
+	if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2)
+		return -EINVAL;
+
+	for (i=0; i<=TC_PRIO_MAX; i++) {
+		if (qopt->priomap[i] >= qopt->bands)
+			return -EINVAL;
+	}
+
+	start_bh_atomic();
+	q->bands = qopt->bands;
+	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
+
+	for (i=q->bands; i<TCQ_PRIO_BANDS; i++) {
+		struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc);
+		if (child != &noop_qdisc)
+			qdisc_destroy(child);
+	}
+	end_bh_atomic();
+
+	for (i=0; i<=TC_PRIO_MAX; i++) {
+		int band = q->prio2band[i];
+		if (q->queues[band] == &noop_qdisc) {
+			struct Qdisc *child;
+			child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+			if (child) {
+				net_serialize_enter();
+				child = xchg(&q->queues[band], child);
+				net_serialize_leave();
+				if (child != &noop_qdisc)
+					qdisc_destroy(child);
+			}
+		}
+	}
+	return 0;
+}
+
 static int prio_init(struct Qdisc *sch, struct rtattr *opt)
 {
 	static const u8 prio2band[TC_PRIO_MAX+1] =
 	{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
-	unsigned mask = 0;
 	int i;
 
+	for (i=0; i<TCQ_PRIO_BANDS; i++)
+		q->queues[i] = &noop_qdisc;
+
 	if (opt == NULL) {
 		q->bands = 3;
 		memcpy(q->prio2band, prio2band, sizeof(prio2band));
-		mask = 7;
+		for (i=0; i<3; i++) {
+			struct Qdisc *child;
+			child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
+			if (child)
+				q->queues[i] = child;
+		}
 	} else {
-		struct tc_prio_qopt *qopt = RTA_DATA(opt);
+		int err;
 
-		if (opt->rta_len < RTA_LENGTH(sizeof(*qopt)))
-			return -EINVAL;
-		if (qopt->bands > TCQ_PRIO_BANDS)
-			return -EINVAL;
-		q->bands = qopt->bands;
-		for (i=0; i<=TC_PRIO_MAX; i++) {
-			if (qopt->priomap[i] >= q->bands)
-				return -EINVAL;
-			q->prio2band[i] = qopt->priomap[i];
-			mask |= (1<<qopt->priomap[i]);
-		}
-	}
-	for (i=0; i<TCQ_PRIO_BANDS; i++) {
-		if (mask&(1<<i))
-			q->queues[i] = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
-		if (q->queues[i] == NULL)
-			q->queues[i] = &noop_qdisc;
+		if ((err= prio_tune(sch, opt)) != 0)
+			return err;
 	}
 	MOD_INC_USE_COUNT;
 	return 0;
@@ -232,6 +270,18 @@
 	return 0;
 }
 
+static struct Qdisc *
+prio_leaf(struct Qdisc *sch, unsigned long arg)
+{
+	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
+	unsigned long band = arg - 1;
+
+	if (band >= q->bands)
+		return NULL;
+
+	return q->queues[band];
+}
+
 static unsigned long prio_get(struct Qdisc *sch, u32 classid)
 {
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
@@ -242,6 +292,12 @@
 	return band;
 }
 
+static unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid)
+{
+	return prio_get(sch, classid);
+}
+
+
 static void prio_put(struct Qdisc *q, unsigned long cl)
 {
 	return;
@@ -267,12 +323,15 @@
 
 
 #ifdef CONFIG_RTNETLINK
-static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm)
+static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb,
+			   struct tcmsg *tcm)
 {
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
 
 	if (cl - 1 > q->bands)
 		return -ENOENT;
+	if (q->queues[cl-1])
+		tcm->tcm_info = q->queues[cl-1]->handle;
 	return 0;
 }
 #endif
@@ -310,6 +369,8 @@
 static struct Qdisc_class_ops prio_class_ops =
 {
 	prio_graft,
+	prio_leaf,
+
 	prio_get,
 	prio_put,
 	prio_change,
@@ -317,7 +378,7 @@
 	prio_walk,
 
 	prio_find_tcf,
-	prio_get,
+	prio_bind,
 	prio_put,
 
 #ifdef CONFIG_RTNETLINK
@@ -340,6 +401,7 @@
 	prio_init,
 	prio_reset,
 	prio_destroy,
+	prio_tune,
 
 #ifdef CONFIG_RTNETLINK
 	prio_dump,
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_red.c linux/net/sched/sch_red.c
--- v2.2.3/linux/net/sched/sch_red.c	Mon Oct  5 13:13:49 1998
+++ linux/net/sched/sch_red.c	Sun Mar 21 07:22:00 1999
@@ -193,8 +193,8 @@
 	}
 
 	if (q->qave < q->qth_min) {
-enqueue:
 		q->qcount = -1;
+enqueue:
 		if (sch->stats.backlog <= q->limit) {
 			__skb_queue_tail(&sch->q, skb);
 			sch->stats.backlog += skb->len;
@@ -231,6 +231,7 @@
 		 */
 		if (((q->qave - q->qth_min)>>q->Wlog)*q->qcount < q->qR)
 			goto enqueue;
+printk(KERN_DEBUG "Drop %d\n", q->qcount);
 		q->qcount = 0;
 		q->qR = net_random()&q->Rmask;
 		sch->stats.overlimits++;
@@ -375,6 +376,7 @@
 	red_init,
 	red_reset,
 	red_destroy,
+	NULL /* red_change */,
 
 #ifdef CONFIG_RTNETLINK
 	red_dump,
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_sfq.c linux/net/sched/sch_sfq.c
--- v2.2.3/linux/net/sched/sch_sfq.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/sch_sfq.c	Sun Mar 21 07:22:00 1999
@@ -380,6 +380,27 @@
 	}
 }
 
+static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
+{
+	struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+	struct tc_sfq_qopt *ctl = RTA_DATA(opt);
+
+	if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
+		return -EINVAL;
+
+	start_bh_atomic();
+	q->quantum = ctl->quantum ? : psched_mtu(sch->dev);
+	q->perturb_period = ctl->perturb_period*HZ;
+
+	del_timer(&q->perturb_timer);
+	if (q->perturb_period) {
+		q->perturb_timer.expires = jiffies + q->perturb_period;
+		add_timer(&q->perturb_timer);
+	}
+	end_bh_atomic();
+	return 0;
+}
+
 static int sfq_init(struct Qdisc *sch, struct rtattr *opt)
 {
 	struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
@@ -399,24 +420,15 @@
 	q->max_depth = 0;
 	q->tail = SFQ_DEPTH;
 	if (opt == NULL) {
-		q->quantum = sch->dev->mtu;
+		q->quantum = psched_mtu(sch->dev);
 		q->perturb_period = 0;
-		if (sch->dev->hard_header)
-			q->quantum += sch->dev->hard_header_len;
 	} else {
-		struct tc_sfq_qopt *ctl = RTA_DATA(opt);
-		if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
-			return -EINVAL;
-		q->quantum = ctl->quantum ? : psched_mtu(sch->dev);
-		q->perturb_period = ctl->perturb_period*HZ;
-		/* The rest is compiled in */
+		int err = sfq_change(sch, opt);
+		if (err)
+			return err;
 	}
 	for (i=0; i<SFQ_DEPTH; i++)
 		sfq_link(q, i);
-	if (q->perturb_period) {
-		q->perturb_timer.expires = jiffies + q->perturb_period;
-		add_timer(&q->perturb_timer);
-	}
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -467,6 +479,7 @@
 	sfq_init,
 	sfq_reset,
 	sfq_destroy,
+	NULL, /* sfq_change */
 
 #ifdef CONFIG_RTNETLINK
 	sfq_dump,
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_tbf.c linux/net/sched/sch_tbf.c
--- v2.2.3/linux/net/sched/sch_tbf.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/sch_tbf.c	Sun Mar 21 07:22:00 1999
@@ -114,6 +114,7 @@
 	u32		limit;		/* Maximal length of backlog: bytes */
 	u32		buffer;		/* Token bucket depth/rate: MUST BE >= MTU/B */
 	u32		mtu;
+	u32		max_size;
 	struct qdisc_rate_table	*R_tab;
 	struct qdisc_rate_table	*P_tab;
 
@@ -132,6 +133,8 @@
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
 
+	if (skb->len > q->max_size)
+		goto drop;
 	__skb_queue_tail(&sch->q, skb);
 	if ((sch->stats.backlog += skb->len) <= q->limit) {
 		sch->stats.bytes += skb->len;
@@ -145,6 +148,8 @@
 
 	__skb_unlink(skb, &sch->q);
 	sch->stats.backlog -= skb->len;
+
+drop:
 	sch->stats.drops++;
 #ifdef CONFIG_NET_CLS_POLICE
 	if (sch->reshape_fail==NULL || sch->reshape_fail(skb, sch))
@@ -180,6 +185,7 @@
 {
 	struct Qdisc *sch = (struct Qdisc*)arg;
 
+	sch->flags &= ~TCQ_F_THROTTLED;
 	qdisc_wakeup(sch->dev);
 }
 
@@ -216,6 +222,7 @@
 			q->tokens = toks;
 			q->ptokens = ptoks;
 			sch->stats.backlog -= skb->len;
+			sch->flags &= ~TCQ_F_THROTTLED;
 			return skb;
 		}
 
@@ -238,10 +245,11 @@
 		   Really, if we split the flow into independent
 		   subflows, it would be a very good solution.
 		   This is the main idea of all FQ algorithms
-		   (cf. CSZ, HPFQ, HFCS)
+		   (cf. CSZ, HPFQ, HFSC)
 		 */
 		__skb_queue_head(&sch->q, skb);
 
+		sch->flags |= TCQ_F_THROTTLED;
 		sch->stats.overlimits++;
 	}
 	return NULL;
@@ -258,53 +266,86 @@
 	PSCHED_GET_TIME(q->t_c);
 	q->tokens = q->buffer;
 	q->ptokens = q->mtu;
+	sch->flags &= ~TCQ_F_THROTTLED;
 	del_timer(&q->wd_timer);
 }
 
-static int tbf_init(struct Qdisc* sch, struct rtattr *opt)
+static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
 {
+	int err = -EINVAL;
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
 	struct rtattr *tb[TCA_TBF_PTAB];
 	struct tc_tbf_qopt *qopt;
+	struct qdisc_rate_table *rtab = NULL;
+	struct qdisc_rate_table *ptab = NULL;
+	int max_size;
 
-	MOD_INC_USE_COUNT;
-
-	if (opt == NULL ||
-	    rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) ||
+	if (rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) ||
 	    tb[TCA_TBF_PARMS-1] == NULL ||
-	    RTA_PAYLOAD(tb[TCA_TBF_PARMS-1]) < sizeof(*qopt)) {
-		MOD_DEC_USE_COUNT;
-		return -EINVAL;
-	}
+	    RTA_PAYLOAD(tb[TCA_TBF_PARMS-1]) < sizeof(*qopt))
+		goto done;
 
 	qopt = RTA_DATA(tb[TCA_TBF_PARMS-1]);
-	q->R_tab = qdisc_get_rtab(&qopt->rate, tb[TCA_TBF_RTAB-1]);
-	if (q->R_tab == NULL) {
-		MOD_DEC_USE_COUNT;
-		return -EINVAL;
-	}
+	rtab = qdisc_get_rtab(&qopt->rate, tb[TCA_TBF_RTAB-1]);
+	if (rtab == NULL)
+		goto done;
 
 	if (qopt->peakrate.rate) {
-		q->P_tab = qdisc_get_rtab(&qopt->rate, tb[TCA_TBF_PTAB-1]);
-		if (q->P_tab == NULL) {
-			MOD_DEC_USE_COUNT;
-			qdisc_put_rtab(q->R_tab);
-			return -EINVAL;
+		if (qopt->peakrate.rate > qopt->rate.rate)
+			ptab = qdisc_get_rtab(&qopt->peakrate, tb[TCA_TBF_PTAB-1]);
+		if (ptab == NULL)
+			goto done;
+	}
+
+	max_size = psched_mtu(sch->dev);
+	if (ptab) {
+		int n = max_size>>qopt->peakrate.cell_log;
+		while (n>0 && ptab->data[n-1] > qopt->mtu) {
+			max_size -= (1<<qopt->peakrate.cell_log);
+			n--;
 		}
 	}
+	if (rtab->data[max_size>>qopt->rate.cell_log] > qopt->buffer)
+		goto done;
 
-	PSCHED_GET_TIME(q->t_c);
-	init_timer(&q->wd_timer);
-	q->wd_timer.function = tbf_watchdog;
-	q->wd_timer.data = (unsigned long)sch;
+	start_bh_atomic();
 	q->limit = qopt->limit;
 	q->mtu = qopt->mtu;
-	if (q->mtu == 0)
-		q->mtu = psched_mtu(sch->dev);
+	q->max_size = max_size;
 	q->buffer = qopt->buffer;
 	q->tokens = q->buffer;
 	q->ptokens = q->mtu;
-	return 0;
+	rtab = xchg(&q->R_tab, rtab);
+	ptab = xchg(&q->P_tab, ptab);
+	end_bh_atomic();
+	err = 0;
+done:
+	if (rtab)
+		qdisc_put_rtab(rtab);
+	if (ptab)
+		qdisc_put_rtab(ptab);
+	return err;
+}
+
+static int tbf_init(struct Qdisc* sch, struct rtattr *opt)
+{
+	int err;
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	
+	if (opt == NULL)
+		return -EINVAL;
+	
+	MOD_INC_USE_COUNT;
+	
+	PSCHED_GET_TIME(q->t_c);
+	init_timer(&q->wd_timer);
+	q->wd_timer.function = tbf_watchdog;
+	q->wd_timer.data = (unsigned long)sch;
+	
+	if ((err = tbf_change(sch, opt)) != 0) {
+		MOD_DEC_USE_COUNT;
+	}
+	return err;
 }
 
 static void tbf_destroy(struct Qdisc *sch)
@@ -328,10 +369,10 @@
 	unsigned char	 *b = skb->tail;
 	struct rtattr *rta;
 	struct tc_tbf_qopt opt;
-
+	
 	rta = (struct rtattr*)b;
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
-
+	
 	opt.limit = q->limit;
 	opt.rate = q->R_tab->rate;
 	if (q->P_tab)
@@ -366,6 +407,7 @@
 	tbf_init,
 	tbf_reset,
 	tbf_destroy,
+	tbf_change,
 
 #ifdef CONFIG_RTNETLINK
 	tbf_dump,
diff -u --recursive --new-file v2.2.3/linux/net/sched/sch_teql.c linux/net/sched/sch_teql.c
--- v2.2.3/linux/net/sched/sch_teql.c	Tue Jun 23 10:01:31 1998
+++ linux/net/sched/sch_teql.c	Sun Mar 21 07:22:00 1999
@@ -444,6 +444,7 @@
 	teql_qdisc_init,
 	teql_reset,
 	teql_destroy,
+	NULL,
 },};
 
 
diff -u --recursive --new-file v2.2.3/linux/net/socket.c linux/net/socket.c
--- v2.2.3/linux/net/socket.c	Tue Jan 19 11:32:53 1999
+++ linux/net/socket.c	Tue Mar  9 09:20:19 1999
@@ -41,6 +41,7 @@
  *		Kevin Buhr	:	Fixed the dumb errors in the above.
  *		Andi Kleen	:	Some small cleanups, optimizations,
  *					and fixed a copy_from_user() bug.
+ *		Tigran Aivazian	:	sys_send(args) calls sys_sendto(args, NULL, 0)
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -929,40 +930,6 @@
 }
 
 /*
- *	Send a datagram down a socket. The datagram as with write() is
- *	in user space. We check it can be read.
- */
-
-asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags)
-{
-	struct socket *sock;
-	int err;
-	struct msghdr msg;
-	struct iovec iov;
-
-	lock_kernel();
-	sock = sockfd_lookup(fd, &err);
-	if (sock) {
-		iov.iov_base=buff;
-		iov.iov_len=len;
-		msg.msg_name=NULL;
-		msg.msg_namelen=0;
-		msg.msg_iov=&iov;
-		msg.msg_iovlen=1;
-		msg.msg_control=NULL;
-		msg.msg_controllen=0;
-		if (sock->file->f_flags & O_NONBLOCK)
-			flags |= MSG_DONTWAIT;
-		msg.msg_flags = flags;
-		err = sock_sendmsg(sock, &msg, len);
-
-		sockfd_put(sock);
-	}
-	unlock_kernel();
-	return err;
-}
-
-/*
  *	Send a datagram to a given address. We move the address into kernel
  *	space and check the user space data area is readable before invoking
  *	the protocol.
@@ -1008,6 +975,14 @@
 	return err;
 }
 
+/*
+ *	Send a datagram down a socket. 
+ */
+
+asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags)
+{
+	return sys_sendto(fd, buff, len, flags, NULL, 0);
+}
 
 /*
  *	Receive a frame from the socket and optionally record the address of the 
@@ -1059,7 +1034,7 @@
 
 asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags)
 {
-	return sys_recvfrom(fd,ubuf,size,flags, NULL, NULL);
+	return sys_recvfrom(fd, ubuf, size, flags, NULL, NULL);
 }
 
 /*
diff -u --recursive --new-file v2.2.3/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- v2.2.3/linux/net/sunrpc/svcsock.c	Tue Feb 23 15:21:36 1999
+++ linux/net/sunrpc/svcsock.c	Mon Mar 15 16:12:46 1999
@@ -611,7 +611,7 @@
 		unsigned long	want = 4 - svsk->sk_tcplen;
 		struct iovec	iov;
 
-		iov.iov_base = ((u32 *) &svsk->sk_reclen) + svsk->sk_tcplen;
+		iov.iov_base = ((char *) &svsk->sk_reclen) + svsk->sk_tcplen;
 		iov.iov_len  = want;
 		if ((len = svc_recvfrom(rqstp, &iov, 1, want)) < 0)
 			goto error;
@@ -621,11 +621,11 @@
 		if (!(svsk->sk_reclen & 0x80000000)) {
 			/* FIXME: shutdown socket */
 			printk(KERN_NOTICE "RPC: bad TCP reclen %08lx",
-					(unsigned long) svsk->sk_reclen);
+			       (unsigned long) svsk->sk_reclen);
 			return -EIO;
 		}
 		svsk->sk_reclen &= 0x7fffffff;
-		dprintk("svc: TCP record, %ld bytes\n", svsk->sk_reclen);
+		dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen);
 	}
 
 	/* Check whether enough data is available */
@@ -634,8 +634,8 @@
 		goto error;
 
 	if (len < svsk->sk_reclen) {
-		dprintk("svc: incomplete TCP record (%d of %ld)\n",
-				len, svsk->sk_reclen);
+		dprintk("svc: incomplete TCP record (%d of %d)\n",
+			len, svsk->sk_reclen);
 		svc_sock_received(svsk, ready);
 		len = -EAGAIN;	/* record not complete */
 	}
diff -u --recursive --new-file v2.2.3/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.2.3/linux/net/unix/af_unix.c	Tue Jan 19 11:32:53 1999
+++ linux/net/unix/af_unix.c	Sun Mar 21 07:22:00 1999
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.73 1999/01/15 06:55:48 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.74 1999/03/21 05:23:16 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -33,6 +33,16 @@
  *					Lots of bug fixes.
  *	     Alexey Kuznetosv	:	Repaired (I hope) bugs introduces
  *					by above two patches.
+ *	     Andrea Arcangeli	:	If possible we block in connect(2)
+ *					if the max backlog of the listen socket
+ *					is been reached. This won't break
+ *					old apps and it will avoid huge amount
+ *					of socks hashed (this for unix_gc()
+ *					performances reasons).
+ *					Security fix that limits the max
+ *					number of socks to 2*max_files and
+ *					the number of skb queueable in the
+ *					dgram receiver.
  *
  * Known differences from reference BSD that was tested:
  *
@@ -100,8 +110,12 @@
 
 int sysctl_unix_delete_delay = HZ;
 int sysctl_unix_destroy_delay = 10*HZ;
+int sysctl_unix_max_dgram_qlen = 10;
 
 unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
+static atomic_t unix_nr_socks = ATOMIC_INIT(0);
+static struct wait_queue * unix_ack_wqueue = NULL;
+static struct wait_queue * unix_dgram_wqueue = NULL;
 
 #define unix_sockets_unbound	(unix_socket_table[UNIX_HASH_SIZE])
 
@@ -263,6 +277,8 @@
 	unix_socket *sk=(unix_socket *)data;
 	if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0)
 	{
+		atomic_dec(&unix_nr_socks);
+
 		sk_free(sk);
 	
 		/* socket destroyed, decrement count		      */
@@ -295,6 +311,11 @@
 	sk->dead=1;
 	sk->socket = NULL;
 
+	if (sk->state == TCP_LISTEN)
+		wake_up_interruptible(&unix_ack_wqueue);
+	if (sk->type == SOCK_DGRAM)
+		wake_up_interruptible(&unix_dgram_wqueue);
+
 	skpair=unix_peer(sk);
 
 	if (skpair!=NULL)
@@ -347,6 +368,8 @@
 	
 	if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0)
 	{
+		atomic_dec(&unix_nr_socks);
+		
 		sk_free(sk);
 	
 		/* socket destroyed, decrement count		      */
@@ -371,6 +394,8 @@
 		return -EOPNOTSUPP;		/* Only stream sockets accept */
 	if (!sk->protinfo.af_unix.addr)
 		return -EINVAL;			/* No listens on an unbound socket */
+	if ((unsigned) backlog > SOMAXCONN)
+		backlog = SOMAXCONN;
 	sk->max_ack_backlog=backlog;
 	sk->state=TCP_LISTEN;
 	sock->flags |= SO_ACCEPTCON;
@@ -388,6 +413,9 @@
 {
 	struct sock *sk;
 
+	if (atomic_read(&unix_nr_socks) >= 2*max_files)
+		return NULL;
+
 	MOD_INC_USE_COUNT;
 	sk = sk_alloc(PF_UNIX, GFP_KERNEL, 1);
 	if (!sk) {
@@ -395,6 +423,8 @@
 		return NULL;
 	}
 
+	atomic_inc(&unix_nr_socks);
+
 	sock_init_data(sock,sk);
 
 	if (stream)
@@ -673,9 +703,25 @@
 	   we will have to recheck all again in any case.
 	 */
 
+restart:
 	/*  Find listening sock */
 	other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
 
+	if (!other)
+		return -ECONNREFUSED;
+
+	while (other->ack_backlog >= other->max_ack_backlog) {
+		unix_unlock(other);
+		if (other->dead || other->state != TCP_LISTEN)
+			return -ECONNREFUSED;
+		if (flags & O_NONBLOCK)
+			return -EAGAIN;
+		interruptible_sleep_on(&unix_ack_wqueue);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		goto restart;
+        }
+
 	/* create new sock for complete connection */
 	newsk = unix_create1(NULL, 1);
 
@@ -704,7 +750,7 @@
 
 	/* Check that listener is in valid state. */
 	err = -ECONNREFUSED;
-	if (other == NULL || other->dead || other->state != TCP_LISTEN)
+	if (other->dead || other->state != TCP_LISTEN)
 		goto out;
 
 	err = -ENOMEM;
@@ -815,11 +861,10 @@
 			continue;
 		}
 		tsk = skb->sk;
-		sk->ack_backlog--;
+		if (sk->max_ack_backlog == sk->ack_backlog--)
+			wake_up_interruptible(&unix_ack_wqueue);
 		kfree_skb(skb);
-		if (!tsk->dead) 
-			break;
-		unix_release_sock(tsk);
+		break;
 	}
 
 
@@ -947,6 +992,7 @@
 		 *	Check with 1003.1g - what should
 		 *	datagram error
 		 */
+	dead:
 		unix_unlock(other);
 		unix_peer(sk)=NULL;
 		other = NULL;
@@ -964,6 +1010,29 @@
 			goto out_unlock;
 	}
 
+	while (skb_queue_len(&other->receive_queue) >=
+	       sysctl_unix_max_dgram_qlen)
+	{
+		if (sock->file->f_flags & O_NONBLOCK)
+		{
+			err = -EAGAIN;
+			goto out_unlock;
+		}
+		interruptible_sleep_on(&unix_dgram_wqueue);
+		if (other->dead)
+			goto dead;
+		if (sk->shutdown & SEND_SHUTDOWN)
+		{
+			err = -EPIPE;
+			goto out_unlock;
+		}
+		if (signal_pending(current))
+		{
+			err = -ERESTARTSYS;
+			goto out_unlock;
+		}
+	}
+
 	skb_queue_tail(&other->receive_queue, skb);
 	other->data_ready(other,len);
 	
@@ -1125,6 +1194,13 @@
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
 	if (!skb)
 		goto out;
+
+	/*
+	 * sysctl_unix_max_dgram_qlen may change over the time we blocked
+	 * in the waitqueue so we must wakeup every time we shrink the
+	 * receiver queue. -arca
+	 */
+	wake_up_interruptible(&unix_dgram_wqueue);
 
 	if (msg->msg_name)
 	{
diff -u --recursive --new-file v2.2.3/linux/net/unix/sysctl_net_unix.c linux/net/unix/sysctl_net_unix.c
--- v2.2.3/linux/net/unix/sysctl_net_unix.c	Sun Nov 30 14:00:40 1997
+++ linux/net/unix/sysctl_net_unix.c	Sun Mar 21 07:22:00 1999
@@ -19,6 +19,7 @@
 
 extern int sysctl_unix_destroy_delay;
 extern int sysctl_unix_delete_delay;
+extern int sysctl_unix_max_dgram_qlen;
 
 ctl_table unix_table[] = {
 	{NET_UNIX_DESTROY_DELAY, "destroy_delay",
@@ -26,6 +27,9 @@
 	 &proc_dointvec_jiffies},
 	{NET_UNIX_DELETE_DELAY, "delete_delay",
 	&sysctl_unix_delete_delay, sizeof(int), 0644, NULL, 
+	 &proc_dointvec_jiffies},
+	{NET_UNIX_MAX_DGRAM_QLEN, "max_dgram_qlen",
+	&sysctl_unix_max_dgram_qlen, sizeof(int), 0600, NULL, 
 	 &proc_dointvec_jiffies},
 	{0}
 };
diff -u --recursive --new-file v2.2.3/linux/scripts/ksymoops/README linux/scripts/ksymoops/README
--- v2.2.3/linux/scripts/ksymoops/README	Thu Jan  7 15:11:42 1999
+++ linux/scripts/ksymoops/README	Mon Mar 15 16:12:14 1999
@@ -110,8 +110,8 @@
   * Prints all relevant lines for a complete Oops report.
   * Tries to provide output no matter how bad the input is.  The level of
      progress and error reporting is aimed at beginners.
-  * Handles i386, alpha, sparc, m68k.  It is a lot easier to extend to
-    other architectures (patches and/or sample data gratefully accepted).
+  * Handles i386, alpha, sparc, sparc64, m68k.  It is a lot easier to extend
+    to other architectures (patches and/or sample data gratefully accepted).
   * Handles all Oops in the input file(s).
 
 
@@ -193,6 +193,7 @@
           m68k code derived from ksymoops.cc and reading traps.c, untested.
 	  MIPS tested.
 	  Sparc tested.
+	  Sparc64 tested.
 	  Alpha tested.
 	  ARM tested.
 
@@ -337,6 +338,9 @@
 
   * The sparc PC line starts with PSR and PC is the second hex value, not
     bracketed.
+
+  * The sparc64 TPC line starts with TSTATE and TPC is the second hex value,
+    not bracketed.
 
   * A call trace line is identified by 'Call Trace:' followed by at least
     one white space.  Or it is a line starting with a bracketed address,
diff -u --recursive --new-file v2.2.3/linux/scripts/ksymoops/oops.c linux/scripts/ksymoops/oops.c
--- v2.2.3/linux/scripts/ksymoops/oops.c	Wed Mar 10 15:29:53 1999
+++ linux/scripts/ksymoops/oops.c	Mon Mar 15 16:12:14 1999
@@ -5,6 +5,10 @@
 
 	Copyright Keith Owens <kaos@ocs.com.au>.
 	Released under the GNU Public Licence, Version 2.
+	
+	Sun Jan  7 12:56:12 CET 1999
+	Added SPARC64 support and some SPARC hacks by "Jakub Jelinek"
+	<jj@ultra.linux.cz>
 
 	Mon Jan  4 08:47:55 EST 1999
 	Version 0.6d
@@ -44,6 +48,20 @@
 #include <string.h>
 #include <unistd.h>
 
+enum oops_arch {
+	OOPS_NOARCH,
+	OOPS_I386,
+	OOPS_SPARC,
+	OOPS_SPARC64,
+	OOPS_ARM,
+	OOPS_ALPHA,
+	OOPS_MIPS,
+	OOPS_PPC,
+	OOPS_M68K
+} oops_arch;
+
+char *eip_names[] = { "IP", "EIP", "PC", "PC", "PC", "PC", "PC", "NIP", "PC" };
+
 /* Error detected by bfd */
 static void Oops_bfd_perror(const char *msg)
 {
@@ -187,11 +205,13 @@
 	static char const options[] = "-dhf ";
 	static char const procname[] = "Oops_objdump";
 
-	cmd = malloc(strlen(path_objdump)+1+strlen(options)+strlen(file)+1);
+	cmd = malloc(strlen(path_objdump)+1+13+strlen(options)+strlen(file)+1);
 	if (!cmd)
 		malloc_error(procname);
 	strcpy(cmd, path_objdump);
 	strcat(cmd, " ");
+	if (oops_arch == OOPS_SPARC64)
+		strcat(cmd, "-m sparc:v9a ");
 	strcat(cmd, options);
 	strcat(cmd, file);
 	if (debug > 1)
@@ -205,9 +225,9 @@
 static void Oops_decode_one(SYMBOL_SET *ss, const char *line, elf_addr_t eip,
 			    int adjust)
 {
-	int i;
+	int i, j;
 	elf_addr_t address, eip_relative;
-	char *line2, *map, **string = NULL;
+	char *line2, *map, **string = NULL, *p;
 	static regex_t     re_Oops_objdump;
 	static regmatch_t *re_Oops_objdump_pmatch;
 	static char const procname[] = "Oops_decode_one";
@@ -233,7 +253,9 @@
 			"( <_EIP[^>]*>)?"				/* 2 */
 			":"
 			"("						/* 3 */
-			".* +<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$"	/* 4 */
+			".* +"
+			"(0?x?[0-9a-fA-F]+ +)"				/* 4 */
+			"<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$"		/* 5 */
 			")?"
 			".*"
 			,
@@ -261,34 +283,58 @@
 		address = 0;
 	}
 	address += eip + adjust;
-	if (string[4]) {
+	if (string[5]) {
 		/* EIP relative data to be adjusted */
 		errno = 0;
-		eip_relative = strtoul(string[4], NULL, 16);
+		eip_relative = strtoul(string[5], NULL, 16);
 		if (errno) {
-			fprintf(stderr,
-				"%s Invalid hex value in objdump line, "
-				"treated as zero - '%s'\n"
-				"  objdump line '%s'\n",
-				procname, string[4], line);
-			perror(" ");
-			++errors;
-			eip_relative = 0;
+			/* Try strtoull also, e.g. sparc binutils print <_PC+0xfffffffffffffd58> */
+			errno = 0;
+			eip_relative = strtoull(string[5], NULL, 16);
+			if (errno) {
+				fprintf(stderr,
+					"%s Invalid hex value in objdump line, "
+					"treated as zero - '%s'\n"
+					"  objdump line '%s'\n",
+					procname, string[5], line);
+				perror(" ");
+				++errors;
+				eip_relative = 0;
+			}
 		}
 		eip_relative += eip + adjust;
 		map = map_address(&ss_merged, eip_relative);
 		/* new text is original line, eip_relative in hex, map text */
-		i = strlen(line)+1+2*sizeof(eip_relative)+1+strlen(map)+1;
-		line2 = malloc(i);
+		j = strlen(line);
+		if (string[4])
+			j = re_Oops_objdump_pmatch[4].rm_so;
+		i = j+1+2*sizeof(eip_relative)+1+strlen(map)+1;
+		line2 = malloc(i + 5);
 		if (!line2)
 			malloc_error(procname);
-		snprintf(line2, i, "%s %s %s",
-			line, format_address(eip_relative), map);
-		add_symbol_n(ss, address, 'C', 1, line2);
-		free(line2);
+		snprintf(line2, i, "%.*s %s %s",
+			j, line, format_address(eip_relative), map);
+	} else {
+		line2 = malloc(strlen(line) + 6);
+		if (!line2)
+			malloc_error(procname);
+		strcpy(line2, line);
 	}
-	else
-		add_symbol_n(ss, address, 'C', 1, line);	/* as is */
+	if (oops_arch != OOPS_I386) {
+		p = line2;
+		while ((p = strstr(p, "_EIP"))) {
+			int l = strlen(eip_names[oops_arch]);
+			memcpy(p + 1, eip_names[oops_arch], l);
+			if (l < 3)
+				strcpy(p + 1 + l, p + 4);
+			p += 1 + l;
+		}
+	}
+	if (address == eip)
+		strcat(line2, " <===");			/* This makes it easier to locate visually the
+							   offending instruction */
+	add_symbol_n(ss, address, 'C', 1, line2);	/* as is */
+	free(line2);
 	re_strings_free(&re_Oops_objdump, &string);
 }
 
@@ -435,6 +481,8 @@
 	int i;
 	static regex_t     re_Oops_eip_sparc;
 	static regmatch_t *re_Oops_eip_sparc_pmatch;
+	static regex_t     re_Oops_eip_sparc64;
+	static regmatch_t *re_Oops_eip_sparc64_pmatch;
 	static regex_t     re_Oops_eip_ppc;
 	static regmatch_t *re_Oops_eip_ppc_pmatch;
 	static regex_t     re_Oops_eip_mips;
@@ -442,7 +490,7 @@
 	static regex_t     re_Oops_eip_other;
 	static regmatch_t *re_Oops_eip_other_pmatch;
 	static const char procname[] = "Oops_eip";
-
+	
 	/* Oops 'EIP:' line for sparc, actually PSR followed by PC */
 	re_compile(&re_Oops_eip_sparc,
 			"^PSR: [0-9a-fA-F]+ PC: " UNBRACKETED_ADDRESS,
@@ -458,9 +506,28 @@
 			procname);
 		re_strings(&re_Oops_eip_sparc, line, re_Oops_eip_sparc_pmatch,
 			string);
+		oops_arch = OOPS_SPARC;
 		return((*string)[re_Oops_eip_sparc.re_nsub]);
 	}
 
+	/* Oops 'EIP:' line for sparc64, actually TSTATE followed by TPC */
+	re_compile(&re_Oops_eip_sparc64,
+			"^TSTATE: [0-9a-fA-F]{16} TPC: " UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_sparc64_pmatch);
+
+	re_string_check(re_Oops_eip_sparc64.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_eip_sparc64, line, re_Oops_eip_sparc64.re_nsub+1,
+		re_Oops_eip_sparc64_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec sparc64 %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_eip_sparc64, line, re_Oops_eip_sparc64_pmatch,
+			string);
+		oops_arch = OOPS_SPARC64;
+		return((*string)[re_Oops_eip_sparc64.re_nsub]);
+	}
+
 	/* Oops 'EIP:' line for PPC, all over the place */
 	re_compile(&re_Oops_eip_ppc,
 			"("
@@ -482,6 +549,7 @@
 			procname);
 		re_strings(&re_Oops_eip_ppc, line, re_Oops_eip_ppc_pmatch,
 			string);
+		oops_arch = OOPS_PPC;
 		return((*string)[re_Oops_eip_ppc.re_nsub]);
 	}
 
@@ -503,6 +571,7 @@
 			procname);
 		re_strings(&re_Oops_eip_mips, line, re_Oops_eip_mips_pmatch,
 			string);
+		oops_arch = OOPS_MIPS;
 		return((*string)[re_Oops_eip_mips.re_nsub]);
 	}
 
@@ -527,6 +596,7 @@
 			procname);
 		re_strings(&re_Oops_eip_other, line, re_Oops_eip_other_pmatch,
 			string);
+		oops_arch = OOPS_I386;
 		return((*string)[re_Oops_eip_other.re_nsub]);
 	}
 	return(NULL);
@@ -536,6 +606,7 @@
 static void Oops_set_eip(const char *value, elf_addr_t *eip, SYMBOL_SET *ss)
 {
 	static const char procname[] = "Oops_set_eip";
+	char buf[10];
 	errno = 0;
 	*eip = strtoul(value, NULL, 16);
 	if (errno) {
@@ -546,7 +617,8 @@
 		++errors;
 		*eip = 0;
 	}
-	add_symbol_n(ss, *eip, 'E', 1, ">>EIP:");
+	sprintf(buf, ">>%s:", eip_names[oops_arch]);
+	add_symbol_n(ss, *eip, 'E', 1, buf);
 }
 
 /* Look for the MIPS ra line, returns start of the relevant hex value */
@@ -597,6 +669,80 @@
 	add_symbol_n(ss, ra, 'R', 1, ">>RA :");
 }
 
+/* Look for the SPARC o7/i7 registers line, returns start of the relevant hex value */
+static char *Oops_oi7(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_oi7;
+	static regmatch_t *re_Oops_oi7_pmatch;
+	static const char procname[] = "Oops_oi7";
+
+	re_compile(&re_Oops_oi7,
+			"^[io][04]: [0-9a-fA-F iosp:]+ ([io]7|ret_pc): "
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_oi7_pmatch);
+
+	re_string_check(re_Oops_oi7.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_oi7, line, re_Oops_oi7.re_nsub+1,
+		re_Oops_oi7_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_oi7, line, re_Oops_oi7_pmatch,
+			string);
+		return((*string)[re_Oops_oi7.re_nsub]);
+	}
+	return(NULL);
+}
+
+/* Set the SPARC o7/i7 from the oi7 line */
+static void Oops_set_oi7(const char *value, char ***string, SYMBOL_SET *ss)
+{
+	static const char procname[] = "Oops_set_oi7";
+	elf_addr_t oi7;
+	int o7 = 1;
+	errno = 0;
+	oi7 = strtoul(value, NULL, 16);
+	if ((*string)[1] && !strcmp((*string)[1], "i7"))
+		o7 = 0;
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in oi7 line, ignored - '%s'\n",
+			procname, value);
+		perror(" ");
+		++errors;
+		oi7 = 0;
+	}
+	add_symbol_n(ss, oi7, 'O', 1, o7 ? ">>O7:" : ">>I7:");
+}
+
+/* Look for the SPARC register dump lines end */
+static int Oops_sparc_regdump(const char *line)
+{
+	int i;
+	static regex_t     re_Oops_sparc_regdump;
+	static regmatch_t *re_Oops_sparc_regdump_pmatch;
+	static const char procname[] = "Oops_sparc_regdump";
+
+	re_compile(&re_Oops_sparc_regdump,
+		       "^(i[04]: "
+			"|Instruction DUMP: "
+			"|Caller\\["
+			")",
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_sparc_regdump_pmatch);
+
+	i = regexec(&re_Oops_sparc_regdump, line, re_Oops_sparc_regdump.re_nsub+1,
+		re_Oops_sparc_regdump_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0)
+		return 1;
+	return 0;
+}
+
+
 /* Look for the Trace multilines :(.  Returns start of addresses. */
 static const char *Oops_trace(const char *line, char ***string, int string_max)
 {
@@ -619,6 +765,8 @@
 	/* ppc */	"|(Call backtrace:)"			/*  6 */
 	/* ppc */	"|(" UNBRACKETED_ADDRESS ")"		/* 7,8*/
 	/* ARM */	"|(Function entered at (" BRACKETED_ADDRESS "))"	/* 9,10,11 */
+	/* sparc */
+	/* sparc64 */	"|(Caller\\[" UNBRACKETED_ADDRESS "\\])"/*12,13*/
 			")",
 		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
 		&re_Oops_trace_pmatch);
@@ -645,8 +793,11 @@
 		else if (MATCHED(10)){
 			trace_line = 1;		/* ARM */
 			start = line + re_Oops_trace_pmatch[10].rm_so;
-		}
-		else
+		} else if (MATCHED(12)) {
+			trace_line = 0;		/* sparc, sparc64 */
+			start = line + re_Oops_trace_pmatch[13].rm_so;
+			return start;
+		} else
 			trace_line = 0;
 	}
 	else
@@ -819,21 +970,25 @@
 	/* m68k */	"|(baddr=)"
 	/* any other m68K lines to print? */
 
-	/* sparc */	"|(Bad unaligned kernel)"
-	/* sparc */	"|(Forwarding unaligned exception)"
-	/* sparc */	"|(: unhandled unaligned exception)"
-	/* sparc */	"|(<sc)"
-	/* sparc */	"|(pc *=)"
-	/* sparc */	"|(r[0-9]+ *=)"
-	/* sparc */	"|(gp *=)"
-	/* any other sparc lines to print? */
-
-	/* alpha */	"|(tsk->)"
-	/* alpha */	"|(PSR: )"
-	/* alpha */	"|([goli]0: )"
-	/* alpha */	"|(Instruction DUMP: )"
+	/* alpha */	"|(Bad unaligned kernel)"
+	/* alpha */	"|(Forwarding unaligned exception)"
+	/* alpha */	"|(: unhandled unaligned exception)"
+	/* alpha */	"|(<sc)"
+	/* alpha */	"|(pc *=)"
+	/* alpha */	"|(r[0-9]+ *=)"
+	/* alpha */	"|(gp *=)"
 	/* any other alpha lines to print? */
 
+	/* sparc */	"|(tsk->)"
+	/* sparc */	"|(PSR: )"
+	/* sparc */	"|([goli][04]: )"
+	/* sparc */	"|(Instruction DUMP: )"
+	/* sparc */	"|(Caller\\[)"
+	/* any other sparc lines to print? */
+	
+	/* sparc64 */	"|(TSTATE: )"
+	/* any other sparc64 lines to print? */
+
 	/* ppc */	"|(MSR: )"
 	/* ppc */	"|(TASK = )"
 	/* ppc */	"|(last math )"
@@ -908,13 +1063,13 @@
 	/* various */	"|(Aiee)"      /* anywhere in text is a bad sign (TM) */
 	/* various */	"|(die_if_kernel)"	/* ditto */
 
-	/* sparc */	"|(\\([0-9]\\): Oops )"
-	/* sparc */	"|(: memory violation)"
-	/* sparc */	"|(: Exception at)"
-	/* sparc */	"|(: Arithmetic fault)"
-	/* sparc */	"|(: Instruction fault)"
-	/* sparc */	"|(: arithmetic trap)"
-	/* sparc */	"|(: unaligned trap)"
+	/* alpha */	"|(\\([0-9]\\): Oops )"
+	/* alpha */	"|(: memory violation)"
+	/* alpha */	"|(: Exception at)"
+	/* alpha */	"|(: Arithmetic fault)"
+	/* alpha */	"|(: Instruction fault)"
+	/* alpha */	"|(: arithmetic trap)"
+	/* alpha */	"|(: unaligned trap)"
 
 	/* sparc      die_if_kernel has no fixed text, identify by (pid): text.
 	 *            Somebody has been playful with the texts.
@@ -1114,8 +1269,9 @@
 {
 	char *line = NULL, **string = NULL;
 	const char *start, *text;
-	int i, size = 0, lineno = 0, lastprint = 0;
+	int i, size = 0, lineno = 0, lastprint = 0, print = 0;
 	elf_addr_t eip = 0;
+	int sparc_regdump = 0;
 	FILE *f;
 	SYMBOL_SET ss_format;
 	static const char procname[] = "Oops_read";
@@ -1138,7 +1294,19 @@
 				fprintf(stderr,
 					"DEBUG: %s - %s\n", procname, line);
 			++lineno;
-			if (Oops_print(line, &text, &string, MAX_STRINGS)) {
+			print = Oops_print(line, &text, &string, MAX_STRINGS);
+			if (Oops_sparc_regdump (text)) {
+				sparc_regdump = 1;
+			} else {
+				if ((oops_arch == OOPS_SPARC || 
+				     oops_arch == OOPS_SPARC64) &&
+				    sparc_regdump && ss_format.used) {
+					Oops_format(&ss_format);
+					ss_free(&ss_format);
+				}
+				sparc_regdump = 0;
+			}
+			if (print) {
 				puts(line);
 				lastprint = lineno;
 				if ((start = Oops_eip(text,
@@ -1147,6 +1315,10 @@
 				if ((start = Oops_ra(text,
 					&string, MAX_STRINGS)))
 					Oops_set_ra(start, &ss_format);
+				if ((start = Oops_oi7(text,
+					&string, MAX_STRINGS)))
+					Oops_set_oi7(start, &string, 
+						&ss_format);
 				if ((start = Oops_trace(text,
 					&string, MAX_STRINGS)))
 					Oops_trace_line(text, start,
@@ -1178,10 +1350,13 @@
 			}
 		}
 		if (ss_format.used) {
-			fprintf(stderr,
-				"Warning, Code line not seen, dumping "
-				"what data is available\n");
-			++warnings;
+			if ((oops_arch != OOPS_SPARC &&
+			     oops_arch != OOPS_SPARC64) || !sparc_regdump) {
+				fprintf(stderr,
+					"Warning, Code line not seen, dumping "
+					"what data is available\n");
+				++warnings;
+			}
 			Oops_format(&ss_format);
 			ss_free(&ss_format);
 			if (one_shot)
diff -u --recursive --new-file v2.2.3/linux/scripts/ver_linux linux/scripts/ver_linux
--- v2.2.3/linux/scripts/ver_linux	Tue Feb 23 15:21:36 1999
+++ linux/scripts/ver_linux	Tue Mar 16 14:21:52 1999
@@ -8,9 +8,7 @@
 echo '-- Versions installed: (if some fields are empty or looks'
 echo '-- unusual then possibly you have very old versions)'
 uname -a
-insmod -V 1>/tmp/ver_linux.tmp 2>>/tmp/ver_linux.tmp
-awk 'NR==1{print "Kernel modules        ",$NF}' /tmp/ver_linux.tmp
-rm -f /tmp/ver_linux.tmp
+insmod -V  2>&1 | awk 'NR==1 {print "Kernel modules        ",$NF}'
 echo "Gnu C                 " `gcc --version`
 ld -v 2>&1 | awk -F\) '{print $1}' | awk \
       '/BFD/{print "Binutils              ",$NF}'
@@ -31,3 +29,5 @@
  (NR==2 && $1 ~ /console-tools/) {print "Console-tools         ",$3; done=1}
  END {if (!done) print "Kbd                   ",ver}'
 expr --v | awk '{print "Sh-utils              ", $NF}'
+X=`cat /proc/modules | sed -e "s/ .*$//"`
+echo "Modules Loaded         "$X
