diff -u --recursive --new-file v1.3.50/linux/CREDITS linux/CREDITS
--- v1.3.50/linux/CREDITS	Thu Dec 21 08:53:30 1995
+++ linux/CREDITS	Tue Dec 26 06:02:59 1995
@@ -893,6 +893,15 @@
 S: Arlington, Massachusetts 02174
 S: USA
 
+N: Craig Small
+E: csmall@triode.apana.org.au
+E: vk2xlz@gonzo.vk2xlz.ampr.org (packet radio)
+S: 10 Stockalls Place
+S: Minto, NSW, 2566
+S: Australia
+D: Gracilis PackeTwin device driver
+D: RSPF daemon 
+
 N: Chris Smith
 E: csmith@convex.com
 D: HPFS filesystem
diff -u --recursive --new-file v1.3.50/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.50/linux/Documentation/Configure.help	Tue Dec 26 04:45:33 1995
+++ linux/Documentation/Configure.help	Tue Dec 26 06:03:00 1995
@@ -439,7 +439,8 @@
   encapsulating protocol. This particular tunneling driver implements
   encapsulation of IP within IP, which sounds kind of pointless, but
   can be useful if you want to make your (or some other) machine
-  appear on a different network than it physically is. Enabling this
+  appear on a different network than it physically is, or to use the 
+  mobile IP facilities (which effectively are doing that). Enabling this
   option will produce two modules ( = code which can be inserted in
   and removed from the running kernel whenever you want), one
   encapsulator and one decapsulator.  This is still alpha code, which
@@ -488,7 +489,8 @@
 CONFIG_INET_PCTCP
   If you have been having difficulties telneting to your Linux machine
   from a DOS system that uses (broken) PC/TCP networking software, try
-  enabling this option.  Everyone else says N.
+  enabling this option.  Everyone else says N. As of later 1.3.x kernels
+  nobody should need this option. Please report if it solves problems.
 
 Reverse ARP
 CONFIG_INET_RARP
@@ -509,7 +511,7 @@
   by Ethernet segments only, as this option optimizes network access
   for this special case. If there are other connections, e.g. SLIP
   links, between machines of your IP network, say N.  If in doubt, say
-  Y.
+  N. The PATH mtu discovery facility will cover most cases anyway.
 
 Disable Path MTU Discovery (normally enabled)
 CONFIG_NO_PATH_MTU_DISCOVERY
@@ -523,10 +525,10 @@
 CONFIG_TCP_NAGLE_OFF
   The NAGLE algorithm works by requiring an acknowledgment before
   sending small IP frames (= packets).  This keeps tiny telnet and
-  rlogin packets from congesting Wide Area Networks.  You may wish to
-  disable it if you run your X-server from across the network, or if
-  multiple byte key sequences are delayed. Most people strongly
-  recommend to say N here, though, thereby leaving NAGLE enabled.
+  rlogin packets from congesting Wide Area Networks.  Most people strongly
+  recommend to say N here, though, thereby leaving NAGLE enabled. Those
+  programs that benefit by disabling the facility should do it on a per
+  connection basis themselves anyway.
 
 IP: Drop source routed frames
 CONFIG_IP_NOSR
@@ -571,15 +573,15 @@
   the programs lynx, netscape or Mosaic). This driver would enlarge
   your kernel by about 5 kB. Unless you have Novell computers on your
   local network, say N.
+  BTW: Although it still doesn't work with this release of the kernel you 
+  can also find ncpfs (a free Novell client) on linux01.gwdg.de.
 
 Appletalk DDP
 CONFIG_ATALK
   Appletalk is the way Apple computers speak to each other on an
   Ethernet (Apple calls it EtherTalk) network. If your linux box is
   connected to such a network and you want to join the conversation,
-  say Y. You would have to give "appletalk" as the address family
-  argument to ifconfig ("man ifconfig") in order to do this. You will
-  also probably want to use the netatalk package so that your Linux
+  say Y. You will need to use the netatalk package so that your Linux
   box can act as a print and file server for macs as well as access
   appletalk printers. Check out
   http://www.cs.dartmouth.edu/~flowerpt/projects/linux-netatalk/ on
@@ -638,7 +640,7 @@
   This driver allows for two-way communication between certain parts
   of the kernel or modules and user processes; the user processes are
   able to read from and write to character special files in the /dev
-  directory having major mode 18. So far, the kernel uses it to
+  directory having major mode 36. So far, the kernel uses it to
   publish some network related information if you enable "Routing
   messages", below. Say Y if you want to experiment with it; this is
   ALPHA code, which means that it need not be completely stable; it
@@ -647,10 +649,9 @@
 Routing messages
 CONFIG_RTNETLINK
   If you enable this and create a character special file /dev/route
-  with major number 18 and minor number 0 using mknod ("man mknod"),
+  with major number 36 and minor number 0 using mknod ("man mknod"),
   you can read some network related routing information from that
-  file. Everything you write to that file will be discarded. Say Y,
-  because otherwise the network link driver is pointless.
+  file. Everything you write to that file will be discarded.
 
 SCSI support?
 CONFIG_SCSI
@@ -1091,15 +1092,13 @@
 
 Sun LANCE Ethernet support
 CONFIG_SUN_LANCE
-  This is support for a certain type of Ethernet cards on Sun
-  workstations. The driver does not yet exist, so you might as well
-  say N.
+  This is support for lance ethernet cards on Sun workstations such as
+  the Sparcstation IPC (any Sparc with an 'le0' under SunOS basically).
 
 Sun Intel Ethernet support
 CONFIG_SUN_INTEL
-  This is support for a certain type of Ethernet cards on Sun
-  workstations. The driver does not yet exist, so you might as well
-  say N.
+  This is support for the intel ethernet cards on some Sun workstations
+  (all those with an ie0 interface under SunOS).
 
 Do you want to be offered ALPHA test drivers
 CONFIG_NET_ALPHA
@@ -1242,7 +1241,9 @@
   Documentation/networking/net-modules.txt. If you plan to use more
   than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If your card is not working
+  you may need to use the DOS setup disk to disable Plug & Play mode, and
+  to select the default media type.
 
 Other ISA cards
 CONFIG_NET_ISA
@@ -1399,6 +1400,16 @@
   you should have said Y to "AX.25 support" above, because AX.25 is
   the protocol used for digital traffic over radio links.
 
+Gracilis PackeTwin support
+CONFIG_PT
+  This card is similar to the PI card (mentioned above).  It is used mainly
+  by amateur radio operators for packet radio.  You should of already said Y
+  to "AX.25 support" as this card uses that protocol. 
+  Other than the code and the PT user documentation, there is no other
+  information on this card.
+  NOTE: The card is capable of DMA and full duplex but neither of these have
+  been coded in the driver as yet.
+
 WaveLAN support
 CONFIG_WAVELAN
   These are cards for wireless ethernet-like networking. Supported are
@@ -1793,7 +1804,7 @@
   still used for root/boot and other floppies or ram disks since it is
   leaner. You don't want to use it on your harddisk because of certain
   built-in restrictions. This option will enlarge your kernel by about
-  25 kB. Everyone should say Y so that they are able to read this
+  25 kB. Everyone should say Y or M so that they are able to read this
   common floppy format.  If you want to compile this as a module
   however ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -2049,7 +2060,7 @@
   connecting the parallel ports of two local machines) or a ethernet
   network pocket adaptor attaching to the parallel port and a parallel
   printer as well, you should compile both drivers as modules because
-  the drivers don't like each other.
+  the drivers both want the same resources.
 
 Logitech busmouse support
 CONFIG_BUSMOUSE
@@ -2103,7 +2114,9 @@
   and read Documentation/modules.txt. If you are unsure, say N and
   read the HOWTO nevertheless: it will tell you what you have. Chances
   are that you have a regular serial MouseSystem or Microsoft mouse
-  plugging in a COM port which is supported automatically.
+  plugging in a COM port which is supported automatically. Also be aware
+  several vendors talk about 'Microsoft busmouse' and actually mean PS/2
+  busmouse - so count the pins on the connector.
 
 ATIXL busmouse support
 CONFIG_ATIXL_BUSMOUSE
diff -u --recursive --new-file v1.3.50/linux/Documentation/SMP.txt linux/Documentation/SMP.txt
--- v1.3.50/linux/Documentation/SMP.txt	Fri Nov 17 08:42:24 1995
+++ linux/Documentation/SMP.txt	Tue Dec 26 06:03:00 1995
@@ -1,20 +1,18 @@
-
-SMP support for Linux with up to 32 processors using the Intel MP
+SMP support for Linux with up to 16 processors using the Intel MP
 specification. 
 
 WARNING:
-	This is experimental. Back up your disks first.
+	This is experimental. Back up your disks first. Experience is that
+it is basically stable in its current (inefficient form).
 
 To fix:
 
 o	Fix sys_idle to exit/enter kernel state and do hlt's.
 o	Fix scheduler decisions to reschedule. Per cpu reschedule ?
-o	Scheduler ignores stick to CPU advantage. Critical for P6! [Done - FK]
 o	Clean up message pass.
 o	Test for B stepping processors.
 o	Clean up processor specific/independant split.
 o	Document it all.	[PARTLY DONE]
-o	Find the exception/crash bug.
 o	Halt other CPU's on reset/panic doesn't always work.
 o	Dont waste page at 4K - dont need it now.(watch the GDT code).
 o	Dump bootup pages once booted somehow.
@@ -25,4 +23,3 @@
 	nicely).
 o	486 startup code.
 o	How to handle mixed FPU/non FPU processors.
-o	Support 4Mb page mode again	[TESTING]
diff -u --recursive --new-file v1.3.50/linux/Documentation/watchdog.txt linux/Documentation/watchdog.txt
--- v1.3.50/linux/Documentation/watchdog.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/watchdog.txt	Tue Dec 26 06:03:00 1995
@@ -0,0 +1,67 @@
+	Watchdog Timer Interfaces For The Linux Operating System
+
+		Alan Cox <alan@lxorguk.ukuu.org.uk>
+
+	    Custom Linux Driver And Program Development
+
+
+The following watchdog drivers are currently implemented:
+
+	IMS	WDT501-P
+	INS	WDT501-P (no fan tachometer)
+	IMS	WDT500-P
+	Software Only
+
+All four interfaces provide /dev/watchdog, which when open must be written
+to within a minute or the machine will reboot. Each write delays the reboot
+time another minute. In the case of the software watchdog the ability to 
+reboot will depend on the state of the machines and interrupts. The hardware
+boards physically pull the machine down off their own onboard timers and
+will reboot from almost anything.
+
+A second temperature monitoring interface is available on the WDT501P cards
+and provides /dev/temperature. This is the machine internal temperature in
+degrees farenheit. Each read returns a single byte giving the temperature.
+
+The third interface logs kernel messages on additional alert events.
+
+At the moment only the software watchdog is available in the standard
+kernel.
+
+Features
+--------
+		WDT501P		WDT500P		Software
+Reboot Timer	   X               X                X
+External Reboot	   X	           X                o
+Temperature	   X		   o		    o
+Fan Speed          X		   o		    o
+Power Under	   X               o                o
+Power Over         X               o                o
+Overheat           X               o                o
+
+The external event interfaces on the WDT boards are not currently supported.
+
+
+Example Watchdog Driver
+-----------------------
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(int argc, const char *argv[])
+{
+	int fd=open("/dev/watchdog",O_WRONLY);
+	if(fd==-1)
+	{
+		perror("watchdog");
+		exit(1);
+	}
+	while(1)
+	{
+		write(fd,"\0",1);
+		sleep(10);
+	}
+}
+
+
diff -u --recursive --new-file v1.3.50/linux/Makefile linux/Makefile
--- v1.3.50/linux/Makefile	Tue Dec 26 04:45:34 1995
+++ linux/Makefile	Tue Dec 26 04:44:59 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 50
+SUBLEVEL = 51
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.50/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.3.50/linux/arch/i386/kernel/process.c	Mon Dec 11 15:42:00 1995
+++ linux/arch/i386/kernel/process.c	Tue Dec 26 06:03:00 1995
@@ -8,6 +8,9 @@
  * This file handles the architecture-dependent parts of process handling..
  */
 
+#define __KERNEL_SYSCALLS__
+#include <stdarg.h>
+
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -21,6 +24,7 @@
 #include <linux/a.out.h>
 #include <linux/interrupt.h>
 #include <linux/config.h>
+#include <linux/unistd.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -50,6 +54,8 @@
 	hlt_counter--;
 }
 
+#ifndef __SMP__
+
 static void hard_idle(void)
 {
 	while (!need_resched) {
@@ -68,7 +74,8 @@
 			__asm__("hlt");
 #endif
 	        }
- 		if (need_resched) break;
+ 		if (need_resched) 
+ 			break;
 		schedule();
 	}
 #ifdef CONFIG_APM
@@ -77,61 +84,73 @@
 }
 
 /*
- * The idle loop on a i386..
+ * The idle loop on a uniprocessor i386..
  */
+ 
 asmlinkage int sys_idle(void)
 {
-#ifndef __SMP__
         unsigned long start_idle = 0;
-#endif
-		
+
 	if (current->pid != 0)
-	{
-	/*	printk("Wrong process idled\n");	SMP bug check */
 		return -EPERM;
-	}
-#ifdef __SMP__
-	/*
-	 *	SMP locking sanity checker
-	 */
-	if(smp_processor_id()!=active_kernel_processor)
-		panic("CPU is %d, kernel CPU is %d in sys_idle!\n",
-			smp_processor_id(), active_kernel_processor);
-	if(syscall_count!=1)
-		printk("sys_idle: syscall count is not 1 (%ld)\n", syscall_count);
-	if(kernel_counter!=1)
-	{
-		printk("CPU %d, sys_idle, kernel_counter is %ld\n", smp_processor_id(), kernel_counter);
-		if(!kernel_counter)
-			panic("kernel locking botch");
-	}
-	/*
-	 *	Until we have C unlocking done
-	 */
-	current->counter = -100;
-	schedule();
-	return 0;
-#endif	
 	/* endless idle loop with no priority at all */
 	current->counter = -100;
-	for (;;) {
-#ifdef __SMP__
-		if (cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
-			__asm__("hlt");
-#else	
-		if (!start_idle) start_idle = jiffies;
-		if (jiffies - start_idle > HARD_IDLE_TIMEOUT) {
+	for (;;) 
+	{
+		/*
+		 *	We are locked at this point. So we can safely call
+		 *	the APM bios knowing only one CPU at a time will do
+		 *	so.
+		 */
+		if (!start_idle) 
+			start_idle = jiffies;
+		if (jiffies - start_idle > HARD_IDLE_TIMEOUT) 
+		{
 			hard_idle();
-		} else {
+		} 
+		else 
+		{
 			if (hlt_works_ok && !hlt_counter && !need_resched)
 		        	__asm__("hlt");
 		}
-		if (need_resched) start_idle = 0;
-#endif
+		if (need_resched) 
+			start_idle = 0;
 		schedule();
 	}
 }
 
+#else
+
+/*
+ *	In the SMP world we hlt outside of kernel syscall rather than within
+ *	so as to get the right locking semantics.
+ */
+ 
+asmlinkage int sys_idle(void)
+{
+	if(current->pid != 0)
+		return -EPERM;
+	current->counter= -100;
+	schedule();
+	return 0;
+}
+
+/*
+ *	This is being executed in task 0 'user space'.
+ */
+
+int cpu_idle(void *unused)
+{
+	while(1)
+	{
+		if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
+			__asm("hlt");
+		idle();
+	}
+}
+
+#endif
+
 /*
  * This routine reboots the machine by asking the keyboard
  * controller to pulse the reset-line low. We try that for a while,
@@ -186,6 +205,7 @@
 /*
  * Free current thread data structures etc..
  */
+
 void exit_thread(void)
 {
 	/* forget lazy i387 state */
diff -u --recursive --new-file v1.3.50/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v1.3.50/linux/arch/i386/kernel/signal.c	Wed Sep 13 12:45:29 1995
+++ linux/arch/i386/kernel/signal.c	Wed Dec 27 07:38:47 1995
@@ -245,8 +245,6 @@
 		eip = (unsigned long) sa->sa_handler;
 		if (sa->sa_flags & SA_ONESHOT)
 			sa->sa_handler = NULL;
-/* force a supervisor-mode page-in of the signal handler to reduce races */
-		__asm__("testb $0,%%fs:%0": :"m" (*(char *) eip));
 		regs->cs = USER_CS; regs->ss = USER_DS;
 		regs->ds = USER_DS; regs->es = USER_DS;
 		regs->gs = USER_DS; regs->fs = USER_DS;
diff -u --recursive --new-file v1.3.50/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v1.3.50/linux/arch/i386/kernel/smp.c	Thu Dec 21 08:53:30 1995
+++ linux/arch/i386/kernel/smp.c	Tue Dec 26 06:03:00 1995
@@ -29,6 +29,7 @@
 #include <linux/smp.h>
 #include <asm/pgtable.h>
 #include <asm/bitops.h>
+#include <asm/pgtable.h>
 #include <asm/smp.h>
 
 extern void *vremap(unsigned long offset, unsigned long size);	/* Linus hasnt put this in the headers yet */
@@ -611,7 +612,7 @@
 	 *	During boot up send no messages
 	 */
 	 
-	if(!smp_activated)
+	if(!smp_activated || !smp_commenced)
 		return;
 		
 	
diff -u --recursive --new-file v1.3.50/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v1.3.50/linux/drivers/block/genhd.c	Tue Dec 26 04:45:35 1995
+++ linux/drivers/block/genhd.c	Tue Dec 26 06:03:00 1995
@@ -573,7 +573,6 @@
 		setup_dev(p);
 		nr += p->nr_real;
 	}
-
 #ifdef CONFIG_BLK_DEV_RAM
 	rd_load();
 #endif
diff -u --recursive --new-file v1.3.50/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.50/linux/drivers/char/Config.in	Mon Dec 11 15:42:02 1995
+++ linux/drivers/char/Config.in	Tue Dec 26 06:03:00 1995
@@ -36,3 +36,12 @@
   bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
   bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
 fi
+bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
+if [ "$CONFIG_WATCHDOG" = "y" ]; then
+#  bool '   WDT501P Watchdog timer' CONFIG_WDT_501P
+#  if [ "$CONFIG_WDT_501P" = "y" ]; then
+#    bool '       Fan Tachomeeter' CONFIG_WDT_501P_TACHO
+#  fi
+#  bool '   WDT500P Watchdog timer' CONFIG_WDT_500P
+  bool '   Software Watchdog' CONFIG_SOFT_WATCHDOG
+fi
diff -u --recursive --new-file v1.3.50/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.3.50/linux/drivers/char/Makefile	Wed Dec 13 09:02:45 1995
+++ linux/drivers/char/Makefile	Tue Dec 26 06:03:00 1995
@@ -92,6 +92,11 @@
   endif
 endif
 
+ifdef CONFIG_SOFT_WATCHDOG
+L_OBJS += softdog.o
+M = y
+endif
+
 ifdef CONFIG_QIC02_TAPE
 L_OBJS += tpqic02.o 
 endif
diff -u --recursive --new-file v1.3.50/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v1.3.50/linux/drivers/char/mem.c	Thu Nov  9 11:23:48 1995
+++ linux/drivers/char/mem.c	Tue Dec 26 06:03:00 1995
@@ -385,7 +385,7 @@
 #endif
 #if defined (CONFIG_BUSMOUSE) || defined (CONFIG_82C710_MOUSE) || \
     defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
-    defined (CONFIG_ATIXL_BUSMOUSE)
+    defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG)
 	mouse_init();
 #endif
 #ifdef CONFIG_SOUND
diff -u --recursive --new-file v1.3.50/linux/drivers/char/mouse.c linux/drivers/char/mouse.c
--- v1.3.50/linux/drivers/char/mouse.c	Wed Nov  8 07:11:31 1995
+++ linux/drivers/char/mouse.c	Tue Dec 26 06:03:00 1995
@@ -119,6 +119,9 @@
 #ifdef CONFIG_ATIXL_BUSMOUSE
  	atixl_busmouse_init();
 #endif
+#ifdef CONFIG_SOFT_WATCHDOG
+	watchdog_init();
+#endif	
 #endif /* !MODULE */
 	if (register_chrdev(MOUSE_MAJOR,"mouse",&mouse_fops)) {
 	  printk("unable to get major %d for mouse devices\n",
diff -u --recursive --new-file v1.3.50/linux/drivers/char/softdog.c linux/drivers/char/softdog.c
--- v1.3.50/linux/drivers/char/softdog.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/softdog.c	Tue Dec 26 06:19:51 1995
@@ -0,0 +1,115 @@
+/*
+ *	SoftDog	0.02:	A Software Watchdog Device
+ *
+ *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ *	Email us for quotes on Linux software and driver development. 
+ *
+ *			-----------------------
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *			-----------------------
+ *
+ *	Software only watchdog driver. Unlike its big brother the WDT501P
+ *	driver this won't always recover a failed machine.
+ */
+ 
+ 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mouse.h>
+#define WATCHDOG_MINOR	130
+#define TIMER_MARGIN	(60*HZ)		/* Allow 1 minute */
+
+/*
+ *	Our timer
+ */
+ 
+struct timer_list watchdog_ticktock;
+static int timer_alive = 0;
+
+
+/*
+ *	If the timer expires..
+ */
+ 
+static void watchdog_fire(long data)
+{
+	extern void hard_reset_now(void);
+	hard_reset_now();
+	printk("WATCHDOG: Reboot didn't ?????\n");
+}
+
+/*
+ *	Allow only one person to hold it open
+ */
+ 
+static int softdog_open(struct inode *inode, struct file *file)
+{
+	if(timer_alive)
+		return -EBUSY;
+	/*
+	 *	Activate timer
+	 */
+	watchdog_ticktock.expires=jiffies+TIMER_MARGIN;
+	add_timer(&watchdog_ticktock);
+	return 0;
+}
+
+static void softdog_release(struct inode *inode, struct file *file)
+{
+	/*
+	 *	Shut off the timer.
+	 */
+	del_timer(&watchdog_ticktock);
+	timer_alive=0;
+}
+
+static int softdog_write(struct inode *inode, struct file *file, const char *data, int len)
+{
+	/*
+	 *	Refresh the timer.
+	 */
+	del_timer(&watchdog_ticktock);
+	watchdog_ticktock.expires=jiffies+TIMER_MARGIN;
+	add_timer(&watchdog_ticktock);
+	return 1;
+}
+
+/*
+ *	The mouse stuff ought to be renamed misc_register etc before 1.4...
+ */
+ 
+void watchdog_init(void)
+{
+	static struct file_operations softdog_fops=
+	{
+		NULL,		/* Seek */
+		NULL,		/* Read */
+		softdog_write,	/* Write */
+		NULL,		/* Readdir */
+		NULL,		/* Select */
+		NULL,		/* Ioctl */
+		NULL,		/* MMap */
+		softdog_open,
+		softdog_release,
+		NULL,		
+		NULL		/* Fasync */
+	};
+	static struct mouse softdog_mouse={
+		WATCHDOG_MINOR,
+		"softdog",
+		&softdog_fops
+	};
+
+	mouse_register(&softdog_mouse);
+	init_timer(&watchdog_ticktock);
+	watchdog_ticktock.function=watchdog_fire;
+	printk("Software Watchdog Timer: 0.02\n");
+}	
diff -u --recursive --new-file v1.3.50/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.3.50/linux/drivers/net/3c501.c	Mon Dec 11 15:42:02 1995
+++ linux/drivers/net/3c501.c	Tue Dec 26 06:03:00 1995
@@ -104,6 +104,8 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+#define BLOCKOUT_2
+
 /* A zero-terminated list of I/O addresses to be probed.
    The 3c501 can be at many locations, but here are the popular ones. */
 static unsigned int netcard_portlist[] =
@@ -128,7 +130,7 @@
 #define EL1_IO_EXTENT	16
 
 #ifndef EL_DEBUG
-#define EL_DEBUG  2	/* use 0 for production, 1 for devel., >2 for debug */
+#define EL_DEBUG  0	/* use 0 for production, 1 for devel., >2 for debug */
 #endif			/* Anything above 5 is wordy death! */
 static int el_debug = EL_DEBUG;
  
@@ -377,6 +379,9 @@
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 	unsigned long flags;
+	
+	if(dev->interrupt)		/* May be unloading, don't stamp on */
+		return 1;		/* the packet buffer this time      */
 
 	if (dev->tbusy) 
 	{
@@ -436,10 +441,13 @@
 		 *	Command mode with status cleared should [in theory]
 		 *	mean no more interrupts can be pending on the card.
 		 */
-	
-		outb(AX_SYS, AX_CMD);
-		inb(RX_STATUS);
-		inb(TX_STATUS);
+		 
+#ifdef BLOCKOUT_1
+		disable_irq(dev->irq);		 
+#endif	
+		outb_p(AX_SYS, AX_CMD);
+		inb_p(RX_STATUS);
+		inb_p(TX_STATUS);
 	
 		lp->loading=1;
 	
@@ -453,13 +461,19 @@
 		outw(gp_start, GP_LOW);		/* aim - packet will be loaded into buffer start */
 		outsb(DATAPORT,buf,skb->len);	/* load buffer (usual thing each byte increments the pointer) */
 		outw(gp_start, GP_LOW);		/* the board reuses the same register */
+#ifndef BLOCKOUT_1		
 		if(lp->loading==2)		/* A receive upset our load, despite our best efforts */
 		{
 			if(el_debug>2)
 				printk("%s: burped during tx load.\n", dev->name);
 			goto load_it_again_sam;	/* Sigh... */
 		}
+#endif
 		outb(AX_XMIT, AX_CMD);		/* fire ... Trigger xmit.  */
+		lp->loading=0;
+#ifdef BLOCKOUT_1		
+		enable_irq(dev->irq);
+#endif		
 		dev->trans_start = jiffies;
 	}
 
@@ -506,18 +520,37 @@
 	if (dev->interrupt)
 		printk("%s: Reentering the interrupt driver!\n", dev->name);
 	dev->interrupt = 1;
-    
+#ifndef BLOCKOUT_1    
+        if(lp->loading==1 && !dev->tbusy)
+        	printk("%s: Inconsistent state loading while not in tx\n",
+        		dev->name);
+#endif        		
+#ifdef BLOCKOUT_3
 	lp->loading=2;		/* So we can spot loading interruptions */
+#endif
 
 	if (dev->tbusy) 
 	{
     
     		/*
-    		 *	Board in transmit mode.
+    		 *	Board in transmit mode. May be loading. If we are
+    		 *	loading we shouldn't have got this.
     		 */
     	 
 		int txsr = inb(TX_STATUS);
-
+#ifdef BLOCKOUT_2		
+		if(lp->loading==1)
+		{
+			if(el_debug > 2)
+			{
+				printk("%s: Interrupt while loading [", dev->name);
+				printk(" txsr=%02x gp=%04x rp=%04x]\n", txsr, inw(GP_LOW),inw(RX_LOW));
+			}
+			lp->loading=2;		/* Force a reload */
+			dev->interrupt = 0;
+			return;
+		}
+#endif
 		if (el_debug > 6)
 			printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW));
 
@@ -593,7 +626,7 @@
 		 */
 		if (rxsr & RX_MISSED)
 			lp->stats.rx_missed_errors++;
-		if (rxsr & RX_RUNT) 
+		else if (rxsr & RX_RUNT) 
 		{	/* Handled to avoid board lock-up. */
 			lp->stats.rx_length_errors++;
 			if (el_debug > 5) 
diff -u --recursive --new-file v1.3.50/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.50/linux/drivers/net/Config.in	Sat Nov 25 19:04:40 1995
+++ linux/drivers/net/Config.in	Tue Dec 26 06:03:00 1995
@@ -51,6 +51,7 @@
   tristate 'NE2000/NE1000 support' CONFIG_NE2000
   if [ "$CONFIG_AX25" = "y" ]; then
     bool 'Ottawa PI and PI/2 support' CONFIG_PI
+    bool 'Gracilis PackeTwin support' CONFIG_PT
   fi
   bool 'SK_G16 support' CONFIG_SK_G16
 fi
diff -u --recursive --new-file v1.3.50/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.50/linux/drivers/net/Makefile	Sat Nov 25 19:04:40 1995
+++ linux/drivers/net/Makefile	Tue Dec 26 06:03:00 1995
@@ -345,6 +345,9 @@
 CONFIG_PI = CONFIG_PI
 endif
 
+ifeq ($(CONFIG_PT),y)
+L_OBJS += pt.o
+endif
 
 # If anything built-in uses slhc, then build it into the kernel also.
 # If not, but a module uses it, build as a module.
diff -u --recursive --new-file v1.3.50/linux/drivers/net/README.pt linux/drivers/net/README.pt
--- v1.3.50/linux/drivers/net/README.pt	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/README.pt	Tue Dec 26 06:03:00 1995
@@ -0,0 +1,62 @@
+This is the README for the Gracilis Packetwin device driver, version 0.5
+ALPHA for Linux 1.3.43.
+
+These files will allow you to talk to the PackeTwin (now know as PT) and
+connect through it just like a pair of TNC's.  To do this you will also
+require the AX.25 code in the kernel enabled.
+
+There are four files in this archive; this readme, a patch file, a .c file
+and finally a .h file.  The two program files need to be put into the
+drivers/net directory in the Linux source tree, for me this is the
+directory /usr/src/linux/drivers/net.  The patch file needs to be patched in
+at the top of the Linux source tree (/usr/src/linux in my case).
+
+You will most probably have to edit the pt.c file to suit your own setup,
+this should just involve changing some of the defines at the top of the file. 
+Please note that if you run an external modem you must specify a speed of 0.
+
+The program is currently setup to run a 4800 baud external modem on port A
+and a Kantronics DE-9600 daughter board on port B so if you have this (or
+something similar) then you're right.
+
+To compile in the driver, put the files in the correct place and patch in
+the diff.  You will have to re-configure the kernel again before you
+recompile it. 
+
+The driver is not real good at the moment for finding the card.  You can
+'help' it by changing the order of the potiential addresses in the structure
+found in the pt_init() function so the address of where the card is is put
+first.
+
+After compiling, you have to get them going, they are pretty well like any
+other net device and just need ifconfig to get them going.
+As an example, here is my /etc/rc.net
+--------------------------
+
+#
+# Configure the PackeTwin, port A.
+/sbin/ifconfig pt0a 44.136.8.87 hw ax25 vk2xlz mtu 512 
+/sbin/ifconfig pt0a 44.136.8.87 broadcast 44.136.8.255 netmask 255.255.255.0
+/sbin/route add -net 44.136.8.0 netmask 255.255.255.0 dev pt0a
+/sbin/route add -net 44.0.0.0 netmask 255.0.0.0 gw 44.136.8.68 dev pt0a
+/sbin/route add -net 138.25.16.0 netmask 255.255.240.0 dev pt0a
+/sbin/route add -host 44.136.8.255 dev pt0a
+#
+# Configure the PackeTwin, port B.
+/sbin/ifconfig pt0b 44.136.8.87 hw ax25 vk2xlz-1 mtu 512
+/sbin/ifconfig pt0b 44.136.8.87 broadcast 44.255.255.255 netmask 255.0.0.0
+/sbin/route add -host 44.136.8.216 dev pt0b
+/sbin/route add -host 44.136.8.95  dev pt0b
+/sbin/route add -host 44.255.255.255 dev pt0b
+
+This version of the driver comes under the GNU GPL.  If you have one on my
+previous (non-GPL) versions of the driver, please update to this one.
+
+I hope that this all works well for you.  I would be pleased to hear how
+many people use the driver and if it does its job.
+
+  - Craig vk2xlz
+
+INET: csmall@acacia.itd.uts.edu.au  craig.small@eol.ieaust.org.au
+AMPR: vk2xlz@gonzo.vk2xlz.ampr.org
+AX25: vk2xlz@vk2gdm.nsw.aus.oc
diff -u --recursive --new-file v1.3.50/linux/drivers/net/apricot.c linux/drivers/net/apricot.c
--- v1.3.50/linux/drivers/net/apricot.c	Sat Nov 25 19:04:40 1995
+++ linux/drivers/net/apricot.c	Wed Dec 27 08:25:37 1995
@@ -960,7 +960,7 @@
 	struct i596_cmd *cmd;
 
 	if (i596_debug > 1)
-		printk ("%s: set multicast list %d\n", dev->name, num_addrs);
+		printk ("%s: set multicast list %d\n", dev->name, dev->mc_count);
 
 	if (dev->mc_count > 0) 
 	{
@@ -974,10 +974,10 @@
 		}
 		cmd->command = CmdMulticastList;
 		*((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
-		cp=((char *)(cmd + 1))+2
+		cp=((char *)(cmd + 1))+2;
 		for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
 		{
-			memcpy(cp, addr,6);
+			memcpy(cp, dmi,6);
 			cp+=6;
 		}
 		print_eth (((char *)(cmd + 1)) + 2);
diff -u --recursive --new-file v1.3.50/linux/drivers/net/pt.c linux/drivers/net/pt.c
--- v1.3.50/linux/drivers/net/pt.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/pt.c	Tue Dec 26 06:03:00 1995
@@ -0,0 +1,1830 @@
+#undef PT_DEBUG 1
+/*
+ * pt.c: Linux device driver for the Gracilis PackeTwin.
+ * Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge MA 02139, USA.
+ *
+ * This driver is largely based upon the PI driver by David Perry.
+ *
+ * Revision History
+ * 23/02/95 cs  Started again on driver, last one scrapped
+ * 27/02/95 cs  Program works, we have chan A only.  Tx stays on
+ * 28/02/95 cs  Fix Tx problem (& TxUIE instead of | )
+ *		Fix Chan B Tx timer problem, used TMR2 instead of TMR1
+ * 03/03/95 cs  Painfully found out (after 3 days) SERIAL_CFG is write only
+ *              created image of it and DMA_CFG
+ * 21/06/95 cs  Upgraded to suit PI driver 0.8 ALPHA
+ * 22/08/95	cs	Changed it all around to make it like pi driver
+ * 23/08/95 cs  It now works, got caught again by TMR2 and we must have
+ *				auto-enables for daughter boards.
+ * 07/10/95 cs  Fixed for 1.3.30 (hopefully)
+ * 26/11/95 cs  Fixed for 1.3.43, ala 29/10 for pi2.c by ac
+ * 21/12/95 cs  Got rid of those nasty warnings when compiling, for 1.3.48
+ */
+ 
+/* 
+ * default configuration of the PackeTwin,
+ * ie What Craig uses his PT for.
+ */
+#define PT_DMA 3
+
+#define DEF_A_SPEED	4800		/* 4800 baud */
+#define DEF_A_TXDELAY	350		/* 350 mS */
+#define DEF_A_PERSIST	64		/* 25% persistance */
+#define DEF_A_SLOTIME	10		/* 10 mS */
+#define DEF_A_SQUELDELAY 30		/* 30 mS */
+#define DEF_A_CLOCKMODE	0		/* Normal clock mode */
+#define DEF_A_NRZI		1		/* NRZI mode */
+
+#define DEF_B_SPEED	0		/* 0 means external clock */
+#define DEF_B_TXDELAY	250		/* 250 mS */
+#define DEF_B_PERSIST	64		/* 25% */
+#define DEF_B_SLOTIME	10		/* 10 mS */
+#define DEF_B_SQUELDELAY 30		/* 30 mS */
+#define DEF_B_CLOCKMODE 0 		/* Normal clock mode ?!? */
+#define DEF_B_NRZI		1		/* NRZI mode */
+
+
+#define	PARAM_TXDELAY	1
+#define	PARAM_PERSIST	2
+#define	PARAM_SLOTTIME	3
+#define	PARAM_FULLDUP	5
+#define	PARAM_HARDWARE	6
+#define	PARAM_RETURN	255
+
+#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/errno.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/if_arp.h>
+#include "pt.h"
+#include "z8530.h"
+#include <net/ax25.h>
+
+static char *version =
+"PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n";
+
+
+struct mbuf {
+    struct mbuf *next;
+    int cnt;
+    char data[0];
+};
+
+/*
+ * The actual PT devices we will use
+ */
+static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */
+static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
+static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
+
+/* Ok, they shouldn't be here, but both channels share them */
+/* The Images of the Serial and DMA config registers */
+static unsigned char pt_sercfg = 0;
+static unsigned char pt_dmacfg = 0;
+
+/* The number of IO ports used by the card */
+#define PT_TOTAL_SIZE   16
+
+/* Index to functions, as function prototypes. */
+
+static int pt_probe(struct device *dev);
+static int pt_open(struct device *dev);
+static int pt_send_packet(struct sk_buff *skb, struct device *dev);
+static void pt_interrupt(int irq, struct pt_regs *regs);
+static int pt_close(struct device *dev);
+static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static struct enet_statistics *pt_get_stats(struct device *dev);
+static void pt_rts(struct pt_local *lp, int x);
+static void pt_rxisr(struct device *dev);
+static void pt_txisr(struct pt_local *lp);
+static void pt_exisr(struct pt_local *lp);
+static void pt_tmrisr(struct pt_local *lp);
+static char *get_dma_buffer(unsigned long *mem_ptr);
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
+static int hw_probe(int ioaddr);
+static void tdelay(struct pt_local *lp, int time);
+static void empty_scc(struct pt_local *lp);
+static void chipset_init(struct device *dev);
+static void send_kiss(struct device *dev, unsigned char arg, unsigned char val);
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+
+
+static int ext2_secrm_seed = 152;
+
+static inline unsigned char random(void)
+{
+    return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1);
+}
+
+static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val)
+{
+    outb_p(sccreg, ctl);        /* Select register */
+    outb_p(val, ctl);           /* Output value */
+}
+
+static inline unsigned char rdscc(int cbase, int ctl, int sccreg)
+{
+    unsigned char retval;
+
+    outb_p(sccreg, ctl);        /* Select register */
+    retval = inb_p(ctl);
+    return retval;
+}
+
+static void switchbuffers(struct pt_local *lp)
+{
+    if (lp->rcvbuf == lp->rxdmabuf1)
+	lp->rcvbuf = lp->rxdmabuf2;
+    else
+	lp->rcvbuf = lp->rxdmabuf1;
+}
+		
+static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb)
+{
+    char kickflag;
+    unsigned long flags;
+    char *ptr;
+    struct device *dev;
+
+	/* First, let's see if this packet is actually a KISS packet */
+	ptr = skb->data;
+	if (ptr[0] != 0 && skb->len >= 2)
+	{
+		printk("Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+		/* Kludge to get device */
+		if ((struct pt_local*)(&pt0b.priv) == lp)
+			dev = &pt0b;
+		else
+			dev = &pt0a;
+		switch(ptr[0])
+		{
+
+			case PARAM_TXDELAY:
+				/*TxDelay is in 10mS increments */
+				lp->txdelay = ptr[1] * 10;
+				send_kiss(dev, PARAM_TXDELAY, (u_char)(lp->txdelay/10));
+				break;
+			case PARAM_PERSIST:
+				lp->persist = ptr[1];
+				send_kiss(dev, PARAM_PERSIST, (u_char)(lp->persist));
+				break;
+			case PARAM_SLOTTIME:
+				lp->slotime = ptr[1];
+				send_kiss(dev, PARAM_SLOTTIME, (u_char)(lp->slotime/10));
+				break;
+			case PARAM_FULLDUP:
+				/* Yeah right, you wish!  Fullduplex is a little while to
+				 * go folks, but this is how you fire it up
+				 */
+				send_kiss(dev, PARAM_FULLDUP, 0);
+				break;
+			/* Perhaps we should have txtail here?? */
+		} /*switch */
+		return;
+	}
+	
+    lp->stats.tx_packets++;
+
+    save_flags(flags);
+    cli();
+    kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
+    restore_flags(flags);
+
+#ifdef PT_DEBUG
+	printk("PTd hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
+#endif	
+    skb_queue_tail(&lp->sndq, skb);
+    if (kickflag) {
+        /* Simulate interrupt to transmit */
+            if (lp->dmachan)
+            {	
+            	pt_txisr(lp);
+            } else {
+            	save_flags(flags);
+	           	cli();
+            	if (lp->tstate == IDLE)
+                	pt_txisr(lp); 
+            	restore_flags(flags);
+            }
+    }
+} /* hardware_send_packet() */
+
+static void setup_rx_dma(struct pt_local *lp)
+{
+    unsigned long flags;
+    int cmd;
+    unsigned long dma_abs;
+    unsigned char dmachan;
+
+    save_flags(flags);
+    cli();
+
+    dma_abs = (unsigned long) (lp->rcvbuf->data);
+    dmachan = lp->dmachan;
+    cmd = lp->base + CTL;
+
+    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+	panic("PI: RX buffer violates DMA boundary!");
+
+    /* Get ready for RX DMA */
+    wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+    disable_dma(dmachan);
+    clear_dma_ff(dmachan);
+
+    /* Set DMA mode register to single transfers, incrementing address,
+     *  auto init, writes
+     */
+    set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
+    set_dma_addr(dmachan, dma_abs);
+    set_dma_count(dmachan, lp->bufsiz);
+    enable_dma(dmachan);
+
+    /* If a packet is already coming in, this line is supposed to
+	   avoid receiving a partial packet.
+     */
+    wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
+
+    /* Enable RX dma */
+    wrtscc(lp->cardbase, cmd, R1,
+      WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+    restore_flags(flags);
+}
+
+static void setup_tx_dma(struct pt_local *lp, int length)
+{
+    unsigned long dma_abs;
+    unsigned long flags;
+    unsigned long dmachan;
+
+    save_flags(flags);
+    cli();
+
+    dmachan = lp->dmachan;
+    dma_abs = (unsigned long) (lp->txdmabuf);
+
+    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+	panic("PT: TX buffer violates DMA boundary!");
+
+    disable_dma(dmachan);
+    /* Set DMA mode register to single transfers, incrementing address,
+     *  no auto init, reads
+     */
+    set_dma_mode(dmachan, DMA_MODE_WRITE);
+    clear_dma_ff(dmachan);
+    set_dma_addr(dmachan, dma_abs);
+    /* output byte count */
+    set_dma_count(dmachan, length);
+
+    restore_flags(flags);
+}
+
+static void free_p(struct sk_buff *skb)
+{
+    dev_kfree_skb(skb, FREE_WRITE);
+}
+
+static void pt_loopback(struct pt_local *lp, int onoff)
+{
+    if (lp->base & CHANA) {
+        if (onoff == ON)
+            outb_p(pt_sercfg |= PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
+        else
+            outb_p(pt_sercfg &= ~PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
+    } else {    /* it's channel B */
+        if (onoff == ON)
+            outb_p(pt_sercfg |= PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
+        else
+            outb_p(pt_sercfg &= ~PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
+    }
+} /*pt_loopback */
+
+/* Fill in the MAC-level header */
+static int pt_header (struct sk_buff *skb, struct device *dev, unsigned short type,
+		void *daddr, void *saddr, unsigned len)
+{
+	return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+
+/* Rebuild the MAC-level header */
+static int pt_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+				struct sk_buff *skb)
+{
+	return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+
+					
+					
+
+
+/*
+ * This sets up all the registers in the SCC for the given channel
+ * based upon tsync_hwint()
+ */
+static void scc_init(struct device *dev)
+{
+    unsigned long flags;
+    struct pt_local *lp = (struct pt_local*) dev->priv;
+    register int cmd = lp->base + CTL;
+    int tc, br;
+
+#ifdef PT_DEBUG
+	printk("PTd scc_init(): (%d).\n", lp->base & CHANA);
+#endif	
+    save_flags(flags);
+    cli();
+
+    /* We may put something here to enable_escc */
+
+    if (cmd & CHANA)
+    {
+        wrtscc(lp->cardbase, cmd, R9, CHRA);		/* Reset channel A */
+        wrtscc(lp->cardbase, cmd, R2, 0xff);		/* Initialise interrupt vector */
+    } else {
+    	wrtscc(lp->cardbase, cmd, R9, CHRB);		/* Reset channel B */
+    }
+    
+    /* Deselect all Rx and Tx interrupts */
+    wrtscc(lp->cardbase, cmd, R1, 0);
+    
+    /* Turn off external interrupts (like CTS/CD) */
+    wrtscc(lp->cardbase, cmd, R15, 0);
+           
+    /* X1 clock, SDLC mode */
+    wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
+	
+	/* Preset CRC and set mode */
+	if (lp->nrzi)
+	{
+    	/* Preset Tx CRC, put into NRZI mode */
+       wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);		
+    } else {
+    	/* Preset Tx CRC, put into NRZ mode */
+       wrtscc(lp->cardbase, cmd, R10, CRCPS);    
+    }
+    
+    /* Tx/Rx parameters */
+    if (lp->speed)		/* Use internal clocking */
+    {
+       /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */
+       wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
+
+    } else {			/* Use external clocking */
+           /* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */
+           wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR);
+           wrtscc(lp->cardbase,cmd, R14, 0);	/* wiz1 */
+    }
+       
+    /* Null out SDLC start address */
+    wrtscc(lp->cardbase, cmd, R6, 0);
+
+    /* SDLC flag */
+    wrtscc(lp->cardbase, cmd, R7, FLAG);
+
+    /* Setup Tx but don't enable it */
+    wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+
+    /* Setup Rx */
+    wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
+
+    /* Setup the BRG, turn it off first */
+    wrtscc(lp->cardbase, cmd, R14, BRSRC);
+
+    /* set the 32x time constant for the BRG in Rx mode */
+	if (lp->speed)
+	{
+		br = lp->speed;
+		tc = ((lp->xtal / 32) / (br * 2)) - 2;
+	    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);		/* lower byte */
+   		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);	/* upper byte */			
+	}
+
+	/* Turn transmitter off, to setup stuff */
+   	pt_rts(lp, OFF);
+   	
+	/* External clocking */
+	if (lp->speed)
+	{
+		/* DPLL frm BRG, BRG src PCLK */
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);	/* SEARCH mode, keep BRG src */
+	    wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);	/* Enable the BRG */
+
+	    /* Turn off external clock port */
+        if (lp->base & CHANA)
+            outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
+        else
+            outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );        	    
+    } else {
+		/* DPLL frm rtxc,BRG src PCLK */
+/*		wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC);*/
+        /* Turn on external clock port */
+        if (lp->base & CHANA)
+            outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
+        else
+            outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );     
+    }
+
+    if (!lp->dmachan)	
+		wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+
+    wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* ABORT int */
+	
+	/* Turn on the DTR to tell modem we're alive */
+	if (lp->base & CHANA)
+	    outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) );
+	else
+    	outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) );
+
+    /* Now, turn on the receiver and hunt for a flag */
+    wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 );
+	
+	restore_flags(flags);
+
+} /* scc_init() */
+
+/* Resets the given channel and whole SCC if both channels off */
+static void chipset_init(struct device *dev)
+{
+
+	struct pt_local *lp = (struct pt_local*) dev->priv;
+#ifdef PT_DEBUG
+	printk("PTd chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
+	printk("PTd chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);	
+#endif
+	/* Reset SCC if both channels are to be canned */
+	if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
+			(!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) ) 
+	{
+		wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES);
+        	/* Reset int and dma registers */
+	        outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
+	        outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
+#ifdef PT_DEBUG
+		printk("PTd chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
+#endif			        
+	}
+	/* Reset individual channel */
+    	if (lp->base & CHANA) {
+        	wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA);
+        	outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG);
+    	} else {
+        	wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB);
+			outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG);			 
+	}	
+} /* chipset_init() */	
+	
+	
+
+int pt_init(void)
+{
+    int *port;
+    int ioaddr = 0;
+    int card_type = 0;
+    int ports[] =
+    { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
+      0x2b0, 0x300, 0x330, 0x3f0,  0};
+
+    printk(version);
+
+    for (port = &ports[0]; *port && !card_type; port++) {
+        ioaddr = *port;
+
+        if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
+            printk("PT: Probing for card at address %#3x\n", ioaddr);
+            card_type = hw_probe(ioaddr);
+        }
+    }
+    if (card_type) {
+        printk("PT: Found a PT at address %#3x\n",ioaddr);
+    } else {
+        printk("PT: ERROR: No card found.\n");
+        return -EIO;
+    }
+
+    /*
+     * Link a couple of device structres into the chain
+     *
+     * For the A port
+     * Allocate space for 4 buffers even though we only need 3,
+     * because one of them may cross a DMA page boundary and
+     * be rejected by get_dma_buffer().
+     */
+    register_netdev(&pt0a);
+
+    pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+    pt0a.dma = 0;	/* wizzer - no dma yet */
+    pt0a.base_addr = ioaddr + CHANA;
+    pt0a.irq = 0;
+
+    /* And B port */
+    register_netdev(&pt0b);
+
+    pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+    pt0b.base_addr = ioaddr + CHANB;
+    pt0b.irq = 0;
+
+    /* Now initialise them */
+    pt_probe(&pt0a);
+    pt_probe(&pt0b);
+
+    pt0b.irq = pt0a.irq;	/* IRQ is shared */
+    
+    return 0;
+} /* pt_init() */
+
+/*
+ * Probe for PT card.  Also initialises the timers
+ */
+static int hw_probe(int ioaddr)
+{
+    int time = 1000;		/* Number of milliseconds to test */
+    int a = 1;
+    int b = 1;
+    unsigned long start_time, end_time;
+    
+    inb_p(ioaddr + TMR1CLR);
+    inb_p(ioaddr + TMR2CLR);
+
+    /* Timer counter channel 0, 1mS period */
+    outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD);
+    outb_p(0x00, ioaddr + TMR0);
+    outb_p(0x18, ioaddr + TMR0);
+
+    /* Setup timer control word for timer 1 */
+    outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+    outb_p((time << 1) & 0xff, ioaddr + TMR1);
+    outb_p((time >> 7) & 0xff, ioaddr + TMR1);
+    
+    /* wait until counter reg is loaded */
+    do {
+        /* Latch count for reading */
+        outb_p(SC1, ioaddr + TMRCMD);
+        a = inb_p(ioaddr + TMR1);
+        b = inb_p(ioaddr + TMR1);
+    } while (b == 0);
+    start_time = jiffies;
+    while(b != 0)
+    {
+        /* Latch count for reading */
+        outb_p(SC1, ioaddr + TMRCMD);
+        a = inb_p(ioaddr + TMR1);
+        b = inb_p(ioaddr + TMR1);
+        end_time = jiffies;
+        /* Don't wait forever - there may be no card here */
+        if ((end_time - start_time) > 200)
+        {
+        	inb_p(ioaddr + TMR1CLR);
+            return 0;
+        }
+    }
+    
+    /* Now fix the timers up for general operation */
+    
+    /* Clear the timers */
+    inb_p(ioaddr + TMR1CLR);
+    inb_p(ioaddr + TMR2CLR);
+    
+    outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+    inb_p(ioaddr + TMR1CLR);
+    
+    outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+    /* Should this be tmr1 or tmr2? wiz3*/
+    inb_p(ioaddr + TMR1CLR);
+    
+    return 1;
+} /* hw_probe() */
+
+
+static void pt_rts(struct pt_local *lp, int x)
+{
+	int tc;
+	long br;
+	int cmd = lp->base + CTL;
+#ifdef PT_DEBUG
+	printk("PTd pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
+#endif			
+	if (x == ON) {
+	    /* Ex ints off to avoid int */
+	    wrtscc(lp->cardbase, cmd, R15, 0);
+	    wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);	/* Rx off */
+	    lp->rstate = IDLE;
+	    
+	    if(lp->dmachan)
+	    {
+	        /* Setup for Tx DMA */
+	        wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+	    } else {
+	        /* No interrupts */
+	        wrtscc(lp->cardbase, cmd, R1, 0);
+	    }
+	
+            if (!lp->clockmode)
+            {
+                if (lp->speed)
+                {
+                    br = lp->speed;
+                    tc = (lp->xtal / (br * 2)) - 2;
+                    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
+                    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
+                }
+            }
+            /* Turn on Tx by raising RTS */
+            wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
+            /* Transmitter on now */
+        } else {		/* turning off Tx */
+            lp->tstate = IDLE;
+            
+            /* Turn off Tx by dropping RTS */
+            wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+            if (!lp->clockmode)
+            {
+                if (lp->speed)		/* internally clocked */
+                {
+                    /* Repogram BRG from 32x clock for Rx DPLL */
+                    /* BRG off, keep PClk source */
+                    wrtscc(lp->cardbase, cmd, R14, BRSRC);
+                    br = lp->speed;
+                    tc = ((lp->xtal / 32) / (br * 2)) - 2;
+                    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
+                    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
+                    
+                    /* SEARCH mode, BRG source */
+                    wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
+                    /* Enalbe the BRG */
+                    wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
+                }
+            }
+            /* Flush Rx fifo */
+            /* Turn Rx off */
+            wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
+            
+            /* Reset error latch */
+            wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+            
+            /* get status byte from R1 */
+            (void) rdscc(lp->cardbase, cmd, R1);
+            
+            /* Read and dump data in queue */
+            (void) rdscc(lp->cardbase, cmd, R8);
+            (void) rdscc(lp->cardbase, cmd, R8);
+            (void) rdscc(lp->cardbase, cmd, R8);
+            
+            /* Now, turn on Rx and hunt for a flag */
+              wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 );
+              
+            lp->rstate = ACTIVE;
+            
+            if (lp->dmachan)
+            {
+                setup_rx_dma(lp);
+            } else {
+                /* Reset buffer pointers */
+                lp->rcp = lp->rcvbuf->data;
+                lp->rcvbuf->cnt = 0;
+                /* Allow aborts to interrupt us */
+                wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB);
+
+	}
+	wrtscc(lp->cardbase, cmd, R15, BRKIE );
+    }
+} /* pt_rts() */	
+		
+				
+static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize)
+{
+    if (((addr & 0xffff) + dev_bufsize) <= 0x10000)
+        return 1;
+    else
+        return 0;
+}
+
+static int pt_set_mac_address(struct device *dev, struct sockaddr *sa)
+{
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);		/* addr is an AX.25 shifted ASCII */
+	return 0;		/* mac address */
+}
+	
+
+/* Allocate a buffer which does not cross a DMA page boundary */
+static char * get_dma_buffer(unsigned long *mem_ptr)
+{
+    char *ret;
+
+    ret = (char *) *mem_ptr;
+
+    if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) {
+        *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+        ret = (char *) *mem_ptr;
+    }
+    *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+    return (ret);
+} /* get_dma_buffer() */
+
+
+/*
+ * Sets up all the structures for the PT device
+ */
+static int pt_probe(struct device *dev)
+{
+    short ioaddr;
+    struct pt_local *lp;
+    int i;
+    unsigned long flags;
+    unsigned long mem_ptr;
+
+    ioaddr = dev->base_addr;
+
+    /*
+     * Initialise the device structure.
+     * Must be done before chipset_init()
+     * Make sure data structures used by  the PT are aligned
+     */
+    dev->priv = (void *) (((int) dev->priv + 7) & ~7);
+    lp = (struct pt_local*) dev->priv;
+
+    memset(dev->priv, 0, sizeof(struct pt_local));
+
+    /* Allocate some buffers which do not cross DMA boundaries */
+    mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local);
+    lp->txdmabuf = get_dma_buffer(&mem_ptr);
+    lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+    lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+
+    /* Initialise the Rx buffer */
+    lp->rcvbuf = lp->rxdmabuf1;
+    lp->rcp = lp->rcvbuf->data;
+    lp->rcvbuf->cnt = 0;
+
+    /* Initialise the transmit queue head structure */
+    skb_queue_head_init(&lp->sndq);
+
+    lp->base = dev->base_addr;
+    lp->cardbase = dev->base_addr & 0x3f0;
+	
+    /* These need to be initialsed before scc_init() is called.
+     */
+    lp->xtal = XTAL;
+    
+    if (dev->base_addr & CHANA) {
+        lp->speed = DEF_A_SPEED;
+        lp->txdelay = DEF_A_TXDELAY;
+        lp->persist = DEF_A_PERSIST;
+        lp->slotime = DEF_A_SLOTIME;
+        lp->squeldelay = DEF_A_SQUELDELAY;
+        lp->clockmode = DEF_A_CLOCKMODE;
+        lp->nrzi = DEF_A_NRZI;
+    } else {
+        lp->speed = DEF_B_SPEED;
+        lp->txdelay = DEF_B_TXDELAY;
+        lp->persist = DEF_B_PERSIST;
+        lp->slotime = DEF_B_SLOTIME;
+        lp->squeldelay = DEF_B_SQUELDELAY;
+        lp->clockmode = DEF_B_CLOCKMODE;
+        lp->nrzi = DEF_B_NRZI;
+    }
+    lp->bufsiz = DMA_BUFF_SIZE;
+    lp->tstate = IDLE;
+
+    chipset_init(dev);
+
+    if (dev->base_addr & CHANA) {
+        /* Note that a single IRQ services 2 devices (A and B channels)
+        */
+
+	/*
+	 * We disable the dma for a while, we have to get ints working
+	 * properly first!!
+	 */
+	lp->dmachan = 0;
+	
+        if (dev->irq < 2) {
+            autoirq_setup(0);
+            
+            /* Turn on PT interrupts */
+            save_flags(flags);
+            cli();
+            outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
+            restore_flags(flags);
+
+            /* Set a timer interrupt */
+            tdelay(lp, 1);
+            dev->irq = autoirq_report(20);
+
+	    /* Turn off PT interrupts */
+	    save_flags(flags);
+	    cli();
+            outb_p( (pt_sercfg  &= ~ PT_EI), lp->cardbase + INT_CFG);            
+            restore_flags(flags);
+
+            if (!dev->irq) {
+                printk("PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
+            }
+        }
+
+        printk("PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
+
+        /* This board has jumpered interrupts. Snarf the interrupt vector
+         * now.  There is no point in waiting since no other device can use
+         * the interrupt, and this marks the 'irqaction' as busy.
+         */
+        {
+            int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt");
+            if (irqval) {
+                printk("PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
+                    dev->irq, irqval);
+                return EAGAIN;
+            }
+        }
+
+        /* Grab the region */
+        snarf_region(ioaddr & 0x3f0, PT_TOTAL_SIZE);
+    } /* A port */
+    dev->open = pt_open;
+    dev->stop = pt_close;
+    dev->do_ioctl = pt_ioctl;
+    dev->hard_start_xmit = pt_send_packet;
+    dev->get_stats = pt_get_stats;
+
+    /* Fill in the fields of the device structure */
+    for (i=0; i < DEV_NUMBUFFS; i++)
+        skb_queue_head_init(&dev->buffs[i]);
+
+    dev->hard_header = pt_header;
+    dev->rebuild_header = pt_rebuild_header;
+    dev->set_mac_address = pt_set_mac_address;
+
+    dev->type = ARPHRD_AX25;            /* AF_AX25 device */
+    dev->hard_header_len = 73;      /* We do digipeaters now */
+    dev->mtu = 1500;                /* eth_mtu is default */
+    dev->addr_len = 7;               /* sizeof an ax.25 address */
+    memcpy(dev->broadcast, ax25_bcast, 7);
+    memcpy(dev->dev_addr, ax25_test, 7);
+
+    /* New style flags */
+    dev->flags = 0;
+    dev->family = AF_INET;
+    dev->pa_addr = 0;
+    dev->pa_brdaddr = 0;
+    dev->pa_mask = 0;
+    dev->pa_alen = sizeof(unsigned long);
+
+    return 0;
+} /* pt_probe() */
+
+
+/* Open/initialise the board.  This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that 'should' only be set once at bott, so that there is
+ * a non-reboot way to recover if something goes wrong.
+ * derived from last half of tsync_attach()
+ */
+static int pt_open(struct device *dev)
+{
+    unsigned long flags;
+    struct pt_local *lp = dev->priv;
+    static first_time = 1;
+    
+    if (dev->base_addr & CHANA)
+    {
+        if (first_time)
+        {
+            if (request_dma(dev->dma, "pt"))
+            {
+                free_irq(dev->irq);
+                return -EAGAIN;
+            }
+        }
+ 	irq2dev_map[dev->irq] = dev;
+ 
+         /* Reset hardware */	
+         chipset_init(dev);
+     }
+     lp->tstate = IDLE;
+    
+     if (dev->base_addr & CHANA)
+     {
+         scc_init(dev);
+         scc_init(dev->next);
+     }
+     /* Save a copy of register RR0 for comparing with later on */
+     /* We always put 0 in zero count */
+     lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT;
+    
+    /* master interrupt enable */
+    save_flags(flags);
+    cli();
+    wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV);
+    outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);    
+    restore_flags(flags);
+
+    lp->open_time = jiffies;
+
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    dev->start = 1;
+    first_time = 0;
+	
+    return 0;
+} /* pt_open() */
+
+static int pt_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct pt_local *lp = (struct pt_local *) dev->priv;
+
+#ifdef PT_DEBUG
+	printk("PTd pt_send_packet(): (%d)\n", lp->base & CHANA);
+#endif			
+	/* If some higher layer thinks we've missed an tx-done interrupt
+	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	   itself.*/
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+	hardware_send_packet(lp, skb);
+	dev->trans_start = jiffies;
+	
+	return 0;
+}
+				   
+	   
+
+/* The inverse routine to pt_open() */
+static int pt_close(struct device *dev)
+{
+	unsigned long flags;
+	struct pt_local *lp = dev->priv;
+	struct sk_buff *ptr = NULL;
+	int cmd;
+
+	cmd = lp->base + CTL;
+		
+	save_flags(flags);
+	cli();
+	
+	/* Reset SCC or channel */
+	chipset_init(dev);	
+	disable_dma(lp->dmachan);
+
+	lp->open_time = 0;
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	/* Free any buffers left in the hardware transmit queue */
+	while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
+		free_p(ptr);
+		
+	restore_flags(flags);
+	
+#ifdef PT_DEBUG
+	printk("PTd pt_close(): Closing down channel (%d).\n", lp->base & CHANA);	
+#endif	
+	
+	return 0;
+} /* pt_close() */
+
+
+static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+    unsigned long flags;
+    struct pt_req rq;
+    struct pt_local *lp = (struct pt_local *) dev->priv;
+
+    int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req));
+    if (ret)
+	return ret;
+
+    if (cmd != SIOCDEVPRIVATE)
+        return -EINVAL;
+        
+    memcpy_fromfs(&rq, ifr->ifr_data, sizeof(struct pt_req));
+
+    switch (rq.cmd) {
+    case SIOCSPIPARAM:
+
+	if (!suser())
+	    return -EPERM;
+	save_flags(flags);
+	cli();
+	lp->txdelay = rq.txdelay;
+	lp->persist = rq.persist;
+	lp->slotime = rq.slotime;
+	lp->squeldelay = rq.squeldelay;
+	lp->clockmode = rq.clockmode;
+	lp->speed = rq.speed;
+	pt_open(&pt0a);
+	restore_flags(flags);
+	ret = 0;
+	break;
+
+    case SIOCSPIDMA:
+
+	if (!suser())
+	    return -EPERM;
+	ret = 0;
+	if (dev->base_addr & CHANA) {   /* if A channel */
+	   if (rq.dmachan < 1 || rq.dmachan > 3)
+		return -EINVAL;
+	   save_flags(flags);
+	   cli();
+	   pt_close(dev);
+	   free_dma(lp->dmachan);
+	   dev->dma = lp->dmachan = rq.dmachan;
+	   if (request_dma(lp->dmachan,"pt")) 
+		ret = -EAGAIN;
+	   pt_open(dev);
+	   restore_flags(flags);
+	}
+	break;
+
+    case SIOCSPIIRQ:
+	ret = -EINVAL;      /* add this later */
+	break;
+
+    case SIOCGPIPARAM:
+    case SIOCGPIDMA:
+    case SIOCGPIIRQ:
+
+	rq.speed = lp->speed;
+	rq.txdelay = lp->txdelay;
+	rq.persist = lp->persist;
+	rq.slotime = lp->slotime;
+	rq.squeldelay = lp->squeldelay;
+	rq.clockmode = lp->clockmode;
+	rq.dmachan = lp->dmachan;
+	rq.irq = dev->irq;
+	memcpy_tofs(ifr->ifr_data, &rq, sizeof(struct pt_req));
+	ret = 0;
+	break;
+
+    default:
+	ret = -EINVAL;
+    }
+    return ret;
+}
+
+/* Get the current statistics.	This may be called with the card open or
+   closed. */
+static struct netstats *
+ pt_get_stats(struct device *dev)
+{
+    struct pt_local *lp = (struct pt_local *) dev->priv;
+
+    return &lp->stats;
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
+ * End:
+ */
+
+
+static void tdelay(struct pt_local *lp, int time)
+{
+	/* For some reason, we turn off the Tx interrupts here! */
+	if (!lp->dmachan)
+		wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
+	
+    if (lp->base & CHANA) {
+        outb_p(time & 0xff, lp->cardbase + TMR1);
+        outb_p((time >> 8)&0xff, lp->cardbase + TMR1);
+    } else {
+        outb_p(time & 0xff, lp->cardbase + TMR2);
+        outb_p((time >> 8)&0xff, lp->cardbase + TMR2);
+    }
+} /* tdelay */
+
+
+static void pt_txisr(struct pt_local *lp)
+{
+	unsigned long flags;
+	int cmd;
+	unsigned char c;
+	
+	save_flags(flags);
+	cli();
+	cmd = lp->base + CTL;
+
+#ifdef PT_DEBUG
+	printk("PTd pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+#endif	
+	
+	switch (lp->tstate) 
+	{
+	case CRCOUT:
+	    lp->tstate = FLAGOUT;
+	    tdelay(lp, lp->squeldelay);
+	    restore_flags(flags);
+	    return;
+	    
+	case IDLE:
+	    /* Transmitter idle. Find a frame for transmission */
+	    if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
+	    {
+	        /* Nothing to send - return to receive mode
+	         * Tx off now - flag should have gone
+	         */
+	        pt_rts(lp, OFF);
+	        
+	        restore_flags(flags);
+	        return;
+	    }
+	    if (!lp->dmachan)
+	    {
+		    lp->txptr = lp->sndbuf->data;
+		    lp->txptr++;		/* Ignore KISS control byte */
+		    lp->txcnt = (int) lp->sndbuf->len - 1;
+		}
+	    /* If a buffer to send, drop though here */
+	
+	case DEFER:
+	    /* Check DCD - debounce it */
+	    /* See Intel Microcommunications Handbook p2-308 */
+	    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	    if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
+	    {
+	        lp->tstate = DEFER;
+	        tdelay(lp, 100);
+	        /* DEFER until DCD transistion or timeout */
+	        wrtscc(lp->cardbase, cmd, R15, DCDIE);
+	        restore_flags(flags);
+	        return;
+	    }
+	    if (random() > lp->persist)
+	    {
+	        lp->tstate = DEFER;
+	        tdelay(lp, lp->slotime);
+	        restore_flags(flags);
+	        return;
+	    }
+	    pt_rts(lp, ON);		/* Tx on */
+	    if (lp->dmachan)
+	    	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+	    lp->tstate = ST_TXDELAY;
+	    tdelay(lp, lp->txdelay);
+	    restore_flags(flags);
+	    return;
+	
+	case ACTIVE:
+	    /* Here we are actively sending a frame */
+	    if (lp->txcnt--)
+	    {
+	        /* XLZ - checkout Gracilis PT code to see if the while 
+	         * loop is better or not.
+	         */
+	        c = *lp->txptr++;
+	        /* next char is gone */
+	        wrtscc(lp->cardbase, cmd, R8, c);
+	        /* stuffing a char satisfies interrupt condition */
+	    } else {
+	        /* No more to send */
+	        free_p(lp->sndbuf);
+	        lp->sndbuf = NULL;
+	        if ((rdscc(lp->cardbase, cmd, R0) & TxEOM))
+	        {
+	            /* Did we underrum */
+	            lp->stats.tx_errors++;
+	            lp->stats.tx_fifo_errors++;
+	            wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+	            lp->tstate = FLAGOUT;
+	            tdelay(lp, lp->squeldelay);
+	            restore_flags(flags);
+	            return;
+	        }
+	        lp->tstate = UNDERRUN;
+	        /* Send flags on underrun */
+	       if (lp->nrzi)
+	       {
+	           wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+	       } else {
+	           wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ);
+	       }
+	       /* Reset Tx interrupt pending */
+	       wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);
+	   }
+	   restore_flags(flags);
+	   return;
+	default:
+		printk("PT: pt_txisr(): Invlaid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
+		pt_rts(lp, OFF);
+		lp->tstate = IDLE;
+		break;		
+    } 				/*switch */
+    restore_flags(flags);
+}
+
+static void pt_rxisr(struct device *dev)
+{
+    struct pt_local *lp = (struct pt_local*) dev->priv;
+    int cmd = lp->base + CTL;
+    int bytecount;
+    unsigned long flags;
+    char rse;
+    struct sk_buff *skb;
+    int sksize, pkt_len;
+    struct mbuf *cur_buf;
+    unsigned char *cfix;
+
+    save_flags(flags);
+    cli();
+
+    /* Get status byte from R1 */
+    rse = rdscc(lp->cardbase, cmd, R1);
+    
+#ifdef PT_DEBUG
+    printk("PTd pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
+#endif        
+
+	if (lp->dmachan && (rse & Rx_OVR))
+		lp->rstate = RXERROR;
+		
+    if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan) 
+    {
+        /* There is a char to be stored
+         * Read special condition bits before reading the data char
+         */
+        if (rse & Rx_OVR)
+        {
+             /* Rx overrun - toss buffer */
+             /* wind back the pointers */
+             lp->rcp = lp->rcvbuf->data;
+             lp->rcvbuf->cnt = 0;
+             lp->rstate = RXERROR;
+             lp->stats.rx_errors++;
+             lp->stats.rx_fifo_errors++;
+         } else if (lp->rcvbuf->cnt >= lp->bufsiz)
+             {
+                 /* Too large packet
+                  * wind back Rx buffer pointers
+                  */
+                 lp->rcp = lp->rcvbuf->data;
+                 lp->rcvbuf->cnt = 0;
+                 lp->rstate = TOOBIG;
+             }
+         /* ok, we can store the Rx char if no errors */
+         if (lp->rstate == ACTIVE)
+         {
+             *lp->rcp++ = rdscc(lp->cardbase, cmd, R8);
+             lp->rcvbuf->cnt++;
+         } else {
+             /* we got an error, dump the FIFO */
+             (void) rdscc(lp->cardbase, cmd, R8);
+             (void) rdscc(lp->cardbase, cmd, R8);
+             (void) rdscc(lp->cardbase, cmd, R8);
+             
+             /* Reset error latch */
+             wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+             lp->rstate = ACTIVE;
+             
+             /* Resync the SCC */
+             wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+             
+         }
+     }
+     
+     if (rse & END_FR)
+     {
+#ifdef PT_DEBUG
+	printk("PTd pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
+#endif	     
+		if (lp->dmachan)
+		{
+			clear_dma_ff(lp->dmachan);
+			bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
+		} else {
+			bytecount = lp->rcvbuf->cnt;
+		}
+			
+         /* END OF FRAME - Make sure Rx was active */
+         if (lp->rcvbuf->cnt > 0 || lp->dmachan)
+         {
+             if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10))
+             {
+                 if ((bytecount >= 10) && (rse & CRC_ERR))
+                 {
+                     lp->stats.rx_crc_errors++;
+                 }
+                 if (lp->dmachan)
+                 {
+                 	if (lp->rstate == RXERROR)
+                 	{
+                 		lp->stats.rx_errors++;
+                 		lp->stats.rx_over_errors++;
+                 	}
+                 	lp->rstate = ACTIVE;
+                 	setup_rx_dma(lp);
+                 } else {
+	                 /* wind back Rx buffer pointers */
+    	             lp->rcp = lp->rcvbuf->data;
+        	         lp->rcvbuf->cnt = 0;
+
+					/* Re-sync the SCC */
+					wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);			
+        	         
+        	     }
+#ifdef PT_DEBUG
+	printk("PTd pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
+#endif                 
+             } else {
+                 /* We have a valid frame */
+                 if (lp->dmachan)
+                 {
+                 	pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1;
+					/* Get buffer for next frame */
+                 	cur_buf = lp->rcvbuf;
+                 	switchbuffers(lp);
+                 	setup_rx_dma(lp);                 	
+                 } else {
+	                 pkt_len = lp->rcvbuf->cnt -= 2;  /* Toss 2 CRC bytes */
+    	             pkt_len += 1;	/* make room for KISS control byte */
+        		}         
+
+                 /* Malloc up new buffer */
+                 sksize = pkt_len;
+                 skb = dev_alloc_skb(sksize);
+                 if (skb == NULL)
+                 {
+                     printk("PT: %s: Memory squeze, dropping packet.\n", dev->name);
+                     lp->stats.rx_dropped++;
+                     restore_flags(flags);
+                     return;
+                 }
+                 skb->dev = dev;
+                 
+                 /* KISS kludge = prefix with a 0 byte */
+                 cfix=skb_put(skb,pkt_len);
+                 *cfix++=0;
+                 /* skb->data points to the start of sk_buff area */
+                 if (lp->dmachan)
+                 	memcpy(cfix, (char*)cur_buf->data, pkt_len - 1);
+                 else
+                    memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
+                 skb->protocol = ntohs(ETH_P_AX25);
+                 skb->mac.raw=skb->data;
+                 IS_SKB(skb);
+                 netif_rx(skb);
+                 lp->stats.rx_packets++;
+                 if (!lp->dmachan)
+                 {
+	                 /* packet queued - wind back buffer for next frame */
+    	             lp->rcp = lp->rcvbuf->data;
+	                 lp->rcvbuf->cnt = 0;
+	             }
+             } /* good frame */
+         } /* check active Rx */
+         /* Clear error status */
+         lp->rstate = ACTIVE;
+         /* Reset error latch */
+     } /* end EOF check */
+     wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+     restore_flags(flags);
+} /* pt_rxisr() */
+
+/* Read the SCC channel till no more data in receiver */
+static void empty_scc(struct pt_local *lp)
+{
+    while( rdscc(lp->cardbase, lp->base + CTL, R0) & Rx_CH_AV) {
+        /* Get data from Rx buffer and toss it */
+        (void) inb_p(lp->base + DATA);
+    }
+} /* empty_scc()*/
+
+/*
+ * This handles the two timer interrupts.
+ * This is a real bugger, cause you have to rip it out of the pi's
+ * external status code.  They use the CTS line or something.
+ */
+static void pt_tmrisr(struct pt_local *lp)
+{
+    unsigned long flags;
+
+#ifdef PT_DEBUG
+	printk("PTd pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+#endif		
+	
+    save_flags(flags);
+    cli();
+    
+    
+    switch (lp->tstate) 
+    {
+    /* Most of this stuff is in pt_exisr() */
+    case FLAGOUT:
+    case ST_TXDELAY:
+    case DEFER:
+/*    case ACTIVE:
+    case UNDERRUN:*/
+        pt_exisr(lp);
+        break; 
+      
+    default:
+	if (lp->base & CHANA)
+ 	    printk("PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
+	else
+	    printk("PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
+	break;		
+    } /* end switch */
+    restore_flags(flags);
+} /* pt_tmrisr() */
+
+	
+/*
+ * This routine is called by the kernel when there is an interrupt for the
+ * PT.
+ */
+static void pt_interrupt(int irq, struct pt_regs *regs)
+{
+    /* It's a tad dodgy here, but we assume pt0a until proven otherwise */
+    struct device *dev = &pt0a;
+    struct pt_local *lp = dev->priv;
+    unsigned char intreg;
+    unsigned char st;
+    register int cbase = dev->base_addr & 0x3f0;
+    unsigned long flags;
+    
+    /* Read the PT's interrupt register, this is not the SCC one! */
+    intreg = inb_p(cbase + INT_REG);
+    while(( intreg & 0x07) != 0x07) {
+        /* Read interrupt register pending from Channel A */
+        while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0)
+        {
+        	/* Read interrupt vector from R2, channel B */
+#ifdef PT_DEBUG
+		printk("PTd pt_interrupt(): R3 = %#3x", st);
+#endif		        
+/*        	st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
+#ifdef PT_DEBUG
+		printk(" R2 = %#3x.\n", st);
+#endif	
+			if (st & CHARxIP) {
+			    /* Channel A Rx */
+	            lp = (struct pt_local*)pt0a.priv;
+	            pt_rxisr(&pt0a);
+	        } else if (st & CHATxIP) {
+	            /* Channel A Tx */
+	            lp = (struct pt_local*)pt0a.priv;
+	            pt_txisr(lp);
+	        } else if (st & CHAEXT) {
+	        	/* Channel A External Status */
+	        	lp = (struct pt_local*)pt0a.priv;
+	        	pt_exisr(lp);
+	        } else if (st & CHBRxIP) {
+	        	/* Channel B Rx */
+	            lp= (struct pt_local*)pt0b.priv;
+	            pt_rxisr(&pt0b);
+	        } else if (st & CHBTxIP) {
+            	/* Channel B Tx */
+	            lp = (struct pt_local*)pt0b.priv;
+	            pt_txisr(lp);
+			} else if (st & CHBEXT) {
+	        	/* Channel B External Status */
+	        	lp = (struct pt_local*)pt0b.priv;
+	        	pt_exisr(lp);
+	        }
+            /* Reset highest interrupt under service */
+            save_flags(flags);
+            cli();
+            wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
+            restore_flags(flags);
+        }  /* end of SCC ints */
+        
+        if (!(intreg & PT_TMR1_MSK))
+        {
+            /* Clear timer 1 */
+            inb_p(cbase + TMR1CLR);
+
+            pt_tmrisr( (struct pt_local*)pt0a.priv);
+        }
+
+        if (!(intreg & PT_TMR2_MSK))
+        {
+            /* Clear timer 2 */
+            inb_p(cbase + TMR2CLR);
+
+            pt_tmrisr( (struct pt_local*)pt0b.priv);
+        }
+
+        /* Get the next PT interrupt vector */
+        intreg = inb_p(cbase + INT_REG);
+    } /* while (intreg) */
+} /* pt_interrupt() */
+
+
+static void pt_exisr(struct pt_local *lp)
+{
+    unsigned long flags;
+    int cmd = lp->base + CTL;
+    unsigned char st;
+    char c;
+    int length;
+    
+    save_flags(flags);
+    cli();
+    
+    /* Get external status */
+    st = rdscc(lp->cardbase, cmd, R0);
+
+#ifdef PT_DEBUG
+	printk("PTd exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
+#endif	
+    /* Reset external status latch */
+    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+    
+    if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan)
+    {
+    	setup_rx_dma(lp);
+    	lp->rstate = ACTIVE;
+    }
+
+    switch (lp->tstate)
+    {
+    case ACTIVE:		/* Unexpected underrun */
+#ifdef PT_DEBUG
+	printk("PTd exisr(): unexpected underrun detected.\n");
+#endif	    
+        free_p(lp->sndbuf);
+        lp->sndbuf = NULL;
+        if (!lp->dmachan)
+        {
+	        wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+	        lp->stats.tx_errors++;
+	        lp->stats.tx_fifo_errors++;
+	    }
+        lp->tstate = FLAGOUT;
+        tdelay(lp, lp->squeldelay);
+        restore_flags(flags);
+        return;
+    case UNDERRUN:		
+        lp->tstate = CRCOUT;
+        restore_flags(flags);
+        return;
+    case FLAGOUT:
+        /* squeldelay has timed out */
+        /* Find a frame for transmission */
+        if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
+        {
+            /* Nothing to send - return to Rx mode */
+            pt_rts(lp, OFF);
+            lp->tstate = IDLE;
+            restore_flags(flags);
+            return;
+        }
+        if (!lp->dmachan)
+        {
+	        lp->txptr = lp->sndbuf->data;
+    	    lp->txptr++;		/* Ignore KISS control byte */
+	        lp->txcnt = (int) lp->sndbuf->len - 1;
+	    }
+        /* Fall through if we have a packet */
+    
+    case ST_TXDELAY:
+    	if (lp->dmachan)
+    	{
+    		/* Disable DMA chan */
+    		disable_dma(lp->dmachan);
+    		
+    		/* Set up for TX dma */
+    		wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+    		
+    		length = lp->sndbuf->len - 1;
+    		memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
+    		
+    		/* Setup DMA controller for Tx */
+    		setup_tx_dma(lp, length);
+    		
+    		enable_dma(lp->dmachan);
+    		
+    		/* Reset CRC, Txint pending */
+    		wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
+    		
+    		/* Allow underrun only */
+    		wrtscc(lp->cardbase, cmd, R15, TxUIE);
+    		
+    		/* Enable TX DMA */
+    		wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
+    		
+    		/* Send CRC on underrun */
+    		wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+    		
+    		lp->tstate = ACTIVE;
+    		break;
+    	} 
+        /* Get first char to send */
+        lp->txcnt--;
+        c = *lp->txptr++;
+        /* Reset CRC for next frame */
+        wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);
+        
+        /* send abort on underrun */
+        if (lp->nrzi)	
+        {
+            wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+        } else {
+            wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER);
+        }
+        /* send first char */
+        wrtscc(lp->cardbase, cmd, R8, c);
+        
+        /* Reset end of message latch */
+        wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+        
+        /* stuff an extra one in */
+/*        while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt)
+        {
+            lp->txcnt--;
+            c = *lp->txptr++;
+            wrtscc(lp->cardbase, cmd, R8, c);
+        }*/
+        
+        /* select Tx interrupts to enable */
+        /* Allow underrun int only */
+        wrtscc(lp->cardbase, cmd, R15, TxUIE);
+        
+        /* Reset external interrupts */
+        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+        
+        /* Tx and Rx ints enabled */
+        wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
+        
+        lp->tstate = ACTIVE;
+        restore_flags(flags);
+        return;
+        
+        /* slotime has timed out */
+    case DEFER:
+        /* Check DCD - debounce it
+         * see Intel Micrommunications Handbook, p2-308
+         */
+        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+        if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
+        {
+            lp->tstate = DEFER;
+            tdelay(lp, 100);
+            /* DEFER until DCD transistion or timeout */
+            wrtscc(lp->cardbase, cmd, R15, DCDIE);
+            restore_flags(flags);
+            return;
+        }
+        if (random() > lp->persist)
+        {
+            lp->tstate = DEFER;
+            tdelay(lp, lp->slotime);
+            restore_flags(flags);
+            return;
+        }
+        if (lp->dmachan)
+        	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+        pt_rts(lp, ON);			/* Tx on */
+        lp->tstate = ST_TXDELAY;
+        tdelay(lp, lp->txdelay);
+        restore_flags(flags);
+        return;
+ 
+ 	/* Only for int driven parts */
+ 	if (lp->dmachan)
+ 	{
+ 		restore_flags(flags);
+ 		return;
+ 	}
+ 	
+    } /* end switch */
+    /*
+     * Rx mode only
+     * This triggers when hunt mode is entered, & since an ABORT
+     * automatically enters hunt mode, we use that to clean up
+     * any waiting garbage
+     */
+    if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
+    {
+#ifdef PT_DEBUG
+	printk("PTd exisr(): abort detected.\n");
+#endif        
+  		/* read and dump all of SCC Rx FIFO */
+        (void) rdscc(lp->cardbase, cmd, R8);
+        (void) rdscc(lp->cardbase, cmd, R8);
+        (void) rdscc(lp->cardbase, cmd, R8);      
+        
+        lp->rcp = lp->rcvbuf->data;
+        lp->rcvbuf->cnt = 0;
+        
+		/* Re-sync the SCC */
+		wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);			
+
+    }
+    
+    /* Check for DCD transistions */
+    if ( (st & DCD) != (lp->saved_RR0 & DCD))
+    {
+#ifdef PT_DEBUG    
+        printk("PTd: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
+#endif
+		if (st & DCD)
+		{
+			/* Check that we don't already have some data */
+			if (lp->rcvbuf->cnt > 0)
+			{
+#ifdef PT_DEBUG
+				printk("PTd pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
+#endif					
+				/* wind back buffers */
+				lp->rcp = lp->rcvbuf->data;
+				lp->rcvbuf->cnt = 0;
+			}
+		} else {  /* DCD off */
+			
+			/* read and dump al SCC FIFO */
+			(void)rdscc(lp->cardbase, cmd, R8);
+			(void)rdscc(lp->cardbase, cmd, R8);
+			(void)rdscc(lp->cardbase, cmd, R8);
+
+			/* wind back buffers */
+			lp->rcp = lp->rcvbuf->data;
+			lp->rcvbuf->cnt = 0;
+			
+			/* Re-sync the SCC */
+			wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);			
+		}		
+			
+    }
+    /* Update the saved version of register RR) */
+    lp->saved_RR0 = st &~ ZCOUNT;
+    restore_flags(flags);
+
+} /* pt_exisr() */
+
+/* This function is used to send the KISS params back to the kernel itself,
+ * just like the TNCs do (I think)
+ * It's a (bit of a) kludge
+ */
+static void send_kiss(struct device *dev, unsigned char arg, unsigned char val)
+{
+	struct sk_buff *skb;
+	unsigned char *cfix;
+/*	struct pt_local *lp = (struct pt_local*)dev->priv;*/
+	
+	
+	skb = dev_alloc_skb(2);
+	if (skb == NULL)
+	{
+		printk("PT: send_kiss(): Memory squeeze, dropping KISS reply.\n");
+		return;
+	}
+	skb->dev = dev;
+	cfix = skb_put(skb, 2);
+	cfix[0]=arg;
+	cfix[1]=val;
+	skb->protocol=htons(ETH_P_AX25);
+	skb->mac.raw=skb->data;
+	IS_SKB(skb);
+	netif_rx(skb);
+}
+	
diff -u --recursive --new-file v1.3.50/linux/drivers/net/pt.h linux/drivers/net/pt.h
--- v1.3.50/linux/drivers/net/pt.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/pt.h	Tue Dec 26 06:03:00 1995
@@ -0,0 +1,176 @@
+/*
+ * pt.h: Linux device driver for the Gracilis PackeTwin
+ * Copyright (C) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
+ *
+ * Please read the notice appearing at the top of the file pt.c
+ */
+#define DMA_BUFF_SIZE 2200
+
+/* Network statistics, with the same names as 'struct enet_statistics'. */
+#define netstats enet_statistics
+
+#define ON 1
+#define OFF 0
+
+
+/* Register offset info, specific to the PT
+ * E.g., to read the data port on channel A, use
+ *  inportb(pichan[dev].base + CHANA + DATA)
+ */
+#define CHANB   0   /* Base of channel B regs */
+#define CHANA   2   /* Base of channel A regs */
+
+/* 8530 ports on each channel */
+#define CTL 0
+#define DATA    1
+
+#define DMAEN   0x8 /* Offset off DMA Enable register */
+
+/* Timer chip offsets */
+#define TMR0    0x4 /* Offset of timer 0 register */
+#define TMR1    0x5 /* Offset of timer 1 register */
+#define TMR2    0x6 /* Offset of timer 2 register */
+#define TMRCMD  0x7 /* Offset of timer command register */
+#define INT_REG	0x8
+#define TMR1CLR 0x9
+#define TMR2CLR 0xa
+
+/* Interrupt register equates */
+#define PT_SCC_MSK	0x1
+#define PT_TMR1_MSK	0x2
+#define PT_TMR2_MSK	0x4
+
+/* Serial/interrupt register equates */
+#define PT_DTRA_ON	0x1
+#define PT_DTRB_ON	0x2
+#define PT_EXTCLKA	0x4
+#define PT_EXTCLKB	0x8
+#define PT_LOOPA_ON	0x10
+#define PT_LOOPB_ON	0x20
+#define PT_EI		0x80
+
+/* Timer chip equates */
+#define SC0 0x00 /* Select counter 0 */
+#define SC1 0x40 /* Select counter 1 */
+#define SC2 0x80 /* Select counter 2 */
+#define CLATCH  0x00 /* Counter latching operation */
+#define MSB 0x20 /* Read/load MSB only */
+#define LSB 0x10 /* Read/load LSB only */
+#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
+#define MODE0   0x00 /* Interrupt on terminal count */
+#define MODE1   0x02 /* Programmable one shot */
+#define MODE2   0x04 /* Rate generator */
+#define MODE3   0x06 /* Square wave rate generator */
+#define MODE4   0x08 /* Software triggered strobe */
+#define MODE5   0x0a /* Hardware triggered strobe */
+#define BCD 0x01 /* BCD counter */
+
+/* DMA controller registers */
+#define DMA_STAT    8   /* DMA controller status register */
+#define DMA_CMD     8   /* DMA controller command register */
+#define DMA_MASK        10  /* DMA controller mask register */
+#define DMA_MODE        11  /* DMA controller mode register */
+#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
+/* DMA data */
+#define DMA_DISABLE (0x04)  /* Disable channel n */
+#define DMA_ENABLE  (0x00)  /* Enable channel n */
+/* Single transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE (0x54)
+/* Single transfers, incr. address, no auto init, reads, ch. n */
+#define DMA_TX_MODE (0x48)
+
+/* Write registers */
+#define DMA_CFG		0x08
+#define SERIAL_CFG	0x09
+#define INT_CFG		0x09	/* shares with serial config */
+#define DMA_CLR_FF	0x0a
+
+#define SINGLE 3686400
+#define DOUBLE 7372800
+#define XTAL   ((long) 6144000L)
+
+#define SIOCGPIPARAM		0x5000	/* get PI parameters */
+#define SIOCSPIPARAM		0x5001	/* set */
+#define SIOCGPIBAUD		0x5002	/* get only baud rate */
+#define SIOCSPIBAUD		0x5003	
+#define SIOCGPIDMA		0x5004	/* get only DMA */
+#define SIOCSPIDMA		0x5005	
+#define SIOCGPIIRQ		0x5006	/* get only IRQ */
+#define SIOCSPIIRQ		0x5007	
+
+struct pt_req  {
+    int cmd;
+    int speed;
+    int clockmode;
+    int txdelay;
+    unsigned char persist;
+    int slotime; 
+    int squeldelay;
+    int dmachan;    
+    int irq;    
+};
+
+/* SCC Interrupt vectors, if we have set 'status low' */
+#define CHBTxIV		0x00
+#define CHBEXTIV	0x02
+#define CHBRxIV		0x04
+#define CHBSRCIV	0x06
+#define CHATxIV		0x08
+#define CHAEXTIV	0x0a
+#define CHARxIV		0x0c
+#define CHASRCIV	0x0e
+
+
+#ifdef __KERNEL__
+
+/* Information that needs to be kept for each channel. */
+struct pt_local {
+    struct netstats stats; /* %%%dp*/
+    long open_time;             /* Useless example local info. */
+    unsigned long xtal; 
+
+    struct mbuf *rcvbuf;/* Buffer for current rx packet */
+    struct mbuf *rxdmabuf1; /* DMA rx buffer */
+    struct mbuf *rxdmabuf2; /* DMA rx buffer */
+
+    int bufsiz;         /* Size of rcvbuf */
+    char *rcp;          /* Pointer into rcvbuf */
+
+    struct sk_buff_head sndq;  /* Packets awaiting transmission */
+    int sndcnt;         /* Number of packets on sndq */
+    struct sk_buff *sndbuf;/* Current buffer being transmitted */
+    char *txdmabuf;     /* Transmit DMA buffer */
+	char *txptr;		/* Used by B port tx */
+	int txcnt;			
+    char tstate;        /* Transmitter state */
+#define IDLE    0       /* Transmitter off, no data pending */
+#define ACTIVE  1       /* Transmitter on, sending data */
+#define UNDERRUN 2      /* Transmitter on, flushing CRC */
+#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
+#define DEFER 4         /* Receive Active - DEFER Transmit */
+#define ST_TXDELAY 5    /* Sending leading flags */
+#define CRCOUT 6
+    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
+/* Normal state is ACTIVE if Receive enabled */
+#define RXERROR 2       /* Error -- Aborting current Frame */
+#define RXABORT 3       /* ABORT sequence detected */
+#define TOOBIG 4        /* too large a frame to store */
+	
+    int dev;            /* Device number */
+    int base;       /* Base of I/O registers */
+    int cardbase;     /* Base address of card */
+    int stata;        /* address of Channel A status regs */
+    int statb;        /* address of Channel B status regs */
+    int speed;        /* Line speed, bps */
+    int clockmode;    /* tapr 9600 modem clocking option */
+    int txdelay;      /* Transmit Delay 10 ms/cnt */
+    unsigned char persist;       /* Persistence (0-255) as a % */
+    int slotime;      /* Delay to wait on persistence hit */
+    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
+    struct iface *iface;    /* Associated interface */
+    int dmachan;           /* DMA channel for this port */
+    char saved_RR0;	/* The saved version of RR) that we compare with */
+    int nrzi;			/* Do we use NRZI (or NRZ) */
+};
+
+#endif
diff -u --recursive --new-file v1.3.50/linux/fs/Config.in linux/fs/Config.in
--- v1.3.50/linux/fs/Config.in	Fri Nov 17 08:42:27 1995
+++ linux/fs/Config.in	Tue Dec 26 06:03:00 1995
@@ -4,6 +4,7 @@
 mainmenu_option next_comment
 comment 'Filesystems'
 
+bool	 'Quota support' CONFIG_QUOTA
 tristate 'Standard (minix) fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
@@ -22,4 +23,6 @@
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
-tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS
+if [ "$CONFIG_INET" = "y" ]; then
+  tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS
+fi
diff -u --recursive --new-file v1.3.50/linux/fs/Makefile linux/fs/Makefile
--- v1.3.50/linux/fs/Makefile	Mon Dec 11 15:42:03 1995
+++ linux/fs/Makefile	Tue Dec 26 06:03:00 1995
@@ -13,10 +13,16 @@
 O_OBJS    = open.o read_write.o inode.o devices.o file_table.o buffer.o \
 		super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
 		ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
-		dcache.o dquot.o $(BINFMTS) 
+		dcache.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv smbfs
+
+ifeq ($(CONFIG_QUOTA),y)
+O_OBJS += dquot.o
+else
+O_OBJS += noquot.o
+endif
 
 ifeq ($(CONFIG_MINIX_FS),y)
 SUB_DIRS += minix
diff -u --recursive --new-file v1.3.50/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.50/linux/fs/buffer.c	Tue Dec 26 04:45:37 1995
+++ linux/fs/buffer.c	Wed Dec 27 08:14:41 1995
@@ -950,7 +950,7 @@
 	if (unused_list)
 		return;
 
-	if (!(bh = (struct buffer_head*) get_free_page(GFP_BUFFER)))
+	if (!(bh = (struct buffer_head*) get_free_page(GFP_KERNEL)))
 		return;
 
 	for (nr_buffer_heads+=i=PAGE_SIZE/sizeof*bh ; i>0; i--) {
@@ -1014,140 +1014,60 @@
 
 static void read_buffers(struct buffer_head * bh[], int nrbuf)
 {
-	int i;
-	int bhnum = 0;
-	struct buffer_head * bhr[MAX_BUF_PER_PAGE];
-
-	for (i = 0 ; i < nrbuf ; i++) {
-		if (bh[i] && !buffer_uptodate(bh[i]))
-			bhr[bhnum++] = bh[i];
-	}
-	if (bhnum)
-		ll_rw_block(READ, bhnum, bhr);
-	for (i = nrbuf ; --i >= 0 ; ) {
-		if (bh[i]) {
-			wait_on_buffer(bh[i]);
-		}
-	}
+	ll_rw_block(READ, nrbuf, bh);
+	bh += nrbuf;
+	do {
+		nrbuf--;
+		bh--;
+		wait_on_buffer(*bh);
+	} while (nrbuf > 0);
 }
 
-static int try_to_load_aligned(unsigned long address,
-	kdev_t dev, int b[], int size)
+int bread_page(unsigned long address, kdev_t dev, int b[], int size)
 {
-	struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE];
-	unsigned long offset;
-        int isize = BUFSIZE_INDEX(size);
-	int * p;
-	int block;
+	struct buffer_head *bh, *next, *arr[MAX_BUF_PER_PAGE];
+	int block, nr;
 
 	bh = create_buffers(address, size);
 	if (!bh)
-		return 0;
-	/* do any of the buffers already exist? punt if so.. */
-	p = b;
-	for (offset = 0 ; offset < PAGE_SIZE ; offset += size) {
-		block = *(p++);
-		if (!block)
-			goto not_aligned;
-		if (find_buffer(dev, block, size))
-			goto not_aligned;
-	}
-	tmp = bh;
-	p = b;
-	block = 0;
-	while (1) {
-		arr[block++] = bh;
-		bh->b_count = 1;
-		bh->b_flushtime = 0;
-		clear_bit(BH_Dirty, &bh->b_state);
-		clear_bit(BH_Uptodate, &bh->b_state);
-		clear_bit(BH_Req, &bh->b_state);
-		bh->b_dev = dev;
-		bh->b_blocknr = *(p++);
-		bh->b_list = BUF_CLEAN;
-		nr_buffers++;
-		nr_buffers_size[isize]++;
-		insert_into_queues(bh);
-		if (bh->b_this_page)
-			bh = bh->b_this_page;
-		else
-			break;
-	}
-	buffermem += PAGE_SIZE;
-	bh->b_this_page = tmp;
-	mem_map[MAP_NR(address)].count++;
-	buffer_pages[MAP_NR(address)] = bh;
-	read_buffers(arr,block);
-	while (block-- > 0)
-		brelse(arr[block]);
+		return -ENOMEM;
+	nr = 0;
+	next = bh;
+	do {
+		struct buffer_head * tmp;
+		block = *(b++);
+		if (!block) {
+			memset(next->b_data, 0, size);
+			continue;
+		}
+		tmp = get_hash_table(dev, block, size);
+		if (tmp) {
+			memcpy(next->b_data, tmp->b_data, size);
+			brelse(tmp);
+			continue;
+		}
+		arr[nr++] = next;
+		next->b_dev = dev;
+		next->b_blocknr = block;
+		next->b_count = 1;
+		next->b_flushtime = 0;
+		clear_bit(BH_Dirty, &next->b_state);
+		clear_bit(BH_Uptodate, &next->b_state);
+		clear_bit(BH_Req, &next->b_state);
+		next->b_list = BUF_CLEAN;
+	} while ((next = next->b_this_page) != NULL);
+
+	if (nr)
+		read_buffers(arr,nr);
 	++current->maj_flt;
-	return 1;
-not_aligned:
-	while ((tmp = bh) != NULL) {
+
+	while ((next = bh) != NULL) {
 		bh = bh->b_this_page;
-		put_unused_buffer_head(tmp);
+		put_unused_buffer_head(next);
 	}
 	return 0;
 }
 
-/*
- * Try-to-share-buffers tries to minimize memory use by trying to keep
- * both code pages and the buffer area in the same page. This is done by
- * trying to load them into memory the way we want them.
- *
- * This doesn't guarantee that the memory is shared, but should under most
- * circumstances work very well indeed (ie >90% sharing of code pages on
- * demand-loadable executables).
- */
-static inline int try_to_share_buffers(unsigned long address,
-	kdev_t dev, int *b, int size)
-{
-	struct buffer_head * bh;
-	int block;
-
-	block = b[0];
-	if (!block)
-		return 0;
-	bh = get_hash_table(dev, block, size);
-	if (!bh)
-		return try_to_load_aligned(address, dev, b, size);
-	brelse(bh);
-	return 0;
-}
-
-/*
- * bread_page reads four buffers into memory at the desired address. It's
- * a function of its own, as there is some speed to be got by reading them
- * all at the same time, not waiting for one to be read, and then another
- * etc. This also allows us to optimize memory usage by sharing code pages
- * and filesystem buffers..
- */
-void bread_page(unsigned long address, kdev_t dev, int b[], int size)
-{
-	struct buffer_head * bh[MAX_BUF_PER_PAGE];
-	unsigned long where;
-	int i, j;
-
-	if (try_to_share_buffers(address, dev, b, size))
-		return;
-	++current->maj_flt;
- 	for (i=0, j=0; j<PAGE_SIZE ; i++, j+= size) {
-		bh[i] = NULL;
-		if (b[i])
-			bh[i] = getblk(dev, b[i], size);
-	}
-	read_buffers(bh,i);
-	where = address;
- 	for (i=0, j=0; j<PAGE_SIZE ; i++, j += size, where += size) {
-		if (bh[i]) {
-			if (buffer_uptodate(bh[i]))
-				memcpy((void *) where, bh[i]->b_data, size);
-			brelse(bh[i]);
-		} else
-			memset((void *) where, 0, size);
-	}
-}
-
 #if 0
 /*
  * bwrite_page writes a page out to the buffer cache and/or the physical device.
@@ -1869,7 +1789,7 @@
 	in a few more things so "top" and /proc/2/{exe,root,cwd}
 	display semi-sane things. Not real crucial though...  */
 
-	sprintf(current->comm, "bdflush - kernel");
+	sprintf(current->comm, "kernel bdflush");
 
 	for (;;) {
 #ifdef DEBUG
diff -u --recursive --new-file v1.3.50/linux/fs/file_table.c linux/fs/file_table.c
--- v1.3.50/linux/fs/file_table.c	Mon Dec 11 15:42:04 1995
+++ linux/fs/file_table.c	Tue Dec 26 06:20:17 1995
@@ -4,6 +4,7 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -120,6 +121,8 @@
 	return NULL;
 }
 
+#ifdef CONFIG_QUOTA
+
 void add_dquot_ref(dev_t dev, short type)
 {
 	struct file *filp;
@@ -149,3 +152,5 @@
 		}
 	}
 }
+
+#endif
diff -u --recursive --new-file v1.3.50/linux/fs/inode.c linux/fs/inode.c
--- v1.3.50/linux/fs/inode.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/inode.c	Tue Dec 26 06:15:44 1995
@@ -430,28 +430,38 @@
 	return;
 }
 
+static inline unsigned long value(struct inode * inode)
+{
+	if (inode->i_lock)  
+		return 1000;
+	if (inode->i_dirt)
+		return 1000;
+	return inode->i_nrpages;
+}
+
 struct inode * get_empty_inode(void)
 {
 	static int ino = 0;
 	struct inode * inode, * best;
+	unsigned long badness = ~0UL;
 	int i;
 
-	if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 2))
+	if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 1))
 		grow_inodes();
 repeat:
 	inode = first_inode;
 	best = NULL;
 	for (i = 0; i<nr_inodes; inode = inode->i_next, i++) {
 		if (!inode->i_count) {
-			if (!best)
-				best = inode;
-			if (!inode->i_dirt && !inode->i_lock) {
+			unsigned long i = value(inode);
+			if (i < badness) {
 				best = inode;
-				break;
+				if ((badness = i) == 0)
+					break;
 			}
 		}
 	}
-	if (!best || best->i_dirt || best->i_lock)
+	if (badness > 20)
 		if (nr_inodes < NR_INODE) {
 			grow_inodes();
 			goto repeat;
diff -u --recursive --new-file v1.3.50/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- v1.3.50/linux/fs/nfs/rpcsock.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/nfs/rpcsock.c	Tue Dec 26 06:03:00 1995
@@ -37,7 +37,12 @@
 			  current->state = TASK_INTERRUPTIBLE; \
 			  schedule(); \
 			}
-#define dprintk		if (0) printk
+			
+#ifdef DEBUG_NFS			
+#define dprintk(x)		printk(x)
+#else
+#define	dprintk(x)
+#endif
 
 static inline void
 rpc_insque(struct rpc_sock *rsock, struct rpc_wait *slot)
@@ -52,9 +57,9 @@
 	rsock->tail = slot;
 	slot->prev = tmp;
 	slot->next = NULL;
-	dprintk("RPC: inserted %08lx into queue.\n", (long)slot);
-	dprintk("RPC: head = %08lx, tail = %08lx.\n",
-			(long) rsock->head, (long) rsock->tail);
+	dprintk(("RPC: inserted %08lx into queue.\n", (long)slot));
+	dprintk(("RPC: head = %08lx, tail = %08lx.\n",
+			(long) rsock->head, (long) rsock->tail));
 }
 
 static inline void
@@ -71,9 +76,9 @@
 		next->prev = prev;
 	else
 		rsock->tail = prev;
-	dprintk("RPC: removed %08lx from queue.\n", (long)slot);
-	dprintk("RPC: head = %08lx, tail = %08lx.\n",
-			(long) rsock->head, (long) rsock->tail);
+	dprintk(("RPC: removed %08lx from queue.\n", (long)slot));
+	dprintk(("RPC: head = %08lx, tail = %08lx.\n",
+			(long) rsock->head, (long) rsock->tail));
 }
 
 static inline int
@@ -83,12 +88,12 @@
 	unsigned long	oldfs;
 	int		result;
 
-	dprintk("RPC: sending %d bytes (buf %p)\n", len, msg->msg_iov[0].iov_base);
+	dprintk(("RPC: sending %d bytes (buf %p)\n", len, msg->msg_iov[0].iov_base));
 	oldfs = get_fs();
 	set_fs(get_ds());
 	result = sock->ops->sendmsg(sock, msg, len, 0, 0);
 	set_fs(oldfs);
-	dprintk("RPC: result = %d\n", result);
+	dprintk(("RPC: result = %d\n", result));
 
 	return result;
 }
@@ -104,7 +109,7 @@
 	struct file	*file = rsock->file;
 	select_table	wait_table;
 
-	dprintk("RPC: selecting on socket...\n");
+	dprintk(("RPC: selecting on socket...\n"));
 	wait_table.nr = 0;
 	wait_table.entry = &entry;
 	current->state = TASK_INTERRUPTIBLE;
@@ -120,7 +125,7 @@
 	} else if (wait_table.nr)
 		remove_wait_queue(entry.wait_address, &entry.wait);
 	current->state = TASK_RUNNING;
-	dprintk("RPC: ...Okay, there appears to be some data.\n");
+	dprintk(("RPC: ...Okay, there appears to be some data.\n"));
 	return 0;
 }
 
@@ -133,16 +138,16 @@
 	unsigned long	oldfs;
 	int		result;
 
-	dprintk("RPC: receiving %d bytes max (buf %p)\n", len, msg->msg_iov[0].iov_base);
+	dprintk(("RPC: receiving %d bytes max (buf %p)\n", len, msg->msg_iov[0].iov_base));
 	oldfs = get_fs();
 	set_fs(get_ds());
 	result = sock->ops->recvmsg(sock, msg, len, 1, flags, &alen);
 	set_fs(oldfs);
-	dprintk("RPC: result = %d\n", result);
+	dprintk(("RPC: result = %d\n", result));
 
 #if 0
 	if (alen != salen || memcmp(&sa, sap, alen)) {
-		dprintk("RPC: reply address mismatch... rejected.\n");
+		dprintk(("RPC: reply address mismatch... rejected.\n"));
 		result = -EAGAIN;
 	}
 #endif
@@ -173,11 +178,11 @@
 	iov.iov_base	=	(void *)sndbuf;
 	iov.iov_len	=	slen;
 
-	dprintk("RPC: placing one call, rsock = %08lx, slot = %08lx, "
+	dprintk(("RPC: placing one call, rsock = %08lx, slot = %08lx, "
 		"sap = %08lx, salen = %d, "
 		"sndbuf = %08lx, slen = %d, rcvbuf = %08lx, rlen = %d\n",
 		(long) rsock, (long) slot, (long) sap, 
-		salen, (long) sndbuf, slen, (long) rcvbuf, rlen);
+		salen, (long) sndbuf, slen, (long) rcvbuf, rlen));
 
 	result = rpc_sendmsg(rsock, &msg, slen);
 	if (result < 0)
@@ -201,7 +206,7 @@
 		/* wait for data to arrive */
 		result = rpc_select(rsock);
 		if (result < 0) {
-			dprintk("RPC: select error = %d\n", result);
+			dprintk(("RPC: select error = %d\n", result));
 			break;
 		}
 
@@ -214,7 +219,7 @@
 			case EAGAIN: case ECONNREFUSED:
 				continue;
 			default:
-				dprintk("rpc_call: recv error = %d\n", result);
+				dprintk(("rpc_call: recv error = %d\n", result));
 			case ERESTARTSYS:
 				return result;
 			}
@@ -234,7 +239,7 @@
 
 		if (!rovr || rovr->gotit) {
 			/* bad XID or duplicate reply, discard dgram */
-			dprintk("RPC: bad XID or duplicate reply.\n");
+			dprintk(("RPC: bad XID or duplicate reply.\n"));
 			iov.iov_base=(void *)&xid;
 			iov.iov_len=sizeof(xid);
 			rpc_recvmsg(rsock, &msg, sizeof(xid),0);
@@ -282,7 +287,7 @@
 	slot = NULL;
 
 	do {
-		dprintk("RPC call TP1\n");
+		dprintk(("RPC call TP1\n"));
 		current->timeout = jiffies + timeout;
 		if (slot == NULL) {
 			while ((slot = rsock->free) == NULL) {
@@ -296,12 +301,12 @@
 					goto timedout;
 				}
 				if (rsock->shutdown) {
-					printk("RPC: aborting call due to shutdown.\n");
+					dprintk(("RPC: aborting call due to shutdown.\n"));
 					current->timeout = 0;
 					return -EIO;
 				}
 			}
-			dprintk("RPC call TP2\n");
+			dprintk(("RPC call TP2\n"));
 			slot->gotit = 0;
 			slot->xid = *(u32 *)sndbuf;
 			slot->buf = rcvbuf;
@@ -310,15 +315,15 @@
 			rpc_insque(rsock, slot);
 		}
 
-		dprintk("RPC call TP3\n");
+		dprintk(("RPC call TP3\n"));
 		result = rpc_call_one(rsock, slot, sap, addrlen,
 					sndbuf, slen, rcvbuf, rlen);
 		if (result != -ETIMEDOUT)
 			break;
 
 timedout:
-		dprintk("RPC call TP4\n");
-		dprintk("RPC: rpc_call_one returned timeout.\n");
+		dprintk(("RPC call TP4\n"));
+		dprintk(("RPC: rpc_call_one returned timeout.\n"));
 		if (strategy->exponential)
 			timeout <<= 1;
 		else
@@ -329,10 +334,10 @@
 			break;
 	} while (1);
 
-	dprintk("RPC call TP5\n");
+	dprintk(("RPC call TP5\n"));
 	current->timeout = 0;
 	if (slot != NULL) {
-		dprintk("RPC call TP6\n");
+		dprintk(("RPC call TP6\n"));
 		rpc_remque(rsock, slot);
 		slot->next = rsock->free;
 		rsock->free = slot;
@@ -356,7 +361,7 @@
 	struct rpc_wait	*slot;
 	int		i;
 
-	dprintk("RPC: make RPC socket...\n");
+	dprintk(("RPC: make RPC socket...\n"));
 	if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
 		return NULL;
 	memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */
@@ -377,7 +382,7 @@
 	rsock->shutdown = 0;
 	 */
 
-	dprintk("RPC: made socket %08lx", (long) rsock);
+	dprintk(("RPC: made socket %08lx", (long) rsock));
 	return rsock;
 }
 
diff -u --recursive --new-file v1.3.50/linux/fs/nfs/sock.c linux/fs/nfs/sock.c
--- v1.3.50/linux/fs/nfs/sock.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/nfs/sock.c	Tue Dec 26 06:03:00 1995
@@ -92,7 +92,7 @@
 			}
 			if ((timeout.init_timeout <<= 1) >= maxtimeo)
 				timeout.init_timeout = maxtimeo;
-		} else if (result < 0) {
+		} else if (result < 0 && result != ERESTARTSYS) {
 			printk("NFS: notice message: result = %d.\n", result);
 		}
 	} while (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT));
diff -u --recursive --new-file v1.3.50/linux/fs/noquot.c linux/fs/noquot.c
--- v1.3.50/linux/fs/noquot.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/noquot.c	Tue Dec 26 06:03:00 1995
@@ -0,0 +1,79 @@
+/*
+ *	A Non implementation of disk quotas. Chainsawed from dquot.c by
+ *	Alan Cox <alan@lxorguk.ukuu.org.uk>. This saves us memory without
+ *	having zillions of #ifdefs (Or if it had been done right one
+ *	
+ *	QUOTA_OP(inode,func)
+ *
+ *	macro.)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/mount.h>
+
+#include <asm/segment.h>
+
+#ifndef min
+#define min(a,b) ((a) < (b)) ? (a) : (b)
+#endif
+
+int sync_dquots(kdev_t dev, short type)
+{
+	return(0);
+}
+
+/*
+ * Trash the cache for a certain type on a device.
+ */
+
+void invalidate_dquots(kdev_t dev, short type)
+{
+}
+
+/*
+ * Initialize pointer in a inode to the right dquots.
+ */
+void dquot_initialize(struct inode *inode, short type)
+{
+}
+
+void dquot_drop(struct inode *inode)
+{
+}
+
+void dquot_init(void)
+{
+}
+
+/*
+ * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
+ */
+
+int quota_off(kdev_t dev, short type)
+{
+	return(0);
+}
+
+int quota_on(kdev_t dev, short type, char *path)
+{
+	return(-ENOPKG);
+}
+
+/*
+ * Ok this is the systemcall interface, this communicates with
+ * the userlevel programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc in the future.
+ * But we probably better use rlimits for that.
+ */
+asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+	return(-ENOPKG);
+}
diff -u --recursive --new-file v1.3.50/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.50/linux/fs/proc/array.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/proc/array.c	Wed Dec 27 07:26:26 1995
@@ -469,7 +469,7 @@
 	else
 		state = "RSDZTW"[tsk->state];
 	vsize = eip = esp = 0;
-	if (tsk->mm) {
+	if (tsk->mm && tsk->mm != &init_mm) {
 		struct vm_area_struct *vma = tsk->mm->mmap;
 		while (vma) {
 			vsize += vma->vm_end - vma->vm_start;
@@ -628,7 +628,7 @@
 
 	if (!p || (tsk = *p) == NULL)
 		return 0;
-	if (tsk->mm) {
+	if (tsk->mm && tsk->mm != &init_mm) {
 		struct vm_area_struct * vma = tsk->mm->mmap;
 
 		while (vma) {
@@ -692,7 +692,7 @@
 	if (!p || !*p)
 		return -EINVAL;
 
-	if (!(*p)->mm || count == 0)
+	if (!(*p)->mm || (*p)->mm == &init_mm || count == 0)
 		return 0;
 
 	/* decode f_pos */
diff -u --recursive --new-file v1.3.50/linux/include/asm-i386/byteorder.h linux/include/asm-i386/byteorder.h
--- v1.3.50/linux/include/asm-i386/byteorder.h	Fri Oct  6 14:35:16 1995
+++ linux/include/asm-i386/byteorder.h	Tue Dec 26 06:44:35 1995
@@ -27,11 +27,15 @@
 extern __inline__ unsigned long int
 __ntohl(unsigned long int x)
 {
+#if defined(CONFIG_M486) && defined(__KERNEL__)
+	__asm__("bswap %0" : "=r" (x) : "0" (x));
+#else
 	__asm__("xchgb %b0,%h0\n\t"	/* swap lower bytes	*/
 		"rorl $16,%0\n\t"	/* swap words		*/
 		"xchgb %b0,%h0"		/* swap higher bytes	*/
 		:"=q" (x)
 		: "0" (x));
+#endif	
 	return x;
 }
 
diff -u --recursive --new-file v1.3.50/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v1.3.50/linux/include/asm-i386/smp.h	Fri Nov 17 08:42:28 1995
+++ linux/include/asm-i386/smp.h	Tue Dec 26 06:03:01 1995
@@ -5,6 +5,7 @@
 #ifndef ASSEMBLY
 
 #include <asm/i82489.h>
+#include <asm/bitops.h>
 #include <linux/tasks.h>
 #include <linux/ptrace.h>
 
@@ -178,6 +179,8 @@
 extern unsigned char *kernel_stacks[NR_CPUS];
 extern unsigned char boot_cpu_id;
 extern unsigned long cpu_present_map;
+extern volatile unsigned long smp_invalidate_needed;
+extern volatile unsigned long smp_spins;
 extern void smp_invalidate(void);
 extern volatile unsigned long kernel_flag, kernel_counter;
 extern volatile unsigned char active_kernel_processor;
@@ -240,6 +243,7 @@
  */
  
 #define PROC_CHANGE_PENALTY	20		/* Schedule penalty */
+
 
 #endif
 #endif
diff -u --recursive --new-file v1.3.50/linux/include/asm-mips/asm.h linux/include/asm-mips/asm.h
--- v1.3.50/linux/include/asm-mips/asm.h	Sun Dec 17 11:43:23 1995
+++ linux/include/asm-mips/asm.h	Tue Dec 26 06:03:01 1995
@@ -7,7 +7,7 @@
  *
  * Copyright (C) 1995 by Ralf Baechle
  *
- * Some usefull macros for MIPS assembler code
+ * Some useful macros for MIPS assembler code
  *
  * Some of the routines below contain useless nops that will be optimized
  * away by gas in -O mode. These nops are however required to fill delay
diff -u --recursive --new-file v1.3.50/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.50/linux/include/linux/fs.h	Tue Dec 26 04:45:41 1995
+++ linux/include/linux/fs.h	Tue Dec 26 05:02:30 1995
@@ -574,7 +574,7 @@
 }
 extern void set_blocksize(kdev_t dev, int size);
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
-extern void bread_page(unsigned long addr,kdev_t dev,int b[],int size);
+extern int bread_page(unsigned long addr,kdev_t dev,int b[],int size);
 extern void bwrite_page(unsigned long addr,kdev_t dev,int b[],int size);
 extern struct buffer_head * breada(kdev_t dev,int block, int size, 
 				   unsigned int pos, unsigned int filesize);
diff -u --recursive --new-file v1.3.50/linux/include/linux/net_alias.h linux/include/linux/net_alias.h
--- v1.3.50/linux/include/linux/net_alias.h	Wed Dec 13 09:02:47 1995
+++ linux/include/linux/net_alias.h	Wed Dec 27 07:46:29 1995
@@ -1,12 +1,25 @@
+/*
+ *		NET_ALIAS network device aliasing definitions.
+ *
+ *
+ * Version:	@(#)net_alias.h	0.43   12/20/95
+ *
+ * Author:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *	
+ */
+
 #ifndef _NET_ALIAS_H
 #define _NET_ALIAS_H
 
 #include <linux/types.h>
 #include <linux/if.h>
 #include <linux/netdevice.h>
-#include <linux/inet.h>
-#include <linux/in.h>		/* for default IP behavior */
-
 
 /*
  * max. alias slot number allowed 
@@ -32,7 +45,7 @@
   unsigned slot;		/* slot number */
   void *data;			/* private data */
   struct device *main_dev;	/* pointer to main device */
-  struct net_alias_type *nat;	/* alias type bound */
+  struct net_alias_type *nat;	/* alias type object bound */
   struct net_alias *next;	/* next alias (hashed linked list) */
 };
 
@@ -61,15 +74,17 @@
   int n_attach;	 		/* number of aliases attached */
   char name[16];		/* af_name */
   __u32 (*get_addr32)		/* get __u32 addr 'representation'*/
-    (struct sockaddr*);	
-  int (*addr_chk)		/* address checking func: */
-    (struct device *, struct sockaddr *);
+    (struct net_alias_type *this, struct sockaddr*);	
+  int (*dev_addr_chk)		/* address checking func: */
+    (struct net_alias_type *this, struct device *, struct sockaddr *);
+  struct device * (*dev_select)	/* closest alias selector*/
+    (struct net_alias_type *this, struct device *, struct sockaddr *sa);
   int (*alias_init_1)		/* called after alias creation: */
-    (struct net_alias *alias, struct sockaddr *sa);
+    (struct net_alias_type *this,struct net_alias *alias, struct sockaddr *sa);
   int (*alias_done_1)		/* called before alias deletion */
-    (struct net_alias *alias);
+    (struct net_alias_type *this, struct net_alias *alias);
   int (*alias_print_1)	
-    (char *buf, int len, struct net_alias *alias);
+    (struct net_alias_type *this, struct net_alias *alias, char *buf, int len);
   struct net_alias_type *next;	/* link */
 };
 
@@ -81,7 +96,7 @@
 static __inline__ int
 net_alias_is(struct device *dev)
 {
-  return (dev->my_alias != 0);
+  return (dev->my_alias != NULL);
 }
 
 
@@ -92,14 +107,14 @@
 static __inline__ int
 net_alias_has(struct device *dev)
 {
-  return (dev->alias_info != 0);
+  return (dev->alias_info != NULL);
 }
 
 
 extern void net_alias_init(void);
 
 extern struct device * net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data);
-extern int net_alias_rehash(struct net_alias *alias, struct sockaddr *sa);
+extern int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa);
 
 extern int net_alias_getinfo(char *buf, char **, off_t , int , int );
 extern int net_alias_types_getinfo(char *buf, char **, off_t , int , int );
@@ -107,8 +122,11 @@
 extern int register_net_alias_type(struct net_alias_type *nat, int type);
 extern int unregister_net_alias_type(struct net_alias_type *nat);
 
-extern struct device * net_alias_chk(struct device *dev, struct sockaddr *sa, int flags_1, int flags_0);
-extern struct device * net_alias_chk32(struct device *dev, int family, __u32 addr32, int flags_1, int flags_0);
+extern struct device * net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa, int flags_on, int flags_off);
+extern struct device * net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, int flags_on, int flags_off);
+
+extern struct device * net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst);
+extern struct device * net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst);
 
 
 /*
@@ -149,28 +167,6 @@
   }
   pdev->next = nextdev;
   return nextdev;
-}
-
-
-/*
- * addr_chk wrapper: check given generic address with (UP) aliases
- */
-
-static __inline__ struct device *
-net_alias_addr_chk(struct device *dev, struct sockaddr *sa)
-{
-  return net_alias_chk(dev, sa, IFF_UP, 0);
-}
-
-
-/*
- * addr_chk32 wrapper: check given u32 address with (UP) aliases
- */
-
-static __inline__ struct device *
-net_alias_addr_chk32(struct device *dev, int family, __u32 addr32)
-{
-  return net_alias_chk32(dev, family, addr32, IFF_UP, 0);
 }
 
 #endif  /* _NET_ALIAS_H */
diff -u --recursive --new-file v1.3.50/linux/include/net/ip_alias.h linux/include/net/ip_alias.h
--- v1.3.50/linux/include/net/ip_alias.h	Wed Dec 13 09:02:47 1995
+++ linux/include/net/ip_alias.h	Tue Dec 26 06:03:01 1995
@@ -1,11 +1,21 @@
-#ifndef _IP_ALIAS_H
-#define _IP_ALIAS_H
-
-/* 
- * IP alias specific prototypes
+/*
+ *		IP_ALIAS (AF_INET) aliasing definitions.
+ *
+ *
+ * Version:	@(#)ip_alias.h	0.43   12/20/95
+ *
+ * Author:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *	
  */
 
-#include <linux/net_alias.h>
+#ifndef _IP_ALIAS_H
+#define _IP_ALIAS_H
 
 extern int ip_alias_init(void);
 extern int ip_alias_done(void);
diff -u --recursive --new-file v1.3.50/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.50/linux/include/net/sock.h	Sat Nov 25 19:04:57 1995
+++ linux/include/net/sock.h	Wed Dec 27 07:46:29 1995
@@ -174,7 +174,10 @@
  */
  
 	volatile unsigned short	backoff;
-	volatile int		err;
+	volatile int		err, err_soft;	/* Soft holds errors that don't
+						   cause failure but are the cause
+						   of a persistent failure not just
+						   'timed out' */
 	unsigned char		protocol;
 	volatile unsigned char	state;
 	volatile unsigned char	ack_backlog;
@@ -409,7 +412,7 @@
 	int err=xchg(&sk->err,0);
 	return -err;
 }
- 
+
 /* 
  *	Declarations from timer.c 
  */
diff -u --recursive --new-file v1.3.50/linux/init/main.c linux/init/main.c
--- v1.3.50/linux/init/main.c	Tue Dec 26 04:45:41 1995
+++ linux/init/main.c	Wed Dec 27 08:33:51 1995
@@ -488,9 +488,29 @@
 	envp_init[envs+1] = NULL;
 }
 
+
 extern void setup_arch(char **, unsigned long *, unsigned long *);
 
-#ifdef __SMP__
+#ifndef __SMP__
+
+/*
+ *	Uniprocessor idle thread
+ */
+ 
+int cpu_idle(void *unused)
+{
+	for(;;)
+		idle();
+}
+
+#else
+
+/*
+ *	Multiprocessor idle thread is in arch/...
+ */
+ 
+extern int cpu_idle(void * unused);
+
 /*
  *	Activate a secondary processor.
  */
@@ -500,15 +520,10 @@
 	trap_init();
 	init_IRQ();
 	smp_callin();
-	for(;;)
-		idle();
+	cpu_idle(NULL);
 }
 
-int smp_idle(void * unused)
-{
-	for (;;)
-		idle();
-}
+
 
 /*
  *	Called by CPU#0 to activate the rest.
@@ -525,7 +540,7 @@
 
 	for(i=1;i<smp_num_cpus;i++)
 	{
-		kernel_thread(smp_idle, NULL, CLONE_PID);
+		kernel_thread(cpu_idle, NULL, CLONE_PID);
 		/*
 		 *	Assume linear processor numbering
 		 */
@@ -633,8 +648,7 @@
  *
  * Right now task[0] just does a infinite idle loop.
  */
-	for(;;)
-		idle();
+ 	cpu_idle(NULL);
 }
 
 static int printf(const char *fmt, ...)
diff -u --recursive --new-file v1.3.50/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.50/linux/kernel/ksyms.c	Tue Dec 26 04:45:41 1995
+++ linux/kernel/ksyms.c	Wed Dec 27 08:25:37 1995
@@ -38,6 +38,8 @@
 #include <linux/ext2_fs.h>
 #include <linux/random.h>
 
+extern unsigned char aux_device_present, kbd_read_mask;
+
 #ifdef __alpha__
 # include <asm/io.h>
 # include <asm/hwrpb.h>
@@ -62,6 +64,9 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/firewall.h>
+
+#include <linux/trdevice.h>
+
 #ifdef CONFIG_AX25
 #include <net/ax25.h>
 #endif
@@ -133,6 +138,11 @@
 
 extern void (* iABI_hook)(struct pt_regs * regs);
 
+#ifdef CONFIG_BINFMT_ELF
+#include <linux/elfcore.h>
+extern int dump_fpu(elf_fpregset_t *);
+#endif
+
 struct symbol_table symbol_table = {
 #include <linux/symtab_begin.h>
 #ifdef MODVERSIONS
@@ -558,6 +568,22 @@
 	X(proc_net_inode_operations),
 	X(proc_net),
 #endif
+/* all busmice */
+	X(add_mouse_randomness),
+	X(fasync_helper),
+/* psaux mouse */
+	X(aux_device_present),
+	X(kbd_read_mask),
+
+#ifdef CONFIG_TR
+	X(tr_setup),
+	X(tr_type_trans),
+#endif
+
+#ifdef CONFIG_BINFMT_ELF
+	X(dump_fpu),
+#endif
+
 	/********************************************************
 	 * Do not add anything below this line,
 	 * as the stacked modules depend on this!
diff -u --recursive --new-file v1.3.50/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.50/linux/mm/filemap.c	Tue Dec 26 04:45:41 1995
+++ linux/mm/filemap.c	Tue Dec 26 06:51:36 1995
@@ -78,7 +78,7 @@
 	limit = MAP_NR(limit);
 	if (clock >= limit)
 		clock = 0;
-	priority = limit >> (2*priority);
+	priority = limit >> priority;
 	page = mem_map + clock;
 	while (priority-- > 0) {
 		if (page->inode && page->count == 1) {
diff -u --recursive --new-file v1.3.50/linux/mm/swap.c linux/mm/swap.c
--- v1.3.50/linux/mm/swap.c	Tue Dec 26 04:45:41 1995
+++ linux/mm/swap.c	Tue Dec 26 06:40:15 1995
@@ -46,7 +46,7 @@
 	20, 3, 1, 3,		/* Page aging */
 	10, 2, 2, 0,		/* Buffer aging */
 	32, 4,			/* Aging cluster */
-	8192, 4096,		/* Pageout and bufferout weights */
+	8192, 8192,		/* Pageout and bufferout weights */
 	-200,			/* Buffer grace */
 	1, 1,			/* Buffs/pages to free */
 	RCL_ROUND_ROBIN		/* Balancing policy */
diff -u --recursive --new-file v1.3.50/linux/net/Changes linux/net/Changes
--- v1.3.50/linux/net/Changes	Mon Dec 11 15:42:08 1995
+++ linux/net/Changes	Tue Dec 26 06:03:01 1995
@@ -156,7 +156,7 @@
 
 o	Missing patches for device change in TCP	[TESTED]
 o	Device locking					[TESTED]
-o	Infinite slip devices				[IN - BUG]
+o	Infinite slip devices				[TESTED]
 o	New AF_UNIX sockets				[TESTED]
 o	Sendmsg/recvmsg (for some stuff only)		[TESTED]
 o	Device unload loopholes fixed			[TESTED]
@@ -209,7 +209,7 @@
 o	Moved most IP addresses to __u32		[TESTED]
 o	Cleaned up ICMP reporting			[TESTED]
 o	Tidied remove_sock				[TESTED]
-o	Added memory allocation type to ip_build_xmit	[IN]
+o	Added memory allocation type to ip_build_xmit	[TESTED]
 o	Cleaned up af_inet to use inet_error		[TESTED]
 o	Named firewall returns				[TESTED]
 o	Added firewall output checks to ip_build_xmit	[TESTED]
@@ -218,7 +218,7 @@
 o	Reformatted ipv4/protocol.c, dropped frag field	[TESTED]
 o	Fixed MSS for TCP				[TESTED]
 o	Dropped sock_awaitconn				[TESTED]
-o	Added ip_forward to ksyms for IPIP etc		[IN]
+o	Added ip_forward to ksyms for IPIP etc		[TESTED]
 o	Appletalk TIOCINQ/TIOCOUTQ bug fix		[TESTED]
 o	Rewrote the IFF_UP/IFF_DOWN handling code	[TESTED]
 
@@ -239,7 +239,7 @@
 -------->>>>> 1.3.31 <<<<<<-------
 
 o	IFF_ALLMULTI support for 3c501,3c509,8390 and
-	tulip(SMC etherpower) boards			[IN]
+	tulip(SMC etherpower) boards			[TESTED]
 
 -------->>>>> 1.3.33 <<<<<<--------
 
@@ -268,38 +268,52 @@
 o	ICMP lockup fix					[TESTED]
 o	Fundamental operations now only sendmsg/recvmsg	[TESTED]
 o	bind() for SOCK_PACKET				[IN]
-o	set_mac_addr fixed up				[IN]
-o	BSD SIOCSIFADDR, AF_UNSPEC behaviour		[IN]
+o	set_mac_addr fixed up				[TESTED]
+o	BSD SIOCSIFADDR, AF_UNSPEC behaviour		[TESTED]
 o	Updated this list				[OK]
 o	Massive ARP/cache/routing rewrite [ANK]		[IN]
-o	AX.25 connect return fixed in using sock_error	[IN]
+o	AX.25 connect return fixed in using sock_error	[TESTED]
 o	Proper netlink device major(36)			[TESTED]
 o	First parts of the SKIP support			[IN, not useful]
-o	TCP ICMP (SOSS should work again)		[IN]
+o	TCP ICMP (SOSS should work again)		[TESTED]
 o	IPFW support for TOS changing (Al Longyear)	[IN]
 o	DECNET PPP test code [Steve]			[IN]
-o	NFS root	[Miguel/Gero]			[IN]
-o	Path MTU discovery [ANK]			[IN]
+o	NFS root	[Miguel/Gero]			[TESTED]
+o	Path MTU discovery [ANK]			[TESTED]
 
 -------->>>>> 1.3.44 <<<<<<--------
 
-o	NFS root/ FPU clash fixed			[IN]
-o	ARP lock bug fixed				[IN]
-o	SO_BSDCOMPAT option(libbsd/ibcs2 ought to set)	[IN]
-o	Changed to new set_multicast_list()		[IN]
-o	ARP ioctl() call fixes	[Bernd]			[IN]
+o	NFS root/ FPU clash fixed			[TESTED]
+o	ARP lock bug fixed				[TESTED]
+o	SO_BSDCOMPAT option(libbsd/ibcs2 ought to set)	[SEMIDONE]
+o	Changed to new set_multicast_list()		[TESTED]
+o	ARP ioctl() call fixes	[Bernd]			[TESTED]
 o	Fixes to the name set functions (maybe fixes
-	netrom)		[Steve]				[IN]
-o	Packet protocol labelling (not IPX yet)		[IN]
-o	Faster buffer copy/clone [Linus]		[IN]
+	netrom)		[Steve]				[TESTED]
+o	Packet protocol labelling (not IPX yet)		[TESTED]
+o	Faster buffer copy/clone [Linus]		[TESTED]
 
 -------->>>>> 1.3.46 <<<<<<--------
 
-o	AX.25/NetROM fixes/changes [John Naylor]	[IN]
+o	AX.25/NetROM fixes/changes [John Naylor]	[TESTED]
 o	Further attempts to fix the IPX memory bug	[IN]
 o	ARP fixes (Assorted)				[IN]
 o	Driver fixes for multicast lists		[IN]
 
+-------->>>>> 1.3.48 <<<<<<--------
+
+o	IPalias						[TESTED]
+
+-------->>>>> 1.3.50 <<<<<<--------
+
+o	TCP soft error support				[IN]
+o	Further 3c501 tweaking				[TESTED]
+o	Still trying to make IPX work right		[IN]
+o	Trap faulty boxes sending IGMP using 0.0.0.0	[IN]
+o	Only allow SMBFS selection with IP configured	[IN]
+o	Packetwin driver [Craig]			[IN]
+o	Net alias changes [Juan]			[IN]
+
 ---------- Things I thought Linus had for a while and not merged ----------------
 
 
@@ -310,7 +324,6 @@
 o	Chase Donald for new drivers, get people to sort out what net
 	drivers should cease to be 'Alpha'.
 o	IPX PPP support
-o	IPalias
 
 ---------- Things pending for me to merge --------------
 
@@ -323,7 +336,7 @@
 
 o	Forwarding queue control (+ fairness algorithms ??)
 o	IP forward flow control.
-o	IPX memory leak ?????
+o	IPX memory leak ?????				[Done with luck]
 o	Clean up RAW AX.25 sockets.
 o	Finish IPIP bug fixes				[Done hopefully]
 o	Multicast routing				[STARTED BITS]
@@ -332,8 +345,22 @@
 o	SKIP						[Available in user mode]
 o	AX.25/NetROM locking changes
 o	insw_and_csum
-o	IPAlias
 o	AF_UNIX fd passing
+
+-------------------------- Bugs to fix ------------------------------
+
+o	signal interrupting a unix domain connect can occasionally hang the 
+	machine ??
+o	IPX has a memory accounting bug.		[HOPE DONE]
+o	TCP socket cache gets things wrong very very occasionally under high 
+	load.						[TRYING THINGS]
+o	AX.25/NetROM needs more locking.
+o	IP mroute code Oopses still.			[WAITING DIFF]
+o	Lance driver in a few rare systems is causing crashes in copy/checksum.
+o	NFS logs an error (-512) when interrupted.
+o	NFS flow control is needed with the new multirequest NFS support.
+o	Need to be able to turn off the intelligent arp refreshing as its not so
+	hot over AX.25 and upsets some people with very dumb ISDN bridges.
 
 0.2
 ---
diff -u --recursive --new-file v1.3.50/linux/net/TUNABLE linux/net/TUNABLE
--- v1.3.50/linux/net/TUNABLE	Thu Jan  1 02:00:00 1970
+++ linux/net/TUNABLE	Tue Dec 26 06:03:01 1995
@@ -0,0 +1,65 @@
+The following parameters should be tunable but aren't, until we get sysctl
+or similar schemes. For now you'll have to dig around. Various CONFIG_xxx
+items that should be configurable using sysctl omitted.
+
+This is far from complete
+
+Item			Description
+----------------------------------------------------------------------------
+MAX_SOCKETS		Tunable on boot, maximum sockets we will allocate
+NUM_PROTO		Maximum loadable address family, will need recompile
+MAX_LINKS		Maximum number of netlink minor devices. (1-32)
+MAX_QBYTES		Size of a netlink device queue (tunable)
+RIF_TABLE_SIZE		Token ring RIF cache size (tunable)
+AARP_HASH_SIZE		Size of appletalk hash table (tunable)
+AX25_DEF_T1		AX.25 parameters. These are all tunable via
+AX25_DEF_T2		SIOCAX25SETPARMS
+AX25_DEF_T3		T1-T3,N2 have the meanings in the specification
+AX25_DEF_N2
+AX25_DEF_AXDEFMODE	8 = normal 128 is PE1CHL extended
+AX25_DEF_IPDEFMODE	'D' - datagram  'V' - virtual connection
+AX25_DEF_BACKOFF	'E'xponential 'L'inear
+AX25_DEF_NETROM		Allow netrom 1=Y
+AX25_DF_TEXT		Allow PID=Text 1=Y
+AX25_DEF_WINDOW		Window for normal mode
+AX25_DEF_EWINDOW	Window for PE1CHL mode
+AX25_DEF_DIGI		1 for inband 2 for cross band 3 for both
+AX25_DEF_CONMODE	Allow connected modes 1=Yes
+AX25_ROUTE_MAX		AX.25 route cache size - no currently tunable
+Unnamed (16)		Number of protocol hash slots (tunable)
+DEV_NUMBUFFS		Number of priority levels (not easily tunable)
+Unnamed (300)		Maximum packet backlog queue (tunable)
+MAX_IOVEC		Maximum number of iovecs in a message (tunable)
+MIN_WINDOW		Offered minimum window (tunable)
+MAX_WINDOW		Offered maximum window (tunable)
+MAX_HEADER		Largest physical header (tunable)
+MAX_ADDR_LEN		Largest physical address (tunable)
+SOCK_ARRAY_SIZE		IP socket array hash size (tunable)
+ARP_RES_TIME		Time we try and resolve (tunable)
+ARP_DEAD_RES_TIME	Time the entry stays dead (tunable)
+ARP_MAX_TRIES		Maximum tries (tunable)
+ARP_TIMEOUT		Timeout on an ARP (tunable)
+ARP_CHECK_INTERVAL	Check interval to refresh an arp (tunable)
+ARP_CONFIRM_INTERVAL	Confirm poll time (tunable)
+ARP_TABLE_SIZE		Hash table size for ARP (tunable)
+IP_MAX_MEMBERSHIPS	Largest number of groups per socket (BSD style)
+16			Hard coded constant for amount of room allowed for
+			cache align and faster forwarding (tunable)
+IPFRAG_HIGH_THRESH	Limit on fragments, we free fragments until we reach
+IPFRAG_LOW_THRESH	which provides some breathing space. (tunable)
+IP_FRAG_TIME		Time we hold a fragment for. (tunable)
+PORT_MASQ_BEGIN		First port reserved for masquerade (tunable)
+PORT_MASQ_END		Last port used for masquerade	(tunable)
+MASQUERADE_EXPIRE_TCP_FIN	Time we keep a masquerade for after a FIN
+MASUQERADE_EXPIRE_UDP	Time we keep a UDP masquerade for (tunable)
+MAXVIFS			Maximum mrouted vifs (1-32)
+MFC_LINES		Lines in the multicast router cache (tunable)
+SK_RMEM_MAX		Max memory a socket owns for receive (tunable)
+SK_WMEM_MAX		Max memory a socket owns for send (tunable)
+
+NetROM parameters are tunable via an ioctl passing a struct
+
+4000			Size a Unix domain socket malloc falls back to 
+			(tunable) should be 8K - a bit for 8K machines like
+			the ALPHA
+
diff -u --recursive --new-file v1.3.50/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.50/linux/net/core/dev.c	Wed Dec 13 09:02:47 1995
+++ linux/net/core/dev.c	Tue Dec 26 06:03:01 1995
@@ -1121,7 +1121,7 @@
 
 #ifdef CONFIG_NET_ALIAS
 			  	if (net_alias_is(dev))
-			    	net_alias_rehash(dev->my_alias,&ifr.ifr_addr);
+			    	net_alias_dev_rehash(dev ,&ifr.ifr_addr);
 #endif
 				dev->pa_addr = (*(struct sockaddr_in *)
 					 &ifr.ifr_addr).sin_addr.s_addr;
@@ -1387,6 +1387,9 @@
 #if defined(CONFIG_PI)
 	pi_init();
 #endif	
+#if defined(CONFIG_PT)
+	pt_init();
+#endif
 #if defined(CONFIG_DEC_ELCP)
 	dec21040_init();
 #endif	
diff -u --recursive --new-file v1.3.50/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v1.3.50/linux/net/core/net_alias.c	Wed Dec 13 09:02:47 1995
+++ linux/net/core/net_alias.c	Tue Dec 26 06:03:01 1995
@@ -1,7 +1,8 @@
 /*
- *		NET_ALIAS device aliasing module.
+ *		NET_ALIAS network device aliasing module.
  *
- * Version:	@(#)net_alias.c	0.42   12/11/95
+ *
+ * Version:	@(#)net_alias.c	0.43   12/20/95
  *
  * Authors:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *		Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
@@ -13,15 +14,13 @@
  *	-	fast hashed alias address lookup
  *	-	net_alias_type objs registration/unreg., module-ables.
  *	-	/proc/net/aliases & /proc/net/alias_types entries
+ * Fixes:
+ *	JJC	:	several net_alias_type func. renamed.
+ *	JJC	:	net_alias_type object methods now pass *this.
+ *	JJC	:	xxx_rcv device selection based on <src,dst> addrs
  *
  * FIXME:
  *	- User calls sleep/wake_up locking.
- *	- Define a way to select the "best" alias device for an incoming
- *	  packet to allow xxx_rcv() device switching based ALSO on pkt's
- *	  src address (this would require a routing query).
- *	  Related stuff:
- *		IP: Test routing between aliases (possible ICMP redirects).
- *		IP: ARP proxy entries attached to aliases are not visible.
  *
  *
  *	This program is free software; you can redistribute it and/or
@@ -37,6 +36,7 @@
 #include <linux/notifier.h>
 #include <linux/if.h>
 #include <linux/inet.h>
+#include <linux/in.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
@@ -68,7 +68,7 @@
 static void net_alias_free(struct device *dev);
 					   
 /*
- * net_alias_type base array, will hold net_alias_type objects.
+ * net_alias_type base array, will hold net_alias_type obj hashed list heads.
  */
 
 struct net_alias_type *nat_base[16];
@@ -99,7 +99,7 @@
 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
 {
   if (nat->get_addr32)
-    return nat->get_addr32(sa);
+    return nat->get_addr32(nat, sa);
   else
     return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
 }
@@ -122,7 +122,7 @@
 /*
  * get hash key for supplied net alias type and address
  * nat must be !NULL
- * the purpose here is to map an net_alias_type and a generic
+ * the purpose here is to map a net_alias_type and a generic
  * address to a hash code.
  */
 
@@ -166,33 +166,33 @@
 static __inline__ int
 nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa)
 {
-  if (nat->alias_init_1) nat->alias_init_1(alias, sa);
+  if (nat->alias_init_1) nat->alias_init_1(nat, alias, sa);
   return nat_attach_chg(nat, +1);
 }
 
 
 /*
- * unbind alias from type object and call 'done' hook
+ * unbind alias from type object and call alias destructor
  */
 
 static __inline__ int
 nat_unbind(struct net_alias_type *nat, struct net_alias *alias)
 {
-  if (nat->alias_done_1) nat->alias_done_1(alias);
+  if (nat->alias_done_1) nat->alias_done_1(nat, alias);
   return nat_attach_chg(nat, -1);
 }
 
 
 /*
- * compare device address with given. if NULL nat->addr_chk,
+ * compare device address with given. if NULL nat->dev_addr_chk,
  * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
  */
 
-static __inline__ int nat_addr_chk(struct net_alias_type *nat,
+static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat,
 				   struct device *dev, struct sockaddr *sa)
 {
-  if (nat->addr_chk)
-    return nat->addr_chk(dev, sa);
+  if (nat->dev_addr_chk)
+    return nat->dev_addr_chk(nat, dev, sa);
   else
     return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
 }
@@ -573,6 +573,81 @@
   return NULL;
 }
 
+/*
+ * free all main device aliasing stuff
+ * will be called on dev_close(main_dev)
+ */
+
+static void
+net_alias_free(struct device *main_dev)
+{
+  struct net_alias_info *alias_info;
+  struct net_alias *alias;
+  struct net_alias_type *nat;
+  struct device *dev;
+  unsigned long flags;
+
+  /*
+   * do I really have aliases?
+   */
+  
+  if (!(alias_info = main_dev->alias_info))    return;
+
+  /*
+   * fast device link "short-circuit": set main_dev->next to
+   * device after last alias
+   */
+  
+  save_flags(flags);
+  cli();
+  
+  dev =  main_dev->next;
+  main_dev->next = alias_info->taildev->next;
+  main_dev->alias_info = NULL;
+  alias_info->taildev->next = NULL;
+  
+  restore_flags(flags);
+
+  /*
+   * loop over alias devices, free and dev_close()
+   */
+  
+  while (dev)
+  {
+    if (net_alias_is(dev))
+    {
+      alias = dev->my_alias;
+      if (alias->main_dev == main_dev)
+      {
+	/*
+	 * unbind alias from alias_type object
+	 */
+	
+	nat = alias->nat;
+	if (nat)
+	{
+	  nat_unbind(nat, alias);
+	} /*  else error/printk ??? */
+	
+	dev_close(dev);
+	dev = dev->next;
+	
+	kfree_s(alias, sizeof(struct net_alias));
+	continue;
+      }
+      else
+	printk("net_alias_free(%s): '%s' is not my alias\n",
+	       main_dev->name, alias->name);
+    }
+    else
+      printk("net_alias_free(%s): found a non-alias after device!\n",
+	     main_dev->name);
+    dev = dev->next;
+  }
+  
+  kfree_s(alias_info, sizeof(alias_info));
+  return;
+}
 
 /*
  * dev_get() with added alias naming magic.
@@ -645,23 +720,26 @@
 
 
 /*
- * rehash alias with address supplied. 
+ * rehash alias device with address supplied. 
  */
 
 int
-net_alias_rehash(struct net_alias *alias, struct sockaddr *sa)
+net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
 {
   struct net_alias_info *alias_info;
-  struct net_alias **aliasp;
-  struct device *dev;
+  struct net_alias *alias, **aliasp;
+  struct device *main_dev;
   unsigned long flags;
   struct net_alias_type *o_nat, *n_nat;
   unsigned n_hash;
-  
+
   /*
    * defensive ...
    */
   
+  if (dev == NULL) return -1;
+  if ( (alias = dev->my_alias) == NULL ) return -1;
+  
   if (!sa)
   {
     printk("ERROR: net_alias_rehash(): NULL sockaddr passed\n");
@@ -671,8 +749,8 @@
   /*
    * defensive. should not happen.
    */
-  
-  if (!(dev = alias->main_dev))
+
+  if ( (main_dev = alias->main_dev) == NULL )
   {
     printk("ERROR: net_alias_rehash for %s: NULL maindev\n", alias->name);
     return -1;
@@ -682,7 +760,7 @@
    * defensive. should not happen.
    */
 
-  if (!(alias_info=dev->alias_info))
+  if (!(alias_info=main_dev->alias_info))
   {
     printk("ERROR: net_alias_rehash for %s: NULL alias_info\n", alias->name);
     return -1;
@@ -747,7 +825,7 @@
   cli();
   
   /*
-   * if type (family) changed unlink from old type object (o_nat)
+   * if type (family) changed, unlink from old type object (o_nat)
    * will call o_nat->alias_done_1()
    */
   
@@ -780,81 +858,6 @@
 }
 
 
-/*
- * free all main device aliasing stuff
- * will be called on dev_close(main_dev)
- */
-
-static void
-net_alias_free(struct device *main_dev)
-{
-  struct net_alias_info *alias_info;
-  struct net_alias *alias;
-  struct net_alias_type *nat;
-  struct device *dev;
-  unsigned long flags;
-
-  /*
-   * do I really have aliases?
-   */
-  
-  if (!(alias_info = main_dev->alias_info))    return;
-
-  /*
-   * fast device link "short-circuit": set main_dev->next to
-   * device after last alias
-   */
-  
-  save_flags(flags);
-  cli();
-  
-  dev =  main_dev->next;
-  main_dev->next = alias_info->taildev->next;
-  main_dev->alias_info = NULL;
-  alias_info->taildev->next = NULL;
-  
-  restore_flags(flags);
-
-  /*
-   * loop over alias devices, free and dev_close()
-   */
-  
-  while (dev)
-  {
-    if (net_alias_is(dev))
-    {
-      alias = dev->my_alias;
-      if (alias->main_dev == main_dev)
-      {
-	/*
-	 * unbind alias from alias_type object
-	 */
-	
-	nat = alias->nat;
-	if (nat)
-	{
-	  nat_unbind(nat, alias);
-	} /*  else error/printk ??? */
-	
-	dev_close(dev);
-	dev = dev->next;
-	
-	kfree_s(alias, sizeof(struct net_alias));
-	continue;
-      }
-      else
-	printk("net_alias_free(%s): '%s' is not my alias\n",
-	       main_dev->name, alias->name);
-    }
-    else
-      printk("net_alias_free(%s): found a non-alias after device!\n",
-	     main_dev->name);
-    dev = dev->next;
-  }
-  
-  kfree_s(alias_info, sizeof(alias_info));
-  return;
-}
 
 
 /*
@@ -898,7 +901,7 @@
  *
  */
 
-#define NAT_REC_SIZE 64
+#define NET_ALIASES_RECSIZ 64
 int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
 {
   off_t pos=0, begin=0;
@@ -908,7 +911,7 @@
   struct net_alias *alias;
   struct device *dev;
 
-  len=sprintf(buffer,"%-*s\n",NAT_REC_SIZE-1,"device           family address");
+  len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device           family address");
   for (dev = dev_base; dev ; dev = dev->next)
     if (net_alias_is(dev))
     {
@@ -921,7 +924,7 @@
        */
       
       if (nat->alias_print_1)
-	dlen += nat->alias_print_1(buffer+len+dlen, NAT_REC_SIZE - dlen, alias);
+	dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
       else
 	dlen += sprintf(buffer+len+dlen, "-");
 
@@ -929,12 +932,12 @@
        * fill with spaces if needed 
        */
       
-      if (dlen < NAT_REC_SIZE) memset(buffer+len+dlen, ' ', NAT_REC_SIZE - dlen);
+      if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
       /*
-       * truncate to NAT_REC_SIZE
+       * truncate to NET_ALIASES_RECSIZ
        */
       
-      len += NAT_REC_SIZE;
+      len += NET_ALIASES_RECSIZ;
       buffer[len-1] = '\n';
       
       pos=begin+len;
@@ -984,58 +987,39 @@
 
 
 /*
- * returns alias device with specified address AND flags_1 on AND flags_0 off.
- *   intended for main devices.
- *   typically called on xxx_rcv() to check if packet's dest address is one
- *   of main_dev's alias address.
+ * device aliases address comparison workhorse
+ * no checks for nat and alias_info, must be !NULL
  */
 
-struct device *
-net_alias_chk(struct device *dev, struct sockaddr *sa,int flags_1, int flags_0)
+static __inline__ struct device *
+nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
 {
-  struct net_alias_info *alias_info = dev->alias_info;
-  struct net_alias_type *nat;
   struct net_alias *alias;
-  
-  if (!alias_info) return NULL;	/* has aliases? */
-  
-  /*
-   * get alias_type object for sa->sa_family.
-   */
-  
-  nat = nat_getbytype(sa->sa_family);
-  if (!nat)
-    return 0;
-  
   for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
       alias; alias = alias->next)
   {
     if (alias->dev.family != sa->sa_family) continue;
 
     /*
-     * nat_addr_chk will call type specific address cmp function.
+     * nat_dev_addr_chk_1 will call type specific address cmp function.
      */
     
-    if (alias->dev.flags & flags_1 && !(alias->dev.flags & flags_0) &&
-	nat_addr_chk(nat,&alias->dev,sa))
+    if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
+	nat_dev_addr_chk_1(nat,&alias->dev,sa))
       return &alias->dev;
   }
   return NULL;
 }
 
 /*
- * addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
+ * nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
+ * note that nat pointer is ignored because of static comparison.
  */
 
-struct device *
-net_alias_chk32(struct device *dev, int family, __u32 addr32,
-		int flags_1, int flags_0)
+static __inline__ struct device *
+nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off)
 {
-  struct net_alias_info *alias_info = dev->alias_info;
   struct net_alias *alias;
-  
-  if (!alias_info) return NULL;	/* has aliases? */
-  
   for (alias=alias_info->hash_tab[HASH(addr32,family)];
        alias; alias=alias->next)
   {
@@ -1045,13 +1029,212 @@
      * "hard" (static) comparison between addr32 and pa_addr.
      */
     
-    if (alias->dev.flags & flags_1 && !(alias->dev.flags & flags_0) &&
+    if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
 	addr32 == alias->dev.pa_addr)
       return &alias->dev;
   }
   return NULL;
 }
 
+/*
+ * returns alias device with specified address AND flags_on AND flags_off,
+ * else NULL.
+ * intended for main devices.
+ */
+
+struct device *
+net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int flags_off)
+{
+  struct net_alias_info *alias_info = main_dev->alias_info;
+  struct net_alias_type *nat;
+  
+  /*
+   * only if main_dev has aliases
+   */
+
+  if (!alias_info) return NULL;
+  
+  /*
+   * get alias_type object for sa->sa_family.
+   */
+  
+  nat = nat_getbytype(sa->sa_family);
+  if (!nat)
+    return NULL;
+
+  return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off);
+}
+
+/*
+ * net_alias_dev_chk enough for protocols whose addr is (fully) stored
+ * at pa_addr.
+ */
+
+struct device *
+net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32,
+		int flags_on, int flags_off)
+{
+  struct net_alias_info *alias_info = main_dev->alias_info;
+  
+  /*
+   * only if main_dev has aliases
+   */
+
+  if (!alias_info) return NULL;
+  
+  return nat_addr_chk32(NULL, alias_info, family, addr32, flags_on, flags_off);
+}
+
+
+/*
+ * select closest (main or alias) device to <src,dst> addresses given. if no
+ * further info is available, return main_dev (for easier calling arrangment).
+ *
+ * Should be called early at xxx_rcv() time for device selection
+ */
+
+struct device *
+net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
+{
+  int family;
+  struct net_alias_type *nat;
+  struct net_alias_info *alias_info;
+  struct device *dev;
+  
+  if (main_dev == NULL) return NULL;
+
+  /*
+   * if not aliased, dont bother any more
+   */
+
+  if ((alias_info = main_dev->alias_info) == NULL)
+    return main_dev;
+
+  /*
+   * find out family
+   */
+
+  family = (sa_src)? sa_src->sa_family : ((sa_dst)? sa_dst->sa_family : AF_UNSPEC);
+  if (family == AF_UNSPEC) return main_dev;
+
+  /*
+   * get net_alias_type object for this family
+   */
+
+  if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
+  
+  /*
+   * first step: find out if dst addr is main_dev's or one of its aliases'
+   */
+
+  if (sa_dst)
+  {
+    if (nat_dev_addr_chk_1(nat, main_dev,sa_dst))
+      return main_dev;
+
+    dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
+
+    if (dev != NULL) return dev;
+  }
+
+  /*
+   * second step: find the rcv addr 'closest' alias through nat method call
+   */
+
+  if ( sa_src == NULL || nat->dev_select == NULL) return main_dev;
+  dev = nat->dev_select(nat, main_dev, sa_src);
+
+  if (dev == NULL || dev->family != family) return main_dev;
+  
+  /*
+   * dev ok only if it is alias of main_dev
+   */
+  
+  dev = net_alias_is(dev)?
+    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+
+  /*
+   * do not return NULL.
+   */
+  
+  return (dev)? dev : main_dev;
+
+}
+
+/*
+ * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ */
+
+struct device *
+net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst)
+{
+  struct net_alias_type *nat;
+  struct net_alias_info *alias_info;
+  struct sockaddr_in sin_src;
+  struct device *dev;
+  
+  if (main_dev == NULL) return NULL;
+
+  /*
+   * if not aliased, dont bother any more
+   */
+
+  if ((alias_info = main_dev->alias_info) == NULL)
+    return main_dev;
+
+  /*
+   * early return if dst is main_dev's address
+   */
+  
+  if (dst == main_dev->pa_addr)
+    return main_dev;
+  
+  if (family == AF_UNSPEC) return main_dev;
+
+  /*
+   * get net_alias_type object for this family
+   */
+
+  if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
+  
+  /*
+   * first step: find out if dst address one of main_dev aliases'
+   */
+  
+  if (dst)
+  {
+    dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
+    if (dev) return dev;
+  }
+  
+  /*
+   * second step: find the rcv addr 'closest' alias through nat method call
+   */
+
+  if ( src == 0 || nat->dev_select == NULL) return main_dev;
+
+  sin_src.sin_family = family;
+  sin_src.sin_addr.s_addr = src;
+    
+  dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
+
+  if (dev == NULL) return main_dev;
+  
+  /*
+   * dev ok only if it is alias of main_dev
+   */
+  
+  dev = net_alias_is(dev)?
+    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+
+  /*
+   * do not return NULL.
+   */
+  
+  return (dev)? dev : main_dev;
+  
+}
+
 
 /*
  * device event hook
@@ -1162,3 +1345,4 @@
   printk("unregister_net_alias_type(type=%d): not found!\n", nat->type);
   return -EINVAL;
 }
+
diff -u --recursive --new-file v1.3.50/linux/net/core/sock.c linux/net/core/sock.c
--- v1.3.50/linux/net/core/sock.c	Sat Nov 25 19:04:57 1995
+++ linux/net/core/sock.c	Tue Dec 26 06:03:01 1995
@@ -66,6 +66,7 @@
  *					(compatibility fix)
  *		Alan Cox	:	Added optimistic memory grabbing for AF_UNIX throughput.
  *		Alan Cox	:	Allocator for a socket is settable.
+ *		Alan Cox	:	SO_ERROR includes soft errors.
  *
  * To Fix:
  *
@@ -259,8 +260,9 @@
 			break;
 
 		case SO_ERROR:
-			val = sk->err;
-			sk->err = 0;
+			val = sock_error(sk);
+			if(val==0)
+				val=xchg(&sk->err_soft,0);
 			break;
 
 		case SO_OOBINLINE:
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.50/linux/net/ipv4/af_inet.c	Wed Dec 13 09:02:47 1995
+++ linux/net/ipv4/af_inet.c	Tue Dec 26 06:03:01 1995
@@ -1471,7 +1471,7 @@
 	int i;
 
 
-	printk("Swansea University Computer Society TCP/IP for NET3.032\n");
+	printk("Swansea University Computer Society TCP/IP for NET3.033\n");
 
 	/*
 	 *	Tell SOCKET that we are alive... 
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.50/linux/net/ipv4/arp.c	Wed Dec 13 09:02:47 1995
+++ linux/net/ipv4/arp.c	Tue Dec 26 06:03:01 1995
@@ -895,15 +895,22 @@
  */
 
 /*
- *	try to switch to alias device whose address is tip, if any
+ *	try to switch to alias device whose addr is tip or closest to sip.
  */
 
 #ifdef CONFIG_NET_ALIAS
-	if (net_alias_has(dev))
+	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
 	{
-		struct device *adev;
-		adev = net_alias_chk32(dev,AF_INET,tip,IFF_UP,IFF_NOARP);
-		if (adev != NULL) dev = adev;
+		/*
+		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
+		 */
+		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+
+		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
 	}
 #endif
 
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v1.3.50/linux/net/ipv4/igmp.c	Mon Dec 11 15:42:10 1995
+++ linux/net/ipv4/igmp.c	Tue Dec 26 06:03:02 1995
@@ -2,9 +2,11 @@
  *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
  *
  *	This code implements the IGMP protocol as defined in RFC1122. There has
- *	been a further revision of this protocol since, but since it is not
- *	cleanly specified in any IETF standards we implement the old one properly
- *	rather than play a game of guess the BSD unofficial extensions.
+ *	been a further revision of this protocol since which is now supported.
+ *
+ *	If you have trouble with this module be careful what gcc you have used,
+ *	the older version didnt come out right using gcc 2.5.8, the newer one
+ *	seems to fall out with gcc 2.6.2.
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -42,6 +44,8 @@
  *					and do what the IGMP version 2 specified.
  *		Chih-Jen Chang	:	Added a timer to revert to IGMP V2 router
  *		Tsu-Sheng Tsao		if the specified time expired.
+ *		Alan Cox	:	Stop IGMP from 0.0.0.0 being accepted.
+ *		Alan Cox	:	Use GFP_ATOMIC in the right places.
  */
 
 
@@ -107,7 +111,10 @@
 	/*
 	 *  Not found. Create a new entry. The default is IGMP V2 router
 	 */
-	i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+	 
+	i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_ATOMIC);
+	if(i==NULL)
+		return NULL;
 	i->dev = dev;
 	i->type = IGMP_NEW_ROUTER;
 	i->time = IGMP_AGE_THRESHOLD;
@@ -153,7 +160,9 @@
 	/*
 	 *  Not found. Create a new entry.
 	 */
-	i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+	i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_ATOMIC);
+	if(i==NULL)
+		return NULL;
 	i->dev = dev;
 	i->type = type;
 	i->time = time;
@@ -242,6 +251,8 @@
 	struct ip_router_info *r;
 	igmp_stop_timer(im);
 	r=igmp_get_mrouter_info(im->interface);
+	if(r==NULL)
+		return;
 	if(r->type==IGMP_NEW_ROUTER)
 		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
 	else
@@ -277,7 +288,8 @@
 	{
 		mrouter_type=IGMP_NEW_ROUTER;
 
-		igmp_set_mrouter_info(dev,mrouter_type,0);
+		if(igmp_set_mrouter_info(dev,mrouter_type,0)==NULL)
+			return;
 		/*
 		 * - Start the timers in all of our membership records
 		 *   that the query applies to for the interface on
@@ -310,7 +322,8 @@
 		mrouter_type=IGMP_OLD_ROUTER;
 		max_resp_time=IGMP_MAX_HOST_REPORT_DELAY*IGMP_TIMER_SCALE;
 
-		igmp_set_mrouter_info(dev,mrouter_type,IGMP_AGE_THRESHOLD);
+		if(igmp_set_mrouter_info(dev,mrouter_type,IGMP_AGE_THRESHOLD)==NULL)
+			return;
 
 		/*
 		 * Start the timers in all of our membership records for
@@ -383,6 +396,8 @@
 	igmp_init_timer(im);
 	ip_mc_filter_add(im->interface, im->multiaddr);
 	r=igmp_get_mrouter_info(im->interface);
+	if(r==NULL)
+		return;
 	if(r->type==IGMP_NEW_ROUTER)
 		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
 	else
@@ -412,6 +427,18 @@
 
 	if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl>1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr)))
 	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	
+	/*
+	 *	I have a report that someone does this!
+	 */
+	 
+	if(saddr==0)
+	{
+		printk("Broken multicast host using 0.0.0.0 heard on %s\n",
+			dev->name);
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/ip_alias.c linux/net/ipv4/ip_alias.c
--- v1.3.50/linux/net/ipv4/ip_alias.c	Wed Dec 13 09:02:47 1995
+++ linux/net/ipv4/ip_alias.c	Tue Dec 26 06:03:02 1995
@@ -1,3 +1,21 @@
+/*
+ *		IP_ALIAS (AF_INET) aliasing module.
+ *
+ *
+ * Version:	@(#)ip_alias.c	0.43   12/20/95
+ *
+ * Author:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ * Fixes:
+ *	JJC	:	ip_alias_dev_select method.
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *	
+ */
+
 #include <linux/module.h>
 
 #include <linux/types.h>
@@ -5,6 +23,10 @@
 #include <linux/netdevice.h>
 #include <linux/if.h>
 #include <linux/inet.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/route.h>
+#include <net/route.h>
 
 #ifdef ALIAS_USER_LAND_DEBUG
 #include "net_alias.h"
@@ -19,7 +41,7 @@
  * AF_INET alias init
  */
 static int 
-ip_alias_init_1(struct net_alias *alias, struct sockaddr *sa)
+ip_alias_init_1(struct net_alias_type *this, struct net_alias *alias, struct sockaddr *sa)
 {
 #ifdef ALIAS_USER_LAND_DEBUG
   printk("alias_init(%s) called.\n", alias->name);
@@ -32,7 +54,7 @@
  * AF_INET alias done
  */
 static int
-ip_alias_done_1(struct net_alias *alias)
+ip_alias_done_1(struct net_alias_type *this, struct net_alias *alias)
 {
 #ifdef ALIAS_USER_LAND_DEBUG
   printk("alias_done(%s) called.\n", alias->name);
@@ -42,11 +64,11 @@
 }
 
 /*
- * print address info
+ * print alias address info
  */
 
 int
-ip_alias_print_1(char *buf, int len, struct net_alias *alias)
+ip_alias_print_1(struct net_alias_type *this, struct net_alias *alias, char *buf, int len)
 {
   char *p;
 
@@ -55,6 +77,37 @@
 		 (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
 }
 
+struct device *
+ip_alias_dev_select(struct net_alias_type *this, struct device *main_dev, struct sockaddr *sa)
+{
+  __u32 addr;
+  struct rtable *rt;
+  
+  /*
+   * defensive...
+   */
+  
+  if (main_dev == NULL) return NULL;
+
+  /*
+   * get u32 address. 
+   */
+
+  addr =  (sa)? (*(struct sockaddr_in *)sa).sin_addr.s_addr : 0;
+
+  if (addr == 0) return NULL;
+
+  /*
+   * find 'closest' device to address given. any other suggestions? ...
+   * net_alias module will check if returned device is main_dev's alias
+   */
+
+  rt = ip_rt_route(addr, 0);
+
+  return (rt)? rt->rt_dev : NULL;
+
+}
+
 /*
  * net_alias AF_INET type defn.
  */
@@ -65,7 +118,8 @@
   0,				/* n_attach */
   "ip",				/* name */
   NULL,				/* get_addr32() */
-  NULL,				/* addr_chk() */
+  NULL,				/* dev_addr_chk() */
+  ip_alias_dev_select,		/* dev_select() */
   ip_alias_init_1,		/* alias_init_1() */
   ip_alias_done_1,		/* alias_done_1() */
   ip_alias_print_1,		/* alias_print_1() */
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.50/linux/net/ipv4/ip_input.c	Wed Dec 13 09:02:47 1995
+++ linux/net/ipv4/ip_input.c	Tue Dec 26 06:03:02 1995
@@ -273,6 +273,17 @@
 	}
 	
 	/*
+	 *	Try to select closest <src,dst> alias device, if any.
+	 *	net_alias_dev_rcv_sel32 returns main device if it 
+	 *	fails to found other.
+	 */
+
+#ifdef CONFIG_NET_ALIAS
+	if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) 
+		skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
+#endif
+
+	/*
 	 *	See if the firewall wants to dispose of the packet. 
 	 */
 
@@ -316,18 +327,7 @@
 	 *	function entry.
 	 */
 
-	/*
-	 *	also check device aliases address : will avoid
-	 *	a full lookup over device chain
-	 */
-
-#ifdef CONFIG_NET_ALIAS
-	if ( iph->daddr == skb->dev->pa_addr ||
-	    ( net_alias_has(skb->dev) && net_alias_addr_chk32(skb->dev,AF_INET, iph->daddr )) ||
-	    (brd = ip_chk_addr(iph->daddr)) != 0)
-#else
 	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
-#endif
 	{
 	        if (opt && opt->srr) 
 	        {
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v1.3.50/linux/net/ipv4/raw.c	Mon Dec 11 15:42:10 1995
+++ linux/net/ipv4/raw.c	Tue Dec 26 06:03:02 1995
@@ -220,11 +220,11 @@
 		if (sk->state != TCP_ESTABLISHED) 
 			return(-EINVAL);
 		sin.sin_family = AF_INET;
-		sin.sin_port = sk->protocol;
+		sin.sin_port = sk->num;
 		sin.sin_addr.s_addr = sk->daddr;
 	}
 	if (sin.sin_port == 0) 
-		sin.sin_port = sk->protocol;
+		sin.sin_port = sk->num;
   
 	if (sin.sin_addr.s_addr == INADDR_ANY)
 		sin.sin_addr.s_addr = ip_my_addr();
diff -u --recursive --new-file v1.3.50/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.50/linux/net/ipv4/tcp.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/tcp.c	Tue Dec 26 06:03:02 1995
@@ -179,6 +179,7 @@
  *		Alan Cox	:	Block double connect().
  *		Alan Cox	:	Small hooks for enSKIP.
  *		Alexey Kuznetsov:	Path MTU discovery.
+ *		Alan Cox	:	Support soft errors.
  *
  *
  * To Fix:
@@ -192,7 +193,6 @@
  *		RFC1323 - PAWS and window scaling. PAWS is required for IPv6 so we
  *		could do with it working on IPv4
  *		User settable/learned rtt/max window/mtu
- *		Cope with MTU/device switches when retransmitting in tcp.
  *		Fix the window handling to use PR's new code.
  *
  *		Change the fundamental structure to a single send queue maintained
@@ -349,12 +349,12 @@
  *   MUST implement receiver-side SWS. (does)
  *   
  * When to Send Data (4.2.3.4)
- *   MUST implement sender-side SWS. (does - imperfectly)
+ *   MUST implement sender-side SWS. (does)
  *   SHOULD implement Nagle algorithm. (does)
  * 
  * TCP Connection Failures (4.2.3.5)
  *  MUST handle excessive retransmissions "properly" (see the RFC). (does)
- *   SHOULD inform application layer of soft errors. (doesn't)
+ *   SHOULD inform application layer of soft errors. (does)
  *   
  * TCP Keep-Alives (4.2.3.6)
  *   MAY provide keep-alives. (does)
@@ -372,13 +372,11 @@
  *   MUST use same local address for all segments of a connection. (does)
  * 
  * IP Options (4.2.3.8)
- *   (I don't think the IP layer sees the IP options, yet.)
- *   MUST ignore unsupported IP options. (does, I guess 8*b)
- *   MAY support Time Stamp and Record Route. (doesn't)
- * **MUST allow application to specify a source route. (doesn't?)
- * **MUST allow receieved Source Route option to set route for all future
- *     segments on this connection. (doesn't, not that I think it's a
- *     huge problem)
+ *   MUST ignore unsupported IP options. (does)
+ *   MAY support Time Stamp and Record Route. (does)
+ *   MUST allow application to specify a source route. (does)
+ *   MUST allow receieved Source Route option to set route for all future
+ *     segments on this connection. (does not (security issues))
  * 
  * ICMP messages (4.2.3.9)
  *   MUST act on ICMP errors. (does)
@@ -387,7 +385,7 @@
  *     Unreachables (0, 1, 5), Time Exceededs and Parameter
  *     Problems. (doesn't)
  *   SHOULD report soft Destination Unreachables etc. to the
- *     application. (doesn't)
+ *     application. (does)
  *   SHOULD abort connection upon receipt of hard Destination Unreachable
  *     messages (2, 3, 4). (does)
  * 
@@ -696,7 +694,7 @@
 		{
 			if(skb->sk)
 			{
-				skb->sk->err=ENETUNREACH;
+				skb->sk->err_soft=ENETUNREACH;
 				skb->sk->error_report(skb->sk);
 			}
 		}
@@ -893,7 +891,10 @@
 	 
 	if(sk->retransmits > TCP_SYN_RETRIES && sk->state==TCP_SYN_SENT)
 	{
-		sk->err=ETIMEDOUT;
+		if(sk->err_soft)
+			sk->err=sk->err_soft;
+		else
+			sk->err=ETIMEDOUT;
 		sk->error_report(sk);
 		del_timer(&sk->retransmit_timer);
 		tcp_statistics.TcpAttemptFails++;	/* Is this right ??? - FIXME - */
@@ -907,7 +908,10 @@
 	 */
 	if (sk->retransmits > TCP_RETR2) 
 	{
-		sk->err = ETIMEDOUT;
+		if(sk->err_soft)
+			sk->err = sk->err_soft;
+		else
+			sk->err = ETIMEDOUT;
 		sk->error_report(sk);
 		del_timer(&sk->retransmit_timer);
 		/*
@@ -1113,17 +1117,21 @@
 	 * until we time out, or the user gives up.
 	 */
 
-	if (code < 13 && (icmp_err_convert[code].fatal || sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV))
-	{
-		sk->err = icmp_err_convert[code].errno;
-		if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
+	if (code < 13)
+	{	
+		if(icmp_err_convert[code].fatal || sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
 		{
-			tcp_statistics.TcpAttemptFails++;
-			tcp_set_state(sk,TCP_CLOSE);
-			sk->error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */
+			sk->err = icmp_err_convert[code].errno;
+			if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
+			{
+				tcp_statistics.TcpAttemptFails++;
+				tcp_set_state(sk,TCP_CLOSE);
+				sk->error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */
+			}
 		}
+		else	/* Only an error on timeout */
+			sk->err_soft = icmp_err_convert[code].errno;
 	}
-	return;
 }
 
 
@@ -3590,6 +3598,13 @@
 	 */
 	 
 	sk->rcv_ack_seq = ack;
+	
+	/*
+	 *	We passed data and got it acked, remove any soft error
+	 *	log. Something worked...
+	 */
+	 
+	sk->err_soft = 0;
 
 	/*
 	 *	If this ack opens up a zero window, clear backoff.  It was
@@ -4809,7 +4824,7 @@
 	 *	Find the socket, using the last hit cache if applicable.
 	 */
 
-	if(saddr==th_cache_saddr && daddr==th_cache_daddr && th->dest==th_cache_dport && th->source==th_cache_sport)
+	if(!redo && saddr==th_cache_saddr && daddr==th_cache_daddr && th->dest==th_cache_dport && th->source==th_cache_sport)
 	{
 		sk=(struct sock *)th_cache_sk;
 		/*
@@ -5319,12 +5334,13 @@
 		 *	Find the first data byte.
 		 */
 		 
-		tcp_data_start = skb->data + skb->dev->hard_header_len + 
-				(iph->ihl << 2) + th->doff * 4;
+		tcp_data_start = skb->ip_hdr + 
+				((iph->ihl + th->doff) << 2);
 
 		/*
 		 *	Add it to our new buffer
 		 */
+		 
 		memcpy(skb_put(buff,win_size), tcp_data_start, win_size);
 		
 		/*
@@ -5333,38 +5349,8 @@
 		 
 	    	buff->end_seq = sk->sent_seq + win_size;
 	    	sk->sent_seq = buff->end_seq;		/* Hack */
-#if 0
-
-	    	/*
-	    	 *	now: shrink the queue head segment 
-	    	 */
-	    	 
-		th->check = 0;
-	    	ow_size = skb->len - win_size - 
-	    		((unsigned long) (tcp_data_start - (void *) skb->data));
-
-		memmove(tcp_data_start, tcp_data_start + win_size, ow_size);
-	    	skb_trim(skb,skb->len-win_size);
-	    	sk->sent_seq += win_size;
-	    	th->seq = htonl(sk->sent_seq);
-	    	if (th->urg)
-	    	{
-			unsigned short urg_ptr;
-	
-			urg_ptr = ntohs(th->urg_ptr);
-			if (urg_ptr <= win_size)
-				th->urg = 0;
-			else
-			{
-				urg_ptr -= win_size;
-		    		th->urg_ptr = htons(urg_ptr);
-		    		nth->urg_ptr = htons(win_size);
-		  	}
-	      	}
-#else
 		if(th->urg && ntohs(th->urg_ptr) < win_size)
 			nth->urg = 0;
-#endif	      	
 
 		/*
 		 *	Checksum the split buffer
diff -u --recursive --new-file v1.3.50/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.50/linux/net/ipx/af_ipx.c	Mon Dec 11 15:42:11 1995
+++ linux/net/ipx/af_ipx.c	Tue Dec 26 06:03:02 1995
@@ -404,19 +404,8 @@
 	 * that skb1 and skb2 point to it (them) so that it (they) can be 
 	 * demuxed to sock1 and/or sock2.  If we are unable to make enough
 	 * copies, we do as much as is possible.
-	 *
-	 * Firstly stop charging the sender for the space. We will
-	 * charge the recipient or discard. If we are called from ipx_rcv
-	 * this is ok as no socket owns an input buffer.
 	 */
 	 
-	if(skb->sk && !copy)
-	{
-		skb->sk->wmem_alloc -= skb->truesize;	/* Adjust */
-		skb->sk=NULL;				/* Disown */
-	}
-
-	 
 	if (copy) 
 	{
 		skb1 = skb_clone(skb, GFP_ATOMIC);
@@ -499,7 +488,7 @@
 	 */
 	 
 	if ((dl == NULL) || (dev == NULL) || (dev->flags & IFF_LOOPBACK)) 
-		send_to_wire = 0;
+		send_to_wire = 0;	/* No non looped */
 
 	/*
 	 *	See if this should be demuxed to sockets on this interface 
@@ -514,12 +503,24 @@
 		 *	To our own node, loop and free the original.
 		 */
 		if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0) 
+		{
+			/*
+			 *	Don't charge sender
+			 */
+			if(skb->sk)
+				skb->sk->wmem_alloc-=skb->truesize;
+			/*
+			 *	Will charge receiver
+			 */
 			return ipxitf_demux_socket(intrfc, skb, 0);
+		}
 		/*
 		 *	Broadcast, loop and possibly keep to send on.
 		 */
 		if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
 		{
+			if (!send_to_wire && skb->sk)
+				skb->sk->wmem_alloc-=skb->truesize;
 			ipxitf_demux_socket(intrfc, skb, send_to_wire);
 			if (!send_to_wire) 
 				return 0;
@@ -527,7 +528,9 @@
 	}
 
 	/*
-	 *	if the originating net is not equal to our net; this is routed 
+	 *	If the originating net is not equal to our net; this is routed 
+	 *	We are still charging the sender. Which is right - the driver
+	 *	free will handle this fairly.
 	 */
 	 
 	if (ipx->ipx_source.net != intrfc->if_netnum) 
diff -u --recursive --new-file v1.3.50/linux/net/socket.c linux/net/socket.c
--- v1.3.50/linux/net/socket.c	Sat Nov 25 19:04:59 1995
+++ linux/net/socket.c	Tue Dec 26 06:03:02 1995
@@ -1341,7 +1341,7 @@
 {
 	int i;
 
-	printk("Swansea University Computer Society NET3.033 for Linux 1.3.38\n");
+	printk("Swansea University Computer Society NET3.033 for Linux 1.3.50\n");
 
 	/*
 	 *	Initialize all address (protocol) families. 
diff -u --recursive --new-file v1.3.50/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.50/linux/net/unix/af_unix.c	Sat Nov 25 19:04:59 1995
+++ linux/net/unix/af_unix.c	Tue Dec 26 06:03:03 1995
@@ -799,8 +799,9 @@
 }
 
 /*
- * Sleep until data has arrive. But check for races..
+ *	Sleep until data has arrive. But check for races..
  */
+ 
 static void unix_data_wait(unix_socket * sk)
 {
 	cli();
@@ -1035,7 +1036,7 @@
 
 void unix_proto_init(struct net_proto *pro)
 {
-	printk("NET3: Unix domain sockets 0.10 BETA for Linux NET3.031.\n");
+	printk("NET3: Unix domain sockets 0.10 BETA for Linux NET3.033.\n");
 	sock_register(unix_proto_ops.family, &unix_proto_ops);
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_UNIX,  4, "unix",
