diff -u --recursive --new-file v1.1.12/linux/MAGIC linux/MAGIC
--- v1.1.12/linux/MAGIC	Thu Jan  1 02:00:00 1970
+++ linux/MAGIC	Mon May 23 07:55:16 1994
@@ -0,0 +1,42 @@
+This file is a registry of magic numbers which are in use.  When you
+add a magic number to a structure, you should also add it to this
+file, since it is best if the magic numbers used by various structures
+are unique.
+
+It is a *very* good idea to protect kernel data structures with magic
+numbers.  This allows you to check at run time whether (a) a structure
+has been clobbered, or (b) you've passed the wrong structure to a
+routine.  This last is especially useful --- particlarly when you are
+passing pointers to structures via a void * pointer.  The tty code,
+for example, does this frequently to pass driver-specific and line
+discipline-specific structures back and forth.
+
+The way to use magic numbers is to declare then at the beginning of
+the structure, like so:
+
+struct tty_ldisc {
+	int	magic;
+	...
+};
+
+Please follow this discpline when you are adding future enhancements
+to the kernel!  It has saved me countless hours of debugging,
+especially in the screw cases where an array has been overrun and
+structures following the array have been overwritten.  Using this
+discpline, these cases get detected quickly and safely.
+
+					Theodore Ts'o
+					31-Mar-94
+
+Magic Name          Number  Structure            File
+===========================================================================
+FASYNC_MAGIC        0x4601  struct fasync_struct include/linux/fs.h
+PTY_MAGIC           0x5001  struct pty_struct    drivers/char/pty.c
+PPP_MAGIC           0x5002  struct ppp_struct    include/linux/ppp.h
+TTY_MAGIC           0x5401  struct tty_struct    include/linux/tty.h
+TTY_DRIVER_MAGIC    0x5402  struct tty_driver    include/linux/tty_driver.h
+TTY_LDISC_MAGIC     0x5403  struct tty_ldisc     include/linux/tty_ldisc.h
+SERIAL_MAGIC        0x5301  struct async_struct  include/linux/serial.h
+SLIP_MAGIC          0x5302  struct slip          drivers/net/slip.h
+
+
diff -u --recursive --new-file v1.1.12/linux/Makefile linux/Makefile
--- v1.1.12/linux/Makefile	Mon May 23 12:14:21 1994
+++ linux/Makefile	Mon May 23 08:41:12 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 12
+SUBLEVEL = 13
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.12/linux/config.in linux/config.in
--- v1.1.12/linux/config.in	Mon May 23 12:14:21 1994
+++ linux/config.in	Mon May 23 11:43:20 1994
@@ -5,24 +5,24 @@
 
 comment 'General setup'
 
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
+bool 'Kernel math emulation' CONFIG_MATH_EMULATION n
 bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
-bool 'TCP/IP networking' CONFIG_INET y
+bool 'Networking support' CONFIG_NET y
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'System V IPC' CONFIG_SYSVIPC y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
 
-if [ "$CONFIG_INET" = "y" ]; then
-
+if [ "$CONFIG_NET" = "y" ]; then
 comment 'Networking options'
+bool 'TCP/IP networking' CONFIG_INET y
+if [ "$CONFIG_INET" "=" "y" ]; then
 comment '(it is safe to leave these untouched)'
-
-comment 'IP (required for now) y'
-bool 'Reverse ARP' CONFIG_INET_RARP y
+bool 'Reverse ARP' CONFIG_INET_RARP n
 bool 'Assume subnets are local' CONFIG_INET_SNARL y
 bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
-#bool 'Novell IPX protocol' CONFIG_IPX n
+fi
+bool 'The IPX protocol' CONFIG_IPX y
 #bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
 fi
 
@@ -62,10 +62,13 @@
 bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
 fi
 
+
+if [ "$CONFIG_NET" = "y" ]; then
+
 comment 'Network device support'
 
-bool 'Network device support?' CONFIG_ETHERCARDS y
-if [ "$CONFIG_ETHERCARDS" = "n" ]; then
+bool 'Network device support?' CONFIG_NETDEVICES y
+if [ "$CONFIG_NETDEVICES" = "n" ]; then
 
 comment 'Skipping ethercard configuration options...'
 
@@ -76,15 +79,16 @@
 #  bool ' SLIP debugging on' SL_DUMP y
 fi
 #bool 'PPP (point-to-point) support' CONFIG_PPP n
+bool 'Load balancing support (very experimental)' CONFIG_SLAVE_BALANCING n
 bool 'PLIP (parallel port) support' CONFIG_PLIP n
 bool 'NE2000/NE1000 support' CONFIG_NE2000 n
-bool 'WD80*3 support' CONFIG_WD80x3 y
+bool 'WD80*3 support' CONFIG_WD80x3 n
 bool 'SMC Ultra support' CONFIG_ULTRA n
 bool '3c501 support' CONFIG_EL1 n
 bool '3c503 support' CONFIG_EL2 n
 #bool '3c505 support' CONFIG_ELPLUS n
 #bool '3c507 support' CONFIG_EL16 n
-bool '3c509/3c579 support' CONFIG_EL3 n
+bool '3c509/3c579 support' CONFIG_EL3 y
 bool 'HP PCLAN support' CONFIG_HPLAN n
 bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
 bool 'AT1700 support' CONFIG_AT1700 n
@@ -98,6 +102,7 @@
 bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
 bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
 fi
+fi
 
 comment 'CD-ROM drivers'
 
@@ -113,7 +118,9 @@
 bool 'xiafs filesystem support' CONFIG_XIA_FS n
 bool 'msdos fs support' CONFIG_MSDOS_FS y
 bool '/proc filesystem support' CONFIG_PROC_FS y
+if [ "$CONFIG_INET" = "y" ]; then
 bool 'NFS filesystem support' CONFIG_NFS_FS y
+fi
 bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
 bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
 bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
@@ -122,7 +129,7 @@
 
 bool 'Parallel printer support' CONFIG_PRINTER n
 bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
-bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
+bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
 if [ "$CONFIG_PSMOUSE" = "y" ]; then
 bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
 fi
diff -u --recursive --new-file v1.1.12/linux/drivers/FPU-emu/fpu_emu.h linux/drivers/FPU-emu/fpu_emu.h
--- v1.1.12/linux/drivers/FPU-emu/fpu_emu.h	Sat Mar 26 14:04:23 1994
+++ linux/drivers/FPU-emu/fpu_emu.h	Mon May 23 10:04:05 1994
@@ -103,9 +103,9 @@
 typedef struct fpu_reg FPU_REG;
 typedef struct { unsigned char address_size, operand_size, segment; }
         overrides;
-/* This structure is 32 bits: */
+/* This structure is 48 bits: */
 typedef struct { overrides override;
-		 unsigned char vm86; } fpu_addr_modes;
+		 unsigned char mode16, vm86, p286; } fpu_addr_modes;
 
 #define	st(x)	( regs[((top+x) &7 )] )
 
diff -u --recursive --new-file v1.1.12/linux/drivers/FPU-emu/fpu_entry.c linux/drivers/FPU-emu/fpu_entry.c
--- v1.1.12/linux/drivers/FPU-emu/fpu_entry.c	Sat Mar 26 14:04:24 1994
+++ linux/drivers/FPU-emu/fpu_entry.c	Mon May 23 10:04:05 1994
@@ -173,13 +173,20 @@
   SETUP_DATA_AREA(arg);
 
   addr_modes.vm86 = (FPU_EFLAGS & 0x00020000) != 0;
+  addr_modes.p286 = (!addr_modes.vm86
+		     && current->ldt
+		     && (current->ldt[FPU_CS >> 3].b & 0xf000) == 0xf000
+		     && (current->ldt[FPU_CS >> 3].b & (1 << 22)) == 0);
+  addr_modes.mode16 = addr_modes.vm86 | addr_modes.p286;
 
   if ( addr_modes.vm86 )
     FPU_EIP += FPU_CS << 4;
+  else if ( addr_modes.p286 )
+    FPU_EIP += LDT_BASE_ADDR(FPU_CS);
 
   FPU_ORIG_EIP = FPU_EIP;
 
-  if ( !addr_modes.vm86 )
+  if ( !addr_modes.mode16 )
     {
       /* user code space? */
       if (FPU_CS == KERNEL_CS)
@@ -283,6 +290,8 @@
 
 	  if ( addr_modes.vm86 )
 	    FPU_EIP -= FPU_CS << 4;
+	  else if ( addr_modes.p286 )
+	    FPU_EIP -= LDT_BASE_ADDR(FPU_CS);
 
 	  RE_ENTRANT_CHECK_OFF;
 	  current->tss.trap_no = 16;
@@ -552,6 +561,8 @@
 
   if ( addr_modes.vm86 )
     FPU_EIP -= FPU_CS << 4;
+  else if ( addr_modes.p286 )
+    FPU_EIP -= LDT_BASE_ADDR(FPU_CS);
 
   RE_ENTRANT_CHECK_OFF;
 }
diff -u --recursive --new-file v1.1.12/linux/drivers/FPU-emu/fpu_system.h linux/drivers/FPU-emu/fpu_system.h
--- v1.1.12/linux/drivers/FPU-emu/fpu_system.h	Wed Feb 16 13:07:56 1994
+++ linux/drivers/FPU-emu/fpu_system.h	Mon May 23 10:04:05 1994
@@ -30,6 +30,10 @@
 #define FPU_EIP			(FPU_info->___eip)
 #define FPU_ORIG_EIP		(FPU_info->___orig_eip)
 
+#define LDT_BASE_ADDR(s)	((current->ldt[(s) >> 3].b & 0xff000000)     \
+				 | ((current->ldt[(s) >> 3].b & 0xff) << 16) \
+				 | (current->ldt[(s) >> 3].a >> 16))
+
 #define FPU_lookahead           (I387.soft.lookahead)
 #define FPU_entry_eip           (I387.soft.entry_eip)
 
diff -u --recursive --new-file v1.1.12/linux/drivers/FPU-emu/get_address.c linux/drivers/FPU-emu/get_address.c
--- v1.1.12/linux/drivers/FPU-emu/get_address.c	Sat Mar 26 14:04:24 1994
+++ linux/drivers/FPU-emu/get_address.c	Mon May 23 10:04:05 1994
@@ -52,7 +52,20 @@
 #define VM86_REG_(x) (*(unsigned short *) \
 		      (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
 
+static int reg_offset_p286[] = {
+	offsetof(struct info,___cs),
+	offsetof(struct info,___ds),
+	offsetof(struct info,___es),
+	offsetof(struct info,___fs),
+	offsetof(struct info,___gs),
+	offsetof(struct info,___ss),
+	offsetof(struct info,___ds)
+      };
 
+#define P286_REG_(x) (*(unsigned short *) \
+		      (reg_offset_p286[((unsigned)x)]+(char *) FPU_info))
+
+
 /* Decode the SIB byte. This function assumes mod != 0 */
 static void *sib(int mod, unsigned long *fpu_eip)
 {
@@ -108,9 +121,10 @@
 }
 
 
-static unsigned long vm86_segment(unsigned char segment)
+static unsigned long mode16_segment(fpu_addr_modes addr_modes)
 { 
-  segment--;
+  int segment = addr_modes.override.segment - 1;
+
 #ifdef PARANOID
   if ( segment > PREFIX_SS_ )
     {
@@ -118,7 +132,11 @@
       math_abort(FPU_info,SIGSEGV);
     }
 #endif PARANOID
-  return (unsigned long)VM86_REG_(segment) << 4;
+  if ( addr_modes.vm86 )
+    return (unsigned long)VM86_REG_(segment) << 4;
+  else if ( addr_modes.p286 )
+    return (unsigned long)LDT_BASE_ADDR(P286_REG_(segment));
+  return 0;
 }
 
 
@@ -152,7 +170,7 @@
 #endif PECULIAR_486
 
   /* Memory accessed via the cs selector is write protected
-     in 32 bit protected mode. */
+     in protected mode. */
 #define FPU_WRITE_BIT 0x10
   if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT)
       && (addr_modes.override.segment == PREFIX_CS_) )
@@ -210,9 +228,9 @@
       EXCEPTION(EX_Invalid);
     }
 
-  if ( addr_modes.vm86 )
+  if ( addr_modes.mode16 )
     {
-      offset += vm86_segment(addr_modes.override.segment);
+      offset += mode16_segment(addr_modes);
     }
 
   FPU_data_address = offset + (char *)*cpu_reg_ptr;
@@ -231,7 +249,7 @@
 #endif PECULIAR_486
 
   /* Memory accessed via the cs selector is write protected
-     in 32 bit protected mode. */
+     in protected mode. */
 #define FPU_WRITE_BIT 0x10
   if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT)
       && (addr_modes.override.segment == PREFIX_CS_) )
@@ -313,9 +331,9 @@
  add_segment:
   offset &= 0xffff;
 
-  if ( addr_modes.vm86 )
+  if ( addr_modes.mode16 )
     {
-      offset += vm86_segment(addr_modes.override.segment);
+      offset += mode16_segment(addr_modes);
     }
 
   FPU_data_address = (void *)offset ;
diff -u --recursive --new-file v1.1.12/linux/drivers/FPU-emu/reg_ld_str.c linux/drivers/FPU-emu/reg_ld_str.c
--- v1.1.12/linux/drivers/FPU-emu/reg_ld_str.c	Wed Feb 16 13:07:56 1994
+++ linux/drivers/FPU-emu/reg_ld_str.c	Mon May 23 10:04:05 1994
@@ -1170,7 +1170,7 @@
   unsigned char tag;
   int i;
 
-  if ( addr_modes.vm86
+  if ( addr_modes.mode16
       || (addr_modes.override.operand_size == OP_SIZE_PREFIX) )
     {
       RE_ENTRANT_CHECK_OFF;
@@ -1189,6 +1189,11 @@
 	  ip_offset += (cs_selector & 0xf000) << 4;
 	  data_operand_offset += (operand_selector & 0xf000) << 4;
 	}
+      else if ( addr_modes.p286 )
+	{
+	  ip_offset += LDT_BASE_ADDR(cs_selector);
+	  data_operand_offset += LDT_BASE_ADDR(operand_selector);
+	}
     }
   else
     {
@@ -1317,7 +1322,7 @@
 {
   char *d = (char *)FPU_data_address;
 
-  if ( addr_modes.vm86
+  if ( addr_modes.mode16
       || (addr_modes.override.operand_size == OP_SIZE_PREFIX) )
     {
       RE_ENTRANT_CHECK_OFF;
diff -u --recursive --new-file v1.1.12/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.1.12/linux/drivers/char/Makefile	Sun Jan  9 15:14:58 1994
+++ linux/drivers/char/Makefile	Mon May 23 07:55:12 1994
@@ -16,7 +16,7 @@
 .c.o:
 	$(CC) $(CFLAGS) -c $<
 
-OBJS  = tty_io.o console.o keyboard.o serial.o \
+OBJS  = tty_io.o n_tty.o console.o keyboard.o serial.o \
 	tty_ioctl.o pty.o vt.o mem.o \
 	defkeymap.o
 
diff -u --recursive --new-file v1.1.12/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.1.12/linux/drivers/char/console.c	Wed Feb 23 08:52:24 1994
+++ linux/drivers/char/console.c	Mon May 23 07:55:12 1994
@@ -48,20 +48,34 @@
 
 #include <linux/sched.h>
 #include <linux/timer.h>
+#include <linux/interrupt.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/kd.h>
+#include <linux/major.h>
 
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/segment.h>
+#include <asm/bitops.h>
 
 #include "kbd_kern.h"
 #include "vt_kern.h"
 
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+struct tty_driver console_driver;
+static int console_refcount;
+static struct tty_struct *console_table[NR_CONSOLES];
+static struct termios *console_termios[NR_CONSOLES];
+static struct termios *console_termios_locked[NR_CONSOLES];
+
 #ifdef CONFIG_SELECTION
 #include <linux/ctype.h>
 
@@ -71,7 +85,7 @@
 static void clear_selection(void);
 
 /* Variables for selection control. */
-#define SEL_BUFFER_SIZE TTY_BUF_SIZE
+#define SEL_BUFFER_SIZE 4096
 static int sel_cons;
 static int sel_start = -1;
 static int sel_end;
@@ -156,7 +170,7 @@
 #define bottom		(vc_cons[currcons].vc_bottom)
 #define x		(vc_cons[currcons].vc_x)
 #define y		(vc_cons[currcons].vc_y)
-#define state		(vc_cons[currcons].vc_state)
+#define vc_state		(vc_cons[currcons].vc_state)
 #define npar		(vc_cons[currcons].vc_npar)
 #define par		(vc_cons[currcons].vc_par)
 #define ques		(vc_cons[currcons].vc_ques)
@@ -699,49 +713,31 @@
 	update_attr(currcons);
 }
 
-static void respond_string(char * p, int currcons, struct tty_struct * tty)
+static void respond_string(char * p, struct tty_struct * tty)
 {
 	while (*p) {
-		put_tty_queue(*p, &tty->read_q);
+		tty_insert_flip_char(tty, *p, 0);
 		p++;
 	}
-	TTY_READ_FLUSH(tty);
-}
-
-static void respond_num(unsigned int n, int currcons, struct tty_struct * tty)
-{
-	char buff[3];
-	int i = 0;
-
-	do {
-		buff[i++] = (n%10)+'0';
-		n /= 10;
-	} while(n && i < 3);	/* We'll take no chances */
-	while (i--) {
-		put_tty_queue(buff[i], &tty->read_q);
-	}
-	/* caller must flush */
+	tty_schedule_flip(tty);
 }
 
 static void cursor_report(int currcons, struct tty_struct * tty)
 {
-	put_tty_queue('\033', &tty->read_q);
-	put_tty_queue('[', &tty->read_q);
-	respond_num(y + (decom ? top+1 : 1), currcons, tty);
-	put_tty_queue(';', &tty->read_q);
-	respond_num(x+1, currcons, tty);
-	put_tty_queue('R', &tty->read_q);
-	TTY_READ_FLUSH(tty);
+	char buf[40];
+
+	sprintf(buf, "\033[%ld;%ldR", y + (decom ? top+1 : 1), x+1);
+	respond_string(buf, tty);
 }
 
 static inline void status_report(int currcons, struct tty_struct * tty)
 {
-	respond_string("\033[0n", currcons, tty);	/* Terminal ok */
+	respond_string("\033[0n", tty);	/* Terminal ok */
 }
 
 static inline void respond_ID(int currcons, struct tty_struct * tty)
 {
-	respond_string(VT102ID, currcons, tty);
+	respond_string(VT102ID, tty);
 }
 
 static void invert_screen(int currcons) {
@@ -953,7 +949,7 @@
 {
 	top		= 0;
 	bottom		= video_num_lines;
-	state		= ESnormal;
+	vc_state		= ESnormal;
 	ques		= 0;
 	translate	= NORM_TRANS;
 	G0_charset	= NORM_TRANS;
@@ -991,25 +987,46 @@
 	}
 }
 
-void con_write(struct tty_struct * tty)
+/*
+ * Turn the Scroll-Lock LED on when the tty is stopped (with a ^S)
+ */
+static void con_stop(struct tty_struct *tty)
+{
+	set_vc_kbd_led(kbd_table + fg_console, VC_SCROLLOCK);
+	set_leds();
+}
+
+/*
+ * Turn the Scroll-Lock LED off when the console is started (with a ^Q)
+ */
+static void con_start(struct tty_struct *tty)
+{
+	clr_vc_kbd_led(kbd_table + fg_console, VC_SCROLLOCK);
+	set_leds();
+}
+
+static int con_write(struct tty_struct * tty, int from_user,
+		     unsigned char *buf, int count)
 {
-	int c;
+	int c, n = 0;
 	unsigned int currcons;
+	struct vt_struct *vt = tty->driver_data;
 
-	currcons = tty->line - 1;
+	currcons = vt->vc_num;
 	if (currcons >= NR_CONSOLES) {
-		printk("con_write: illegal tty (%d)\n", currcons);
-		return;
+		printk("con_write: illegal vc index (%d)\n", currcons);
+		return 0;
 	}
 #ifdef CONFIG_SELECTION
-	/* clear the selection as soon as any characters are to be written
-	   out on the console holding the selection. */
-	if (!EMPTY(&tty->write_q) && currcons == sel_cons)
+	/* clear the selection */
+	if (currcons == sel_cons)
 		clear_selection();
 #endif /* CONFIG_SELECTION */
 	disable_bh(KEYBOARD_BH);
-	while (!tty->stopped &&	(c = get_tty_queue(&tty->write_q)) >= 0) {
-		if (state == ESnormal && translate[c]) {
+	while (!tty->stopped &&	count) {
+		c = from_user ? get_fs_byte(buf) : *buf;
+		buf++; n++; count--;
+		if (vc_state == ESnormal && translate[c]) {
 			if (need_wrap) {
 				cr(currcons);
 				lf(currcons);
@@ -1063,24 +1080,24 @@
 				translate = G0_charset;
 				continue;
 			case 24: case 26:
-				state = ESnormal;
+				vc_state = ESnormal;
 				continue;
 			case 27:
-				state = ESesc;
+				vc_state = ESesc;
 				continue;
 			case 127:
 				del(currcons);
 				continue;
 			case 128+27:
-				state = ESsquare;
+				vc_state = ESsquare;
 				continue;
 		}
-		switch(state) {
+		switch(vc_state) {
 			case ESesc:
-				state = ESnormal;
+				vc_state = ESnormal;
 				switch (c) {
 				  case '[':
-					state = ESsquare;
+					vc_state = ESsquare;
 					continue;
 				  case 'E':
 					cr(currcons);
@@ -1105,13 +1122,13 @@
 					restore_cur(currcons);
 					continue;
 				  case '(':
-					state = ESsetG0;
+					vc_state = ESsetG0;
 					continue;
 				  case ')':
-					state = ESsetG1;
+					vc_state = ESsetG1;
 					continue;
 				  case '#':
-					state = EShash;
+					vc_state = EShash;
 					continue;
 				  case 'c':
 					reset_terminal(currcons,1);
@@ -1128,9 +1145,9 @@
 				for(npar = 0 ; npar < NPAR ; npar++)
 					par[npar] = 0;
 				npar = 0;
-				state = ESgetpars;
+				vc_state = ESgetpars;
 				if (c == '[') { /* Function key */
-					state=ESfunckey;
+					vc_state=ESfunckey;
 					continue;
 				}
 				ques = (c=='?');
@@ -1144,9 +1161,9 @@
 					par[npar] *= 10;
 					par[npar] += c-'0';
 					continue;
-				} else state=ESgotpars;
+				} else vc_state=ESgotpars;
 			case ESgotpars:
-				state = ESnormal;
+				vc_state = ESnormal;
 				switch(c) {
 					case 'h':
 						set_mode(currcons,1);
@@ -1265,10 +1282,10 @@
 				}
 				continue;
 			case ESfunckey:
-				state = ESnormal;
+				vc_state = ESnormal;
 				continue;
 			case EShash:
-				state = ESnormal;
+				vc_state = ESnormal;
 				if (c == '8') {
 					/* DEC screen alignment test. kludge :-) */
 					video_erase_char =
@@ -1289,7 +1306,7 @@
 					G0_charset = USER_TRANS;
 				if (charset == 0)
 					translate = G0_charset;
-				state = ESnormal;
+				vc_state = ESnormal;
 				continue;
 			case ESsetG1:
 				if (c == '0')
@@ -1302,22 +1319,32 @@
 					G1_charset = USER_TRANS;
 				if (charset == 1)
 					translate = G1_charset;
-				state = ESnormal;
+				vc_state = ESnormal;
 				continue;
 			default:
-				state = ESnormal;
+				vc_state = ESnormal;
 		}
 	}
 	if (vcmode != KD_GRAPHICS)
 		set_cursor(currcons);
 	enable_bh(KEYBOARD_BH);
-	if (LEFT(&tty->write_q) > WAKEUP_CHARS)
-		wake_up_interruptible(&tty->write_q.proc_list);
+	return n;
+}
+
+static int con_write_room(struct tty_struct *tty)
+{
+	if (tty->stopped)
+		return 0;
+	return 4096;		/* No limit, really; we're not buffering */
+}
+
+static int con_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;		/* we're not buffering */
 }
 
-void do_keyboard_interrupt(void)
+void poke_blanked_console(void)
 {
-	TTY_READ_FLUSH(TTY_TABLE(0));
 	timer_active &= ~(1<<BLANK_TIMER);
 	if (vt_cons[fg_console].vc_mode == KD_GRAPHICS)
 		return;
@@ -1378,6 +1405,22 @@
 }
 
 /*
+ * con_throttle and con_unthrottle are only used for
+ * paste_selection(), which has to stuff in a large number of
+ * characters...
+ */
+static void con_throttle(struct tty_struct *tty)
+{
+}
+
+static void con_unthrottle(struct tty_struct *tty)
+{
+	struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+
+	wake_up_interruptible(&vt->paste_wait);
+}
+
+/*
  *  long con_init(long);
  *
  * This routine initalizes console interrupts, and does nothing
@@ -1395,6 +1438,34 @@
 	int orig_x = ORIG_X;
 	int orig_y = ORIG_Y;
 
+	memset(&console_driver, 0, sizeof(struct tty_driver));
+	console_driver.magic = TTY_DRIVER_MAGIC;
+	console_driver.name = "tty";
+	console_driver.name_base = 1;
+	console_driver.major = TTY_MAJOR;
+	console_driver.minor_start = 1;
+	console_driver.num = NR_CONSOLES;
+	console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
+	console_driver.init_termios = tty_std_termios;
+	console_driver.flags = TTY_DRIVER_REAL_RAW;
+	console_driver.refcount = &console_refcount;
+	console_driver.table = console_table;
+	console_driver.termios = console_termios;
+	console_driver.termios_locked = console_termios_locked;
+
+	console_driver.open = con_open;
+	console_driver.write = con_write;
+	console_driver.write_room = con_write_room;
+	console_driver.chars_in_buffer = con_chars_in_buffer;
+	console_driver.ioctl = vt_ioctl;
+	console_driver.stop = con_stop;
+	console_driver.start = con_start;
+	console_driver.throttle = con_throttle;
+	console_driver.unthrottle = con_unthrottle;
+	
+	if (tty_register_driver(&console_driver))
+		panic("Couldn't register console driver\n");
+	
 	vc_scrmembuf = (unsigned short *) kmem_start;
 	video_num_columns = ORIG_VIDEO_COLS;
 	video_size_row = video_num_columns * 2;
@@ -1466,6 +1537,7 @@
 		def_color	= 0x07;   /* white */
 		ulcolor		= 0x0f;   /* bold white */
 		halfcolor	= 0x08;   /* grey */
+		vt_cons[currcons].paste_wait = 0;
 		reset_terminal(currcons, currcons);
 	}
 	currcons = fg_console = 0;
@@ -1613,15 +1685,24 @@
 }
 
 /*
- * All we do is set the write and ioctl subroutines; later on maybe we'll
- * dynamically allocate the console screen memory.
+ * Later on maybe we'll dynamically allocate the console screen
+ * memory.
  */
 int con_open(struct tty_struct *tty, struct file * filp)
 {
-	tty->write = con_write;
-	tty->ioctl = vt_ioctl;
-	if (tty->line > NR_CONSOLES)
+	int	idx;
+
+	idx = MINOR(tty->device) - tty->driver.minor_start;
+	
+	if (idx > NR_CONSOLES)
 		return -ENODEV;
+	vt_cons[idx].vc_num = idx;
+	tty->driver_data = &vt_cons[idx];
+	
+	if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
+		tty->winsize.ws_row = video_num_lines;
+		tty->winsize.ws_col = video_num_columns;
+	}
 	return 0;
 }
 
@@ -1801,16 +1882,28 @@
    tty associated with the current console. Invoked by ioctl(). */
 int paste_selection(struct tty_struct *tty)
 {
-	char *bp = sel_buffer;
-
-	if (! *bp)
+	struct wait_queue wait = { current, NULL };
+	char	*bp = sel_buffer;
+	int	c, l;
+	struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+	
+	if (!sel_buffer[0])
 		return 0;
 	unblank_screen();
-	while (*bp) {
-		put_tty_queue(*bp, &tty->read_q);
-		bp++;
-		TTY_READ_FLUSH(tty);
+	c = strlen(sel_buffer);
+	current->state = TASK_INTERRUPTIBLE;
+	add_wait_queue(&vt->paste_wait, &wait);
+	while (c) {
+		if (test_bit(TTY_THROTTLED, &tty->flags)) {
+			schedule();
+			continue;
+		}
+		l = MIN(c, tty->ldisc.receive_room(tty));
+		tty->ldisc.receive_buf(tty, bp, 0, l);
+		c -= l;
+		bp += l;
 	}
+	current->state = TASK_RUNNING;
 	return 0;
 }
 
diff -u --recursive --new-file v1.1.12/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v1.1.12/linux/drivers/char/keyboard.c	Tue Apr 19 10:52:43 1994
+++ linux/drivers/char/keyboard.c	Mon May 23 07:55:12 1994
@@ -14,10 +14,11 @@
 #define KEYBOARD_IRQ 1
 
 #include <linux/sched.h>
+#include <linux/interrupt.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/mm.h>
 #include <linux/ptrace.h>
-#include <linux/interrupt.h>
 #include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/string.h>
@@ -26,6 +27,7 @@
 
 #include "kbd_kern.h"
 #include "diacr.h"
+#include "vt_kern.h"
 
 #define SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
@@ -59,7 +61,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
-extern void do_keyboard_interrupt(void);
+extern void poke_blanked_console(void);
 extern void ctrl_alt_del(void);
 extern void change_console(unsigned int new_console);
 extern void scrollback(int);
@@ -89,6 +91,7 @@
 static unsigned char diacr = 0;
 static char rep = 0;			/* flag telling character repeat */
 struct kbd_struct kbd_table[NR_CONSOLES];
+static struct tty_struct **ttytab;
 static struct kbd_struct * kbd = kbd_table;
 static struct tty_struct * tty = NULL;
 
@@ -110,7 +113,7 @@
 
 /* maximum values each key_handler can handle */
 const int max_vals[] = {
-	255, NR_FUNC - 1, 14, 17, 4, 255, 3, NR_SHIFT,
+	255, NR_FUNC - 1, 15, 17, 4, 255, 3, NR_SHIFT,
 	255, 9, 3, 255
 };
 
@@ -118,12 +121,11 @@
 
 static void put_queue(int);
 static unsigned char handle_diacr(unsigned char);
+static void SAK(void);
 
 /* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
 static struct pt_regs * pt_regs;
 
-static int got_break = 0;
-
 static inline void kb_wait(void)
 {
 	int i;
@@ -224,7 +226,7 @@
 		prev_scancode = 0;
 		goto end_kbd_intr;
 	}
-	tty = TTY_TABLE(0);
+	tty = ttytab[fg_console];
  	kbd = kbd_table + fg_console;
 	if ((raw_mode = vc_kbd_mode(kbd,VC_RAW))) {
  		put_queue(scancode);
@@ -332,8 +334,7 @@
 	 */
 	if (!rep ||
 	    (vc_kbd_mode(kbd,VC_REPEAT) && tty &&
-	     (L_ECHO(tty) || (EMPTY(&tty->secondary) && EMPTY(&tty->read_q)))))
-	{
+	     (L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) {
 		u_short key_code;
 		u_char type;
 
@@ -357,36 +358,24 @@
 
 static void put_queue(int ch)
 {
-	struct tty_queue *qp;
-
 	wake_up(&keypress_wait);
-	if (!tty)
-		return;
-	qp = &tty->read_q;
-
-	if (LEFT(qp)) {
-		qp->buf[qp->head] = ch;
-		INC(qp->head);
+	if (tty) {
+		tty_insert_flip_char(tty, ch, 0);
+		tty_schedule_flip(tty);
 	}
 }
 
 static void puts_queue(char *cp)
 {
-	struct tty_queue *qp;
-	char ch;
-
-	/* why interruptible here, plain wake_up above? */
-	wake_up_interruptible(&keypress_wait);
+	wake_up(&keypress_wait);
 	if (!tty)
 		return;
-	qp = &tty->read_q;
 
-	while ((ch = *(cp++)) != 0) {
-		if (LEFT(qp)) {
-			qp->buf[qp->head] = ch;
-			INC(qp->head);
-		}
+	while (*cp) {
+		tty_insert_flip_char(tty, *cp, 0);
+		cp++;
 	}
+	tty_schedule_flip(tty);
 }
 
 static void applkey(int key, char mode)
@@ -479,7 +468,9 @@
 
 static void send_intr(void)
 {
-	got_break = 1;
+	if (tty->termios && I_IGNBRK(tty))
+		return;
+	tty_insert_flip_char(tty, 0, TTY_BREAK);
 }
 
 static void scrll_forw(void)
@@ -502,6 +493,33 @@
 	dead_key_next = 1;
 }
 
+static void SAK(void)
+{
+	do_SAK(tty);
+#if 0
+	/*
+	 * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
+	 * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
+	 * handling.
+	 * 
+	 * We should do this some day --- the whole point of a secure
+	 * attention key is that it should be guaranteed to always
+	 * work.
+	 */
+	clr_vc_kbd_flag(kbd, VC_RAW);
+	clr_vc_kbd_flag(kbd, VC_MEDIUMRAW);
+	vt_cons[fg_console].vc_mode = KD_TEXT;
+	vt_cons[fg_console].vt_mode.mode = VT_AUTO;
+	vt_cons[fg_console].vt_mode.waitv = 0;
+	vt_cons[fg_console].vt_mode.relsig = 0;
+	vt_cons[fg_console].vt_mode.acqsig = 0;
+	vt_cons[fg_console].vt_mode.frsig = 0;
+	vt_cons[fg_console].vt_pid = -1;
+	vt_cons[fg_console].vt_newvt = -1;
+	unblank_screen();
+#endif
+}
+
 static void do_spec(unsigned char value, char up_flag)
 {
 	typedef void (*fnp)(void);
@@ -509,7 +527,7 @@
 		NULL,		enter,		show_ptregs,	show_mem,
 		show_state,	send_intr,	lastcons,	caps_toggle,
 		num,		hold,		scrll_forw,	scrll_back,
-		boot_it,	caps_on,        compose
+		boot_it,	caps_on,        compose,        SAK
 	};
 
 	if (up_flag)
@@ -683,7 +701,8 @@
 	/* kludge... */
 	if (value == KVAL(K_CAPSSHIFT)) {
 		value = KVAL(K_SHIFT);
-		clr_vc_kbd_led(kbd, VC_CAPSLOCK);
+		if (!up_flag)
+			clr_vc_kbd_led(kbd, VC_CAPSLOCK);
 	}
 
 	if (up_flag) {
@@ -819,27 +838,7 @@
 		}
 		want_console = -1;
 	}
-	if (got_break) {
-		if (tty && !I_IGNBRK(tty)) {
-			if (I_BRKINT(tty)) {
-				flush_input(tty);
-				flush_output(tty);
-				if (tty->pgrp > 0)
-					kill_pg(tty->pgrp, SIGINT, 1);
-			} else {
-				cli();
-				if (LEFT(&tty->read_q) >= 2) {
-					set_bit(tty->read_q.head,
-						&tty->readq_flags);
-					put_queue(TTY_BREAK);
-					put_queue(0);
-				}
-				sti();
-			}
-		}
-		got_break = 0;
-	}
-	do_keyboard_interrupt();
+	poke_blanked_console();
 	cli();
 	if ((inb_p(0x64) & kbd_read_mask) == 0x01)
 		fake_keyboard_interrupt();
@@ -877,6 +876,7 @@
 {
 	int i;
 	struct kbd_struct * kbd;
+	extern struct tty_driver console_driver;
 
 	kbd = kbd_table + 0;
 	for (i = 0 ; i < NR_CONSOLES ; i++,kbd++) {
@@ -885,6 +885,7 @@
 		kbd->lockstate = KBD_DEFLOCK;
 		kbd->modeflags = KBD_DEFMODE;
 	}
+	ttytab = console_driver.table;
 
 	bh_base[KEYBOARD_BH].routine = kbd_bh;
 	request_irq(KEYBOARD_IRQ,keyboard_interrupt);
diff -u --recursive --new-file v1.1.12/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v1.1.12/linux/drivers/char/n_tty.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/n_tty.c	Mon May 23 09:00:08 1994
@@ -0,0 +1,997 @@
+/*
+ * n_tty.c --- implements the N_TTY line discpline.
+ * 
+ * This code used to be in tty_io.c, but things are getting hairy
+ * enough that it made sense to split things off.  (The N_TTY
+ * processing has changed so much that it's hardly recognizable,
+ * anyway...)
+ *
+ * Note that the open routine for N_TTY is guaranteed never to return
+ * an error.  This is because Linux will fall back to setting a line
+ * to N_TTY if it can not switch to any other line discpline.  
+ *
+ * Written by Theodore Ts'o, Copyright 1994.
+ * 
+ * This file also contains code originally written by Linus Torvalds,
+ * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
+ * 
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/* number of characters left in xmit buffer before select has we have room */
+#define WAKEUP_CHARS 256
+
+/*
+ * This defines the low- and high-watermarks for throttling and
+ * unthrottling the TTY driver.  These watermarks are used for
+ * controlling the space in the read buffer.
+ */
+#define TTY_THRESHOLD_THROTTLE		(N_TTY_BUF_SIZE - 128)
+#define TTY_THRESHOLD_UNTHROTTLE 	128
+
+static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
+{
+	if (tty->read_cnt < N_TTY_BUF_SIZE) {
+		tty->read_buf[tty->read_head] = c;
+		tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt++;
+	}
+}
+
+/*
+ * Flush the input buffer
+ */
+void n_tty_flush_buffer(struct tty_struct * tty)
+{
+	tty->read_head = tty->read_tail = tty->read_cnt = 0;
+	tty->canon_head = tty->canon_data = tty->erasing = 0;
+	memset(&tty->read_flags, 0, sizeof tty->read_flags);
+	
+	if (!tty->link)
+		return;
+
+	wake_up_interruptible(&tty->link->write_wait);
+	if (tty->link->packet) {
+		tty->ctrl_status |= TIOCPKT_FLUSHREAD;
+		wake_up_interruptible(&tty->link->read_wait);
+	}
+}
+
+/*
+ * Return number of characters buffered to be delievered to user
+ */
+int n_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	return tty->read_cnt;
+}
+
+/*
+ * Perform OPOST processing.  Returns -1 when the output device is
+ * full and the character must be retried.
+ */
+static int opost(unsigned char c, struct tty_struct *tty)
+{
+	int	space, spaces;
+
+	space = tty->driver.write_room(tty);
+	if (!space)
+		return -1;
+
+	if (O_OPOST(tty)) {
+		switch (c) {
+		case '\n':
+			if (O_ONLRET(tty))
+				tty->column = 0;
+			if (O_ONLCR(tty)) {
+				if (space < 2)
+					return -1;
+				tty->driver.write(tty, 0, "\r", 1);
+				tty->column = 0;
+			}
+			tty->canon_column = tty->column;
+			break;
+		case '\r':
+			if (O_ONOCR(tty) && tty->column == 0)
+				return 0;
+			if (O_OCRNL(tty)) {
+				c = '\n';
+				if (O_ONLRET(tty))
+					tty->canon_column = tty->column = 0;
+				break;
+			}
+			tty->canon_column = tty->column = 0;
+			break;
+		case '\t':
+			spaces = 8 - (tty->column & 7);
+			if (O_TABDLY(tty) == XTABS) {
+				if (space < spaces)
+					return -1;
+				tty->column += spaces;
+				tty->driver.write(tty, 0, "        ", spaces);
+				return 0;
+			}
+			tty->column += spaces;
+			break;
+		case '\b':
+			if (tty->column > 0)
+				tty->column--;
+			break;
+		default:
+			if (O_OLCUC(tty))
+				c = toupper(c);
+			if (!iscntrl(c))
+				tty->column++;
+			break;
+		}
+	}
+	tty->driver.put_char(tty, c);
+	return 0;
+}
+
+static inline void put_char(unsigned char c, struct tty_struct *tty)
+{
+	tty->driver.put_char(tty, c);
+}
+
+/* Must be called only when L_ECHO(tty) is true. */
+
+static void echo_char(unsigned char c, struct tty_struct *tty)
+{
+	if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
+		put_char('^', tty);
+		put_char(c ^ 0100, tty);
+		tty->column += 2;
+	} else
+		opost(c, tty);
+}
+
+static inline void finish_erasing(struct tty_struct *tty)
+{
+	if (tty->erasing) {
+		put_char('/', tty);
+		tty->column += 2;
+		tty->erasing = 0;
+	}
+}
+
+static void eraser(unsigned char c, struct tty_struct *tty)
+{
+	enum { ERASE, WERASE, KILL } kill_type;
+	int head, seen_alnums;
+
+	if (tty->read_head == tty->canon_head) {
+		/* opost('\a', tty); */		/* what do you think? */
+		return;
+	}
+	if (c == ERASE_CHAR(tty))
+		kill_type = ERASE;
+	else if (c == WERASE_CHAR(tty))
+		kill_type = WERASE;
+	else {
+		if (!L_ECHO(tty)) {
+			tty->read_cnt -= ((tty->read_head - tty->canon_head) &
+					  (N_TTY_BUF_SIZE - 1));
+			tty->read_head = tty->canon_head;
+			return;
+		}
+		if (!L_ECHOK(tty) || !L_ECHOKE(tty)) {
+			tty->read_cnt -= ((tty->read_head - tty->canon_head) &
+					  (N_TTY_BUF_SIZE - 1));
+			tty->read_head = tty->canon_head;
+			finish_erasing(tty);
+			echo_char(KILL_CHAR(tty), tty);
+			/* Add a newline if ECHOK is on and ECHOKE is off. */
+			if (L_ECHOK(tty))
+				opost('\n', tty);
+			return;
+		}
+		kill_type = KILL;
+	}
+
+	seen_alnums = 0;
+	while (tty->read_head != tty->canon_head) {
+		head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1);
+		c = tty->read_buf[head];
+		if (kill_type == WERASE) {
+			/* Equivalent to BSD's ALTWERASE. */
+			if (isalnum(c) || c == '_')
+				seen_alnums++;
+			else if (seen_alnums)
+				break;
+		}
+		tty->read_head = head;
+		tty->read_cnt--;
+		if (L_ECHO(tty)) {
+			if (L_ECHOPRT(tty)) {
+				if (!tty->erasing) {
+					put_char('\\', tty);
+					tty->column++;
+					tty->erasing = 1;
+				}
+				echo_char(c, tty);
+			} else if (!L_ECHOE(tty)) {
+				echo_char(ERASE_CHAR(tty), tty);
+			} else if (c == '\t') {
+				unsigned int col = tty->canon_column;
+				unsigned long tail = tty->canon_head;
+
+				/* Find the column of the last char. */
+				while (tail != tty->read_head) {
+					c = tty->read_buf[tail];
+					if (c == '\t')
+						col = (col | 7) + 1;
+					else if (iscntrl(c)) {
+						if (L_ECHOCTL(tty))
+							col += 2;
+					} else
+						col++;
+					tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+				}
+
+				/* Now backup to that column. */
+				while (tty->column > col) {
+					/* Can't use opost here. */
+					put_char('\b', tty);
+					tty->column--;
+				}
+			} else {
+				if (iscntrl(c) && L_ECHOCTL(tty)) {
+					put_char('\b', tty);
+					put_char(' ', tty);
+					put_char('\b', tty);
+					tty->column--;
+				}
+				if (!iscntrl(c) || L_ECHOCTL(tty)) {
+					put_char('\b', tty);
+					put_char(' ', tty);
+					put_char('\b', tty);
+					tty->column--;
+				}
+			}
+		}
+		if (kill_type == ERASE)
+			break;
+	}
+	if (tty->read_head == tty->canon_head)
+		finish_erasing(tty);
+}
+
+static void isig(int sig, struct tty_struct *tty)
+{
+	if (tty->pgrp > 0)
+		kill_pg(tty->pgrp, sig, 1);
+	if (!L_NOFLSH(tty)) {
+		n_tty_flush_buffer(tty);
+		if (tty->driver.flush_buffer)
+			tty->driver.flush_buffer(tty);
+	}
+}
+
+static inline void n_tty_receive_break(struct tty_struct *tty)
+{
+	if (I_IGNBRK(tty))
+		return;
+	if (I_BRKINT(tty)) {
+		isig(SIGINT, tty);
+		return;
+	}
+	if (I_PARMRK(tty)) {
+		put_tty_queue('\377', tty);
+		put_tty_queue('\0', tty);
+	}
+	put_tty_queue('\0', tty);
+	wake_up_interruptible(&tty->read_wait);
+}
+
+static inline void n_tty_receive_overrun(struct tty_struct *tty)
+{
+	char buf[64];
+
+	tty->num_overrun++;
+	if (tty->overrun_time < (jiffies - HZ)) {
+		printk("%s: %d input overrun(s)\n", _tty_name(tty, buf),
+		       tty->num_overrun);
+		tty->overrun_time = jiffies;
+		tty->num_overrun = 0;
+	}
+}
+
+static inline void n_tty_receive_parity_error(struct tty_struct *tty,
+					      unsigned char c)
+{
+	if (I_IGNPAR(tty)) {
+		return;
+	}
+	if (I_PARMRK(tty)) {
+		put_tty_queue('\377', tty);
+		put_tty_queue('\0', tty);
+		put_tty_queue(c, tty);
+	} else
+		put_tty_queue('\0', tty);
+	wake_up_interruptible(&tty->read_wait);
+}
+
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+{
+	if (tty->raw) {
+		put_tty_queue(c, tty);
+		return;
+	}
+	
+	if (tty->stopped && I_IXON(tty) && I_IXANY(tty) && L_IEXTEN(tty)) {
+		start_tty(tty);
+		return;
+	}
+	
+	if (I_ISTRIP(tty))
+		c &= 0x7f;
+	if (I_IUCLC(tty) && L_IEXTEN(tty))
+		c=tolower(c);
+
+	/*
+	 * If the previous character was LNEXT, or we know that this
+	 * character is not one of the characters that we'll have to
+	 * handle specially, do shortcut processing to speed things
+	 * up.
+	 */
+	if (!test_bit(c, &tty->process_char_map) || tty->lnext) {
+		finish_erasing(tty);
+		tty->lnext = 0;
+		if (L_ECHO(tty)) {
+			if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+				put_char('\a', tty); /* beep if no space */
+				return;
+			}
+			/* Record the column of first canon char. */
+			if (tty->canon_head == tty->read_head)
+				tty->canon_column = tty->column;
+			echo_char(c, tty);
+		}
+		if (I_PARMRK(tty) && c == (unsigned char) '\377')
+			put_tty_queue(c, tty);
+		put_tty_queue(c, tty);
+		return;
+	}
+		
+	if (c == '\r') {
+		if (I_IGNCR(tty))
+			return;
+		if (I_ICRNL(tty))
+			c = '\n';
+	} else if (c == '\n' && I_INLCR(tty))
+		c = '\r';
+	if (I_IXON(tty)) {
+		if (c == START_CHAR(tty)) {
+			start_tty(tty);
+			return;
+		}
+		if (c == STOP_CHAR(tty)) {
+			stop_tty(tty);
+			return;
+		}
+	}
+	if (L_ISIG(tty)) {
+		if (c == INTR_CHAR(tty)) {
+			isig(SIGINT, tty);
+			return;
+		}
+		if (c == QUIT_CHAR(tty)) {
+			isig(SIGQUIT, tty);
+			return;
+		}
+		if (c == SUSP_CHAR(tty)) {
+			if (!is_orphaned_pgrp(tty->pgrp))
+				isig(SIGTSTP, tty);
+			return;
+		}
+	}
+	if (L_ICANON(tty)) {
+		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
+		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
+			eraser(c, tty);
+			return;
+		}
+		if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
+			tty->lnext = 1;
+			if (L_ECHO(tty)) {
+				finish_erasing(tty);
+				if (L_ECHOCTL(tty)) {
+					put_char('^', tty);
+					put_char('\b', tty);
+				}
+			}
+			return;
+		}
+		if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
+		    L_IEXTEN(tty)) {
+			unsigned long tail = tty->canon_head;
+
+			finish_erasing(tty);
+			echo_char(c, tty);
+			opost('\n', tty);
+			while (tail != tty->read_head) {
+				echo_char(tty->read_buf[tail], tty);
+				tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+			}
+			return;
+		}
+		if (c == '\n') {
+			if (L_ECHO(tty) || L_ECHONL(tty)) {
+				if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+					put_char('\a', tty);
+					return;
+				}
+				opost('\n', tty);
+			}
+			goto handle_newline;
+		}
+		if (c == EOF_CHAR(tty)) {
+			c = __DISABLED_CHAR;
+			goto handle_newline;
+		}
+		if ((c == EOL_CHAR(tty)) ||
+		    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
+			/*
+			 * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
+			 */
+			if (L_ECHO(tty)) {
+				if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+					put_char('\a', tty);
+					return;
+				}
+				/* Record the column of first canon char. */
+				if (tty->canon_head == tty->read_head)
+					tty->canon_column = tty->column;
+				echo_char(c, tty);
+			}
+			/*
+			 * XXX does PARMRK doubling happen for
+			 * EOL_CHAR and EOL2_CHAR?
+			 */
+			if (I_PARMRK(tty) && c == (unsigned char) '\377')
+				put_tty_queue(c, tty);
+
+		handle_newline:
+			set_bit(tty->read_head, &tty->read_flags);
+			put_tty_queue(c, tty);
+			tty->canon_head = tty->read_head;
+			tty->canon_data++;
+			return;
+		}
+	}
+	
+	finish_erasing(tty);
+	if (L_ECHO(tty)) {
+		if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+			put_char('\a', tty); /* beep if no space */
+			return;
+		}
+		if (c == '\n')
+			opost('\n', tty);
+		else {
+			/* Record the column of first canon char. */
+			if (tty->canon_head == tty->read_head)
+				tty->canon_column = tty->column;
+			echo_char(c, tty);
+		}
+	}
+
+	if (I_PARMRK(tty) && c == (unsigned char) '\377')
+		put_tty_queue(c, tty);
+
+	put_tty_queue(c, tty);
+}	
+
+static void n_tty_receive_buf(struct tty_struct *tty, unsigned char *cp,
+			      char *fp, int count)
+{
+	unsigned char *p;
+	char *f, flags = 0;
+	int	i;
+
+	if (!tty->read_buf)
+		return;
+
+	if (tty->real_raw) {
+		i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
+				   N_TTY_BUF_SIZE - tty->read_head));
+		memcpy(tty->read_buf + tty->read_head, cp, i);
+		tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt += i;
+		cp += i;
+		count -= i;
+
+		i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
+			       N_TTY_BUF_SIZE - tty->read_head));
+		memcpy(tty->read_buf + tty->read_head, cp, i);
+		tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt += i;
+	} else {
+		for (i=count, p = cp, f = fp; i; i--, p++) {
+			if (f)
+				flags = *f++;
+			switch (flags) {
+			case TTY_NORMAL:
+				n_tty_receive_char(tty, *p);
+				break;
+			case TTY_BREAK:
+				n_tty_receive_break(tty);
+				break;
+			case TTY_PARITY:
+			case TTY_FRAME:
+				n_tty_receive_parity_error(tty, *p);
+				break;
+			case TTY_OVERRUN:
+				n_tty_receive_overrun(tty);
+				break;
+			default:
+				printk("%s: unknown flag %d\n", tty_name(tty),
+				       flags);
+				break;
+			}
+		}
+		if (tty->driver.flush_chars)
+			tty->driver.flush_chars(tty);
+	}
+
+	if (tty->icanon ? tty->canon_data :
+	    (tty->read_cnt >= tty->minimum_to_wake)) {
+		if (tty->fasync)
+			kill_fasync(tty->fasync, SIGIO);
+		if (tty->read_wait)
+			wake_up_interruptible(&tty->read_wait);
+	}
+
+	if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) &&
+	    tty->driver.throttle &&
+	    !set_bit(TTY_THROTTLED, &tty->flags))
+		tty->driver.throttle(tty);
+}
+
+static int n_tty_receive_room(struct tty_struct *tty)
+{
+	int	left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+
+	if (left > 0)
+		return left;
+	return 0;
+}
+
+int is_ignored(int sig)
+{
+	return ((current->blocked & (1<<(sig-1))) ||
+	        (current->sigaction[sig-1].sa_handler == SIG_IGN));
+}
+
+static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
+{
+	tty->icanon = (L_ICANON(tty) != 0);
+	if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
+	    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
+	    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
+	    I_PARMRK(tty)) {
+		cli();
+		memset(tty->process_char_map, 0, 256/32);
+
+		if (I_IGNCR(tty) || I_ICRNL(tty))
+			set_bit('\r', &tty->process_char_map);
+		if (I_INLCR(tty))
+			set_bit('\n', &tty->process_char_map);
+
+		if (L_ICANON(tty)) {
+			set_bit(ERASE_CHAR(tty), &tty->process_char_map);
+			set_bit(KILL_CHAR(tty), &tty->process_char_map);
+			set_bit(EOF_CHAR(tty), &tty->process_char_map);
+			set_bit('\n', &tty->process_char_map);
+			set_bit(EOL_CHAR(tty), &tty->process_char_map);
+			if (L_IEXTEN(tty)) {
+				set_bit(WERASE_CHAR(tty),
+					&tty->process_char_map);
+				set_bit(LNEXT_CHAR(tty),
+					&tty->process_char_map);
+				set_bit(EOL2_CHAR(tty),
+					&tty->process_char_map);
+				if (L_ECHO(tty))
+					set_bit(REPRINT_CHAR(tty),
+						&tty->process_char_map);
+			}
+		}
+		if (I_IXON(tty)) {
+			set_bit(START_CHAR(tty), &tty->process_char_map);
+			set_bit(STOP_CHAR(tty), &tty->process_char_map);
+		}
+		if (L_ISIG(tty)) {
+			set_bit(INTR_CHAR(tty), &tty->process_char_map);
+			set_bit(QUIT_CHAR(tty), &tty->process_char_map);
+			set_bit(SUSP_CHAR(tty), &tty->process_char_map);
+		}
+		clear_bit(__DISABLED_CHAR, &tty->process_char_map);
+		sti();
+		tty->raw = 0;
+		tty->real_raw = 0;
+	} else {
+		tty->raw = 1;
+		if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
+		    (I_IGNPAR(tty) || !I_INPCK(tty)) &&
+		    (tty->driver.flags & TTY_DRIVER_REAL_RAW))
+			tty->real_raw = 1;
+		else
+			tty->real_raw = 0;
+	}
+}
+
+static void n_tty_close(struct tty_struct *tty)
+{
+	wait_until_sent(tty, 0);
+	n_tty_flush_buffer(tty);
+	if (tty->read_buf) {
+		free_page((unsigned long) tty->read_buf);
+		tty->read_buf = 0;
+	}
+}
+
+static int n_tty_open(struct tty_struct *tty)
+{
+	if (!tty->read_buf) {
+		tty->read_buf = (unsigned char *)
+			get_free_page(intr_count ? GFP_ATOMIC : GFP_KERNEL);
+		if (!tty->read_buf)
+			return -ENOMEM;
+	}
+	memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
+	tty->read_head = tty->read_tail = tty->read_cnt = 0;
+	memset(tty->read_flags, 0, sizeof(tty->read_flags));
+	n_tty_set_termios(tty, 0);
+	tty->minimum_to_wake = 1;
+	return 0;
+}
+
+static inline int input_available_p(struct tty_struct *tty, int amt)
+{
+	if (L_ICANON(tty)) {
+		if (tty->canon_data)
+			return 1;
+	} else if (tty->read_cnt >= (amt ? amt : 1))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Helper function to speed up read_chan.  It is only called when
+ * ICANON is off; it copies characters straight from the tty queue to
+ * user space directly.  It can be profitably called twice; once to
+ * drain the space from the tail pointer to the (physical) end of the
+ * buffer, and once to drain the space from the (physical) beginning of
+ * the buffer to head pointer.
+ */
+static inline void copy_from_read_buf(struct tty_struct *tty,
+				      unsigned char **b,
+				      unsigned int *nr)
+
+{
+	int	n;
+
+	n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
+	if (!n)
+		return;
+	memcpy_tofs(*b, &tty->read_buf[tty->read_tail], n);
+	tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
+	tty->read_cnt -= n;
+	*b += n;
+	*nr -= n;
+}
+
+/*
+ * Called to gobble up an immediately following EOF when there is no
+ * more room in buf (this can happen if the user "pushes" some
+ * characters using ^D).  This prevents the next read() from falsely
+ * returning EOF.
+ */
+static inline void gobble_eof(struct tty_struct *tty)
+{
+	cli();
+	if ((tty->read_cnt) &&
+	    (tty->read_buf[tty->read_tail] == __DISABLED_CHAR) &&
+	    clear_bit(tty->read_tail, &tty->read_flags)) {
+		tty->read_tail = (tty->read_tail+1) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt--;
+	}
+	sti();
+}
+
+static int read_chan(struct tty_struct *tty, struct file *file,
+		     unsigned char *buf, unsigned int nr)
+{
+	struct wait_queue wait = { current, NULL };
+	int c;
+	unsigned char *b = buf;
+	int minimum, time;
+	int retval = 0;
+
+	if (!tty->read_buf) {
+		printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
+		return -EIO;
+	}
+
+	/* Job control check -- must be done at start and after
+	   every sleep (POSIX.1 7.1.1.4). */
+	/* NOTE: not yet done after every sleep pending a thorough
+	   check of the logic of this change. -- jlc */
+	/* don't stop on /dev/console */
+	if (file->f_inode->i_rdev != CONSOLE_DEV &&
+	    current->tty == tty) {
+		if (tty->pgrp <= 0)
+			printk("read_chan: tty->pgrp <= 0!\n");
+		else if (current->pgrp != tty->pgrp) {
+			if (is_ignored(SIGTTIN) ||
+			    is_orphaned_pgrp(current->pgrp))
+				return -EIO;
+			kill_pg(current->pgrp, SIGTTIN, 1);
+			return -ERESTARTSYS;
+		}
+	}
+
+	if (L_ICANON(tty)) {
+		minimum = time = 0;
+		current->timeout = (unsigned long) -1;
+	} else {
+		time = (HZ / 10) * TIME_CHAR(tty);
+		minimum = MIN_CHAR(tty);
+		if (minimum) {
+		  	current->timeout = (unsigned long) -1;
+			if (time)
+				tty->minimum_to_wake = 1;
+			else if (!tty->read_wait ||
+				 (tty->minimum_to_wake > minimum))
+				tty->minimum_to_wake = minimum;
+		} else {
+			if (time) {
+				current->timeout = time + jiffies;
+				time = 0;
+			} else
+				current->timeout = 0;
+			tty->minimum_to_wake = minimum = 1;
+		}
+	}
+
+	add_wait_queue(&tty->read_wait, &wait);
+	while (1) {
+		/* First test for status change. */
+		if (tty->packet && tty->link->ctrl_status) {
+			if (b != buf)
+				break;
+			put_fs_byte(tty->link->ctrl_status, b++);
+			tty->link->ctrl_status = 0;
+			break;
+		}
+		/* This statement must be first before checking for input
+		   so that any interrupt will set the state back to
+		   TASK_RUNNING. */
+		current->state = TASK_INTERRUPTIBLE;
+		
+		if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
+		    ((minimum - (b - buf)) >= 1))
+			tty->minimum_to_wake = (minimum - (b - buf));
+		
+		if (!input_available_p(tty, 0)) {
+			if (tty->flags & (1 << TTY_SLAVE_CLOSED)) {
+				retval = -EIO;
+				break;
+			}
+			if (tty_hung_up_p(file))
+				break;
+			if (!current->timeout)
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (current->signal & ~current->blocked) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			continue;
+		}
+		current->state = TASK_RUNNING;
+
+		/* Deal with packet mode. */
+		if (tty->packet && b == buf) {
+			put_fs_byte(TIOCPKT_DATA, b++);
+			nr--;
+		}
+
+		if (L_ICANON(tty)) {
+			while (1) {
+				int eol;
+
+				disable_bh(TQUEUE_BH);
+				if (!tty->read_cnt) {
+					enable_bh(TQUEUE_BH);
+					break;
+				}
+				eol = clear_bit(tty->read_tail,
+						&tty->read_flags);
+				c = tty->read_buf[tty->read_tail];
+				tty->read_tail = ((tty->read_tail+1) &
+						  (N_TTY_BUF_SIZE-1));
+				tty->read_cnt--;
+				enable_bh(TQUEUE_BH);
+				if (!eol) {
+					put_fs_byte(c, b++);
+					if (--nr)
+						continue;
+					gobble_eof(tty);
+					break;
+				}
+				if (--tty->canon_data < 0) {
+					tty->canon_data = 0;
+				}
+				if (c != __DISABLED_CHAR) {
+					put_fs_byte(c, b++);
+					nr--;
+				}
+				break;
+			}
+		} else {
+			disable_bh(TQUEUE_BH);
+			copy_from_read_buf(tty, &b, &nr);
+			copy_from_read_buf(tty, &b, &nr);
+			enable_bh(TQUEUE_BH);
+		}
+
+		/* If there is enough space in the read buffer now, let the
+		   low-level driver know. */
+		if (tty->driver.unthrottle &&
+		    (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE)
+		    && clear_bit(TTY_THROTTLED, &tty->flags))
+			tty->driver.unthrottle(tty);
+
+		if (b - buf >= minimum || !nr)
+			break;
+		if (time)
+			current->timeout = time + jiffies;
+	}
+	remove_wait_queue(&tty->read_wait, &wait);
+
+	if (!tty->read_wait)
+		tty->minimum_to_wake = minimum;
+
+	current->state = TASK_RUNNING;
+	current->timeout = 0;
+	/*
+	 * Hack for PTY's; we need to wake up the other tty if there's
+	 * enough space.
+	 */
+	if (tty->link && tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE)
+		wake_up_interruptible(&tty->link->write_wait);
+	return (b - buf) ? b - buf : retval;
+}
+
+static int write_chan(struct tty_struct * tty, struct file * file,
+		      unsigned char * buf, unsigned int nr)
+{
+	struct wait_queue wait = { current, NULL };
+	int c;
+	unsigned char *b = buf;
+	int retval = 0;
+
+	/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
+	if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+	}
+
+	add_wait_queue(&tty->write_wait, &wait);
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
+			retval = -EIO;
+			break;
+		}
+		if (O_OPOST(tty)) {
+			while (nr > 0) {
+				c = get_fs_byte(b);
+				if (opost(c, tty) < 0)
+					break;
+				b++; nr--;
+				if (tty->driver.flush_chars)
+					tty->driver.flush_chars(tty);
+			}
+		} else {
+			c = tty->driver.write(tty, 1, b, nr);
+			b += c;
+			nr -= c;
+		}
+		if (!nr)
+			break;
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&tty->write_wait, &wait);
+	return (b - buf) ? b - buf : retval;
+}
+
+static int normal_select(struct tty_struct * tty, struct inode * inode,
+			 struct file * file, int sel_type, select_table *wait)
+{
+	switch (sel_type) {
+		case SEL_IN:
+			if (input_available_p(tty, MIN_CHAR(tty)))
+				return 1;
+			/* fall through */
+		case SEL_EX:
+			if (tty->packet && tty->link->ctrl_status)
+				return 1;
+			if (tty->flags & (1 << TTY_SLAVE_CLOSED))
+				return 1;
+			if (tty_hung_up_p(file))
+				return 1;
+			if (!tty->read_wait)
+				tty->minimum_to_wake = MIN_CHAR(tty) ?
+					MIN_CHAR(tty) : 1;
+			select_wait(&tty->read_wait, wait);
+			return 0;
+		case SEL_OUT:
+			if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS)
+				return 1;
+			select_wait(&tty->write_wait, wait);
+			return 0;
+	}
+	return 0;
+}
+
+struct tty_ldisc tty_ldisc_N_TTY = {
+	TTY_LDISC_MAGIC,	/* magic */
+	0,			/* num */
+	0,			/* flags */
+	n_tty_open,		/* open */
+	n_tty_close,		/* close */
+	n_tty_flush_buffer,	/* flush_buffer */
+	n_tty_chars_in_buffer,	/* chars_in_buffer */
+	read_chan,		/* read */
+	write_chan,		/* write */
+	n_tty_ioctl,		/* ioctl */
+	n_tty_set_termios,	/* set_termios */
+	normal_select,		/* select */
+	n_tty_receive_buf,	/* receive_buf */
+	n_tty_receive_room,	/* receive_room */
+	0			/* write_wakeup */
+};
+
diff -u --recursive --new-file v1.1.12/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v1.1.12/linux/drivers/char/pty.c	Wed Feb  9 08:57:58 1994
+++ linux/drivers/char/pty.c	Mon May 23 07:55:13 1994
@@ -14,36 +14,59 @@
 
 #include <linux/errno.h>
 #include <linux/sched.h>
+#include <linux/interrupt.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/fcntl.h>
-#include <linux/interrupt.h>
 #include <linux/string.h>
+#include <linux/major.h>
 
+#include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 
+struct pty_struct {
+	int	magic;
+	struct wait_queue * open_wait;
+};
+
+#define PTY_MAGIC 0x5001
+
+#define PTY_BUF_SIZE 1024
+
+static unsigned char tmp_buf[PTY_BUF_SIZE];
+
+struct tty_driver pty_driver, pty_slave_driver;
+static int pty_refcount;
+
+static struct tty_struct *pty_table[NR_PTYS];
+static struct termios *pty_termios[NR_PTYS];
+static struct termios *pty_termios_locked[NR_PTYS];
+static struct tty_struct *ttyp_table[NR_PTYS];
+static struct termios *ttyp_termios[NR_PTYS];
+static struct termios *ttyp_termios_locked[NR_PTYS];
+static struct pty_struct pty_state[NR_PTYS];
+
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 
 static void pty_close(struct tty_struct * tty, struct file * filp)
 {
 	if (!tty)
 		return;
-	if (IS_A_PTY_MASTER(tty->line)) {
+	if (tty->driver.subtype == PTY_TYPE_MASTER) {
 		if (tty->count > 1)
 			printk("master pty_close: count = %d!!\n", tty->count);
 	} else {
 		if (tty->count > 2)
 			return;
 	}
-	wake_up_interruptible(&tty->secondary.proc_list);
-	wake_up_interruptible(&tty->read_q.proc_list);
-	wake_up_interruptible(&tty->write_q.proc_list);
+	wake_up_interruptible(&tty->read_wait);
+	wake_up_interruptible(&tty->write_wait);
 	if (!tty->link)
 		return;
-	wake_up_interruptible(&tty->link->secondary.proc_list);
-	wake_up_interruptible(&tty->link->read_q.proc_list);
-	wake_up_interruptible(&tty->link->write_q.proc_list);
-	if (IS_A_PTY_MASTER(tty->line))
+	wake_up_interruptible(&tty->link->read_wait);
+	wake_up_interruptible(&tty->link->write_wait);
+	if (tty->driver.subtype == PTY_TYPE_MASTER)
 		tty_hangup(tty->link);
 	else {
 		start_tty(tty);
@@ -51,58 +74,137 @@
 	}
 }
 
-static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
+static int pty_write(struct tty_struct * tty, int from_user,
+		       unsigned char *buf, int count)
 {
-	unsigned long count, n;
-	struct tty_queue *fq, *tq;
+	struct tty_struct *to = tty->link;
+	int	c, n;
 
-	if (from->stopped || EMPTY(&from->write_q))
-		return;
-	fq = &from->write_q;
-	tq = &to->read_q;
-	count = MIN(CHARS(fq), LEFT(tq));
-	while (count) {
-		n = MIN(MIN(TTY_BUF_SIZE - fq->tail, TTY_BUF_SIZE - tq->head),
-			count);
-		memcpy(&tq->buf[tq->head], &fq->buf[fq->tail], n);
-		count -= n;
-		fq->tail = (fq->tail + n) & (TTY_BUF_SIZE - 1);
-		tq->head = (tq->head + n) & (TTY_BUF_SIZE - 1);
-	}
-	TTY_READ_FLUSH(to);
-	if (LEFT(fq) > WAKEUP_CHARS)
-		wake_up_interruptible(&fq->proc_list);
-	if (from->write_data_cnt) {
-		set_bit(from->line, &tty_check_write);
-		mark_bh(TTY_BH);
-	}
+	if (!to || tty->stopped)
+		return 0;
+	
+	count = MIN(count, to->ldisc.receive_room(to));
+
+	if (from_user) {
+		for (c = count; c > 0; c -= n) {
+			n = MIN(c, PTY_BUF_SIZE);
+			memcpy_fromfs(tmp_buf, buf, n);
+			to->ldisc.receive_buf(to, tmp_buf, 0, n);
+			buf += n;
+		}
+	} else
+		to->ldisc.receive_buf(to, buf, 0, count);
+	
+	return count;
+}
+
+static int pty_write_room(struct tty_struct *tty)
+{
+	struct tty_struct *to = tty->link;
+
+	if (!to || tty->stopped)
+		return 0;
+
+	return to->ldisc.receive_room(to);
 }
 
-/*
- * This routine gets called when tty_write has put something into
- * the write_queue. It copies the input to the output-queue of its
- * slave.
- */
-static void pty_write(struct tty_struct * tty)
+static int pty_chars_in_buffer(struct tty_struct *tty)
 {
-	if (tty->link)
-		pty_copy(tty,tty->link);
+	struct tty_struct *to = tty->link;
+
+	if (!to)
+		return 0;
+
+	return to->ldisc.chars_in_buffer(to);
+}
+
+static void pty_flush_buffer(struct tty_struct *tty)
+{
+	struct tty_struct *to = tty->link;
+	
+	if (!to)
+		return;
+	
+	if (to->ldisc.flush_buffer)
+		to->ldisc.flush_buffer(to);
+	
+	if (to->packet) {
+		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
+		wake_up_interruptible(&to->read_wait);
+	}
 }
 
 int pty_open(struct tty_struct *tty, struct file * filp)
 {
+	int	line;
+	struct	pty_struct *pty;
+	
 	if (!tty || !tty->link)
 		return -ENODEV;
-	if (IS_A_PTY_SLAVE(tty->line))
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PTYS))
+		return -ENODEV;
+	pty = pty_state + line;
+	tty->driver_data = pty;
+	
+	if (tty->driver.subtype == PTY_TYPE_SLAVE)
 		clear_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
-	tty->write = tty->link->write = pty_write;
-	tty->close = tty->link->close = pty_close;
-	wake_up_interruptible(&tty->read_q.proc_list);
+	wake_up_interruptible(&pty->open_wait);
 	if (filp->f_flags & O_NDELAY)
 		return 0;
 	while (!tty->link->count && !(current->signal & ~current->blocked))
-		interruptible_sleep_on(&tty->link->read_q.proc_list);
+		interruptible_sleep_on(&pty->open_wait);
 	if (!tty->link->count)
 		return -ERESTARTSYS;
 	return 0;
 }
+
+long pty_init(long kmem_start)
+{
+	memset(&pty_state, 0, sizeof(pty_state));
+	memset(&pty_driver, 0, sizeof(struct tty_driver));
+	pty_driver.magic = TTY_DRIVER_MAGIC;
+	pty_driver.name = "pty";
+	pty_driver.major = TTY_MAJOR;
+	pty_driver.minor_start = 128;
+	pty_driver.num = NR_PTYS;
+	pty_driver.type = TTY_DRIVER_TYPE_PTY;
+	pty_driver.subtype = PTY_TYPE_MASTER;
+	pty_driver.init_termios = tty_std_termios;
+	pty_driver.init_termios.c_iflag = 0;
+	pty_driver.init_termios.c_oflag = 0;
+	pty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD;
+	pty_driver.init_termios.c_lflag = 0;
+	pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
+	pty_driver.refcount = &pty_refcount;
+	pty_driver.table = pty_table;
+	pty_driver.termios = pty_termios;
+	pty_driver.termios_locked = pty_termios_locked;
+	pty_driver.other = &pty_slave_driver;
+
+	pty_driver.open = pty_open;
+	pty_driver.close = pty_close;
+	pty_driver.write = pty_write;
+	pty_driver.write_room = pty_write_room;
+	pty_driver.flush_buffer = pty_flush_buffer;
+	pty_driver.chars_in_buffer = pty_chars_in_buffer;
+
+	pty_slave_driver = pty_driver;
+	pty_slave_driver.name = "ttyp";
+	pty_slave_driver.subtype = PTY_TYPE_SLAVE;
+	pty_slave_driver.minor_start = 192;
+	pty_slave_driver.init_termios = tty_std_termios;
+	pty_slave_driver.table = ttyp_table;
+	pty_slave_driver.termios = ttyp_termios;
+	pty_slave_driver.termios_locked = ttyp_termios_locked;
+	pty_slave_driver.other = &pty_driver;
+
+	if (tty_register_driver(&pty_driver))
+		panic("Couldn't register pty driver\n");
+	if (tty_register_driver(&pty_slave_driver))
+		panic("Couldn't register pty slave driver\n");
+	
+	return kmem_start;
+}
+
+	
diff -u --recursive --new-file v1.1.12/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.1.12/linux/drivers/char/serial.c	Mon Apr 25 10:04:32 1994
+++ linux/drivers/char/serial.c	Mon May 23 07:55:13 1994
@@ -21,45 +21,70 @@
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
+#include <linux/interrupt.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/serial.h>
-#include <linux/interrupt.h>
+#include <linux/serial_reg.h>
 #include <linux/config.h>
 #include <linux/major.h>
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
+#include <linux/major.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
 
-#undef ISR_HACK
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ *
+ * CONFIG_HUB6
+ *		Enables support for the venerable Bell Technologies
+ *		HUB6 card.
+ *
+ * SERIAL_PARANOIA_CHECK
+ * 		Check the magic number for the async_structure where
+ * 		ever possible.
+ */
+
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+#define CONFIG_SERIAL_NEW_ISR
 
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+
+#define _INLINE_ inline
+  
 /*
- * rs_event		- Bitfield of serial lines that events pending
- * 				to be processed at the next clock tick.
  * IRQ_timeout		- How long the timeout should be for each IRQ
  * 				should be after the IRQ has been active.
- * IRQ_timer		- Array of timeout values for each interrupt IRQ.
- * 				This is based on jiffies; not offsets.
- * 
- * We assume here that int's are 32 bits, so an array of two gives us
- * 64 lines, which is the maximum we can support.
  */
-static int rs_event[2];
 
 static struct async_struct *IRQ_ports[16];
-static int IRQ_active;
-static unsigned long IRQ_timer[16];
 static int IRQ_timeout[16];
 static volatile int rs_irq_triggered;
 static volatile int rs_triggered;
 static int rs_wild_int_mask;
 
 static void autoconfig(struct async_struct * info);
-static void change_speed(unsigned int line);
+static void change_speed(struct async_struct *info);
 	
 /*
  * This assumes you have a 1.8432 MHz clock for your UART.
@@ -70,39 +95,14 @@
  */
 #define BASE_BAUD ( 1843200 / 16 )
 
-#ifdef CONFIG_AUTO_IRQ
-#define AUTO_IRQ_FLAG ASYNC_AUTO_IRQ
-#else
-#define AUTO_IRQ_FLAG 0
-#endif
-
 /* Standard COM flags (except for COM4, because of the 8514 problem) */
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | AUTO_IRQ_FLAG)
-#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | AUTO_IRQ_FLAG)
-
-#ifdef CONFIG_AST_FOURPORT
-#define FOURPORT_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_FOURPORT | AUTO_IRQ_FLAG)
-#else
-#define FOURPORT_FLAGS (ASYNC_FOURPORT | AUTO_IRQ_FLAG)
-#endif
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
+#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
 
-#ifdef CONFIG_ACCENT_ASYNC
-#define ACCENT_FLAGS (ASYNC_BOOT_AUTOCONF | AUTO_IRQ_FLAG)
-#else
-#define ACCENT_FLAGS AUTO_IRQ_FLAG
-#endif
-
-#ifdef CONFIG_BOCA
-#define BOCA_FLAGS (ASYNC_BOOT_AUTOCONF | AUTO_IRQ_FLAG)
-#else
-#define BOCA_FLAGS AUTO_IRQ_FLAG
-#endif
-
-#ifdef CONFIG_HUB6
-#define HUB6_FLAGS (ASYNC_BOOT_AUTOCONF)
-#else
+#define FOURPORT_FLAGS ASYNC_FOURPORT
+#define ACCENT_FLAGS 0
+#define BOCA_FLAGS 0
 #define HUB6_FLAGS 0
-#endif
 	
 /*
  * The following define the access methods for the HUB6 card. All
@@ -122,62 +122,91 @@
 
 struct async_struct rs_table[] = {
 	/* UART CLK   PORT IRQ     FLAGS        */
-	{ BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },		/* ttyS0 */
-	{ BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },		/* ttyS1 */
-	{ BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },		/* ttyS2 */
-	{ BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
-
-	{ BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */
-	{ BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */
-	{ BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */
-	{ BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */
-
-	{ BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */
-	{ BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */
-	{ BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */
-	{ BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */
-	
-	{ BASE_BAUD, 0x330, 4, ACCENT_FLAGS },		/* ttyS12 */
-	{ BASE_BAUD, 0x338, 4, ACCENT_FLAGS },		/* ttyS13 */
-	{ BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare; user configurable) */
-	{ BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare; user configurable) */
-
-	{ BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */
-	{ BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */
-	{ BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */
-	{ BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */
-	{ BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */
-	{ BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */
-	{ BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */
-	{ BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */
-	{ BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */
-	{ BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */
-	{ BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */
-	{ BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */
-	{ BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */
-	{ BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */
-	{ BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */
-	{ BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
+	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */
+	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */
+	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */
+	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
+
+	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */
+	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */
+	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */
+	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */
+
+	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */
+	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */
+	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */
+	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */
+	
+	{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */
+	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare; user configurable) */
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare; user configurable) */
+
+	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */
+	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */
+	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */
+	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */
+	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */
+	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */
+	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */
+	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */
+	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */
+	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */
+	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */
+	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */
+	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */
+	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */
+	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */
+	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
 
 /* You can have up to four HUB6's in the system, but I've only
  * included two cards here for a total of twelve ports.
  */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS32 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS33 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS34 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS35 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS36 */
-	{ BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS37 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS32 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS33 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS34 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS35 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS36 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS37 */
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
 
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+static inline int serial_paranoia_check(struct async_struct *info,
+					dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%d, %d) in %s\n";
+	static const char *badinfo =
+		"Warning: null async_struct for (%d, %d) in %s\n";
+
+	if (!info) {
+		printk(badinfo, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
 /*
  * This is used to figure out the divsor speeds and the timeouts
  */
@@ -185,43 +214,57 @@
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
 	9600, 19200, 38400, 57600, 115200, 0 };
 
-static void rs_throttle(struct tty_struct * tty, int status);
-
 static inline unsigned int serial_in(struct async_struct *info, int offset)
 {
+#ifdef CONFIG_HUB6
     if (info->hub6) {
 	outb(info->hub6 - 1 + offset, info->port);
 	return inb(info->port+1);
     } else
+#endif
 	return inb(info->port + offset);
 }
 
 static inline unsigned int serial_inp(struct async_struct *info, int offset)
 {
+#ifdef CONFIG_HUB6
     if (info->hub6) {
 	outb(info->hub6 - 1 + offset, info->port);
 	return inb_p(info->port+1);
     } else
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	return inb(info->port + offset);
+#else
 	return inb_p(info->port + offset);
+#endif
 }
 
 static inline void serial_out(struct async_struct *info, int offset, int value)
 {
+#ifdef CONFIG_HUB6
     if (info->hub6) {
 	outb(info->hub6 - 1 + offset, info->port);
 	outb(value, info->port+1);
     } else
+#endif
 	outb(value, info->port+offset);
 }
 
 static inline void serial_outp(struct async_struct *info, int offset,
 			       int value)
 {
+#ifdef CONFIG_HUB6
     if (info->hub6) {
 	outb(info->hub6 - 1 + offset, info->port);
 	outb_p(value, info->port+1);
     } else
-	outb_p(value, info->port+offset);
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	outb(value, info->port+offset);
+#else
+    	outb_p(value, info->port+offset);
+#endif
 }
 
 /*
@@ -234,33 +277,41 @@
  */
 static void rs_stop(struct tty_struct *tty)
 {
-	struct async_struct *info;
-	
-	info = rs_table + DEV_TO_SL(tty->line);
+	struct async_struct *info = tty->driver_data;
+	unsigned long flags;
 
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+	
 	if (info->flags & ASYNC_CLOSING) {
 		tty->stopped = 0;
 		tty->hw_stopped = 0;
 		return;
 	}
 
-	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
-#ifdef ISR_HACK
-	serial_out(info, UART_IER, info->IER);
-#endif
+	save_flags(flags); cli();
+	if (info->IER & UART_IER_THRI) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+	restore_flags(flags);
 }
 
 static void rs_start(struct tty_struct *tty)
 {
-	struct async_struct *info;
+	struct async_struct *info = tty->driver_data;
+	unsigned long flags;
 	
-	info = rs_table + DEV_TO_SL(tty->line);
+	if (serial_paranoia_check(info, tty->device, "rs_start"))
+		return;
 	
-	info->IER = (UART_IER_MSI | UART_IER_RLSI |
-		     UART_IER_THRI | UART_IER_RDI);
-#ifdef ISR_HACK
-	serial_out(info, UART_IER, info->IER);
-#endif
+	save_flags(flags); cli();
+	if (info->xmit_cnt && !info->xmit_buf &&
+	    !(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+	restore_flags(flags);
 }
 
 /*
@@ -299,115 +350,107 @@
  * This routine is used by the interrupt handler to schedule
  * processing in the software interrupt portion of the driver.
  */
-static inline void rs_sched_event(struct async_struct *info,
+static _INLINE_ void rs_sched_event(struct async_struct *info,
 				  int event)
 {
 	info->event |= 1 << event;
-	set_bit(info->line, rs_event);
+	queue_task_irq_off(&info->tqueue, &tq_serial);
 	mark_bh(SERIAL_BH);
 }
 
-static inline void receive_chars(struct async_struct *info,
+static _INLINE_ void receive_chars(struct async_struct *info,
 				 int *status)
 {
-	struct tty_queue * queue;
-	int head, tail, ch;
+	struct tty_struct *tty = info->tty;
+	unsigned char ch;
 
-/*
- * Just like the LEFT(x) macro, except it uses the loal tail
- * and head variables.
- */
-#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
-
-	queue = &info->tty->read_q;
-	head = queue->head;
-	tail = queue->tail;
 	do {
 		ch = serial_inp(info, UART_RX);
-		/*
-		 * There must be at least 2 characters
-		 * free in the queue; otherwise we punt.
-		 */
-		if (VLEFT < 2)
+		if (*status & info->ignore_status_mask)
+			continue;
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 			break;
+		tty->flip.count++;
 		if (*status & info->read_status_mask) {
-			set_bit(head, &info->tty->readq_flags);
 			if (*status & (UART_LSR_BI)) {
-				queue->buf[head++]= TTY_BREAK;
-				rs_sched_event(info, RS_EVENT_BREAK);
+				*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+				if (info->flags & ASYNC_SAK)
+					do_SAK(tty);
 			} else if (*status & UART_LSR_PE)
-				queue->buf[head++]= TTY_PARITY;
+				*tty->flip.flag_buf_ptr++ = TTY_PARITY;
 			else if (*status & UART_LSR_FE)
-				queue->buf[head++]= TTY_FRAME;
-			else if (*status & UART_LSR_OE)
-				queue->buf[head++]= TTY_OVERRUN;
-			head &= TTY_BUF_SIZE-1;
-		}
-		queue->buf[head++] = ch;
-		head &= TTY_BUF_SIZE-1;
+				*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+			else if (*status & UART_LSR_OE) 
+				*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+			else
+				*tty->flip.flag_buf_ptr++ = 0;
+		} else
+			*tty->flip.flag_buf_ptr++ = 0;
+		*tty->flip.char_buf_ptr++ = ch;
 	} while ((*status = serial_inp(info, UART_LSR)) & UART_LSR_DR);
-	queue->head = head;
-	if ((VLEFT < RQ_THRESHOLD_LW) && !set_bit(TTY_RQ_THROTTLED,
-						  &info->tty->flags)) 
-		rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL);
-	rs_sched_event(info, RS_EVENT_READ_PROCESS);
+	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
 #ifdef SERIAL_DEBUG_INTR
 	printk("DR...");
 #endif
 }
 
-static inline void transmit_chars(struct async_struct *info, int *done_work)
+static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
 {
-	struct tty_queue * queue;
-	int head, tail, count;
+	int count;
 	
-	queue = &info->tty->write_q;
-	head = queue->head;
-	tail = queue->tail;
-	if (head==tail && !info->x_char) {
-		info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
-#ifdef ISR_HACK
-		serial_out(info, UART_IER, info->IER);
-#endif
-		return;
-	}
-	count = info->xmit_fifo_size;
 	if (info->x_char) {
 		serial_outp(info, UART_TX, info->x_char);
 		info->x_char = 0;
-		count--;
+		if (intr_done)
+			*intr_done = 0;
+		return;
 	}
-	while (count-- && (tail != head)) {
-		serial_outp(info, UART_TX, queue->buf[tail++]);
-		tail &= TTY_BUF_SIZE-1;
+	if (!info->xmit_cnt || info->tty->stopped || info->tty->hw_stopped) {
+		info->IER &= ~UART_IER_THRI;
+#ifdef CONFIG_SERIAL_NEW_ISR
+		serial_out(info, UART_IER, info->IER);
+#endif
+		return;
 	}
-	queue->tail = tail;
-	if (VLEFT > WAKEUP_CHARS) {
+	
+	count = info->xmit_fifo_size;
+	do {
+		serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
+		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		if (--info->xmit_cnt == 0)
+			break;
+	} while (--count > 0);
+	
+	if (info->xmit_cnt < WAKEUP_CHARS)
 		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-		if (info->tty->write_data_cnt) {
-			set_bit(info->tty->line, &tty_check_write);
-			mark_bh(TTY_BH);
-		}
-	}
+
 #ifdef SERIAL_DEBUG_INTR
 	printk("THRE...");
 #endif
-	(*done_work)++;
+	if (intr_done)
+		*intr_done = 0;
+
+	if (info->xmit_cnt == 0) {
+		info->IER &= ~UART_IER_THRI;
+#ifdef CONFIG_SERIAL_NEW_ISR
+		serial_out(info, UART_IER, info->IER);
+#endif
+	}
 }
 
-static inline int check_modem_status(struct async_struct *info)
+static _INLINE_ void check_modem_status(struct async_struct *info)
 {
 	int	status;
 	
 	status = serial_in(info, UART_MSR);
-		
-	if ((status & UART_MSR_DDCD) && !C_CLOCAL(info->tty)) {
+
+	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
 #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
 		printk("ttys%d CD now %s...", info->line,
 		       (status & UART_MSR_DCD) ? "on" : "off");
 #endif		
 		if (status & UART_MSR_DCD)
-			rs_sched_event(info, RS_EVENT_OPEN_WAKEUP);
+			wake_up_interruptible(&info->open_wait);
 		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
 			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
 #ifdef SERIAL_DEBUG_OPEN
@@ -416,15 +459,19 @@
 			rs_sched_event(info, RS_EVENT_HANGUP);
 		}
 	}
-	if (C_CRTSCTS(info->tty) && !(info->flags & ASYNC_CLOSING)) {
+	if (info->flags & ASYNC_CTS_FLOW) {
 		if (info->tty->hw_stopped) {
 			if (status & UART_MSR_CTS) {
 #ifdef SERIAL_DEBUG_INTR
 				printk("CTS tx start...");
 #endif
 				info->tty->hw_stopped = 0;
-				rs_start(info->tty);
-				return 1;
+				info->IER |= UART_IER_THRI;
+#ifdef CONFIG_SERIAL_NEW_ISR
+				serial_out(info, UART_IER, info->IER);
+#endif
+				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
 			}
 		} else {
 			if (!(status & UART_MSR_CTS)) {
@@ -432,30 +479,91 @@
 				printk("CTS tx stop...");
 #endif
 				info->tty->hw_stopped = 1;
-				rs_stop(info->tty);
+				info->IER &= ~UART_IER_THRI;
+#ifdef CONFIG_SERIAL_NEW_ISR
+				serial_out(info, UART_IER, info->IER);
+#endif
 			}
 		}
 	}
-	return 0;
 }
 
-static inline void figure_RS_timer(void)
+#ifdef CONFIG_SERIAL_NEW_ISR
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void rs_interrupt(int irq)
 {
-	int	timeout = jiffies + 60*HZ; /* 60 seconds; really big :-) */
-	int	i, mask;
-	
-	if (!IRQ_active)
+	int status;
+	struct async_struct * info;
+	int pass_counter = 0;
+	struct async_struct *end_mark = 0;
+
+	info = IRQ_ports[irq];
+	if (!info)
 		return;
-	for (i=0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
-		if (!(mask & IRQ_active))
+	
+	do {
+		if (!info->tty ||
+		    (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
+			if (!end_mark)
+				end_mark = info;
+			goto next;
+		}
+		end_mark = 0;
+
+		status = serial_inp(info, UART_LSR);
+		if (status & UART_LSR_DR)
+			receive_chars(info, &status);
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, 0);
+
+	next:
+		info = info->next_port;
+		if (!info) {
+			info = IRQ_ports[irq];
+			if (pass_counter++ > 64) {
+#if 0
+				printk("rs loop break\n");
+#endif
+				break; 	/* Prevent infinite loops */
+			}
 			continue;
-		if (IRQ_timer[i] < timeout)
-			timeout = IRQ_timer[i];
-	}
-	timer_table[RS_TIMER].expires = timeout;
-	timer_active |= 1 << RS_TIMER;
+		}
+	} while (end_mark != info);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq)
+{
+	int status;
+	int pass_counter = 0;
+	struct async_struct * info;
+	
+	info = IRQ_ports[irq];
+	if (!info || !info->tty)
+		return;
+
+	do {
+		status = serial_inp(info, UART_LSR);
+		if (status & UART_LSR_DR)
+			receive_chars(info, &status);
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, 0);
+		if (pass_counter++ > 64) {
+#if 0
+			printk("rs_single loop break.\n");
+#endif
+			break;
+		}
+	} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
 }
 
+#else /* CONFIG_SERIAL_NEW_ISR */
 
 /*
  * This is the serial driver's generic interrupt routine
@@ -464,94 +572,84 @@
 {
 	int status;
 	struct async_struct * info;
-	int done, done_work, pass_number, recheck_count;
+	int done = 1, pass_counter = 0;
 
-	rs_irq_triggered = irq;
-	rs_triggered |= 1 << irq;
-	
 	info = IRQ_ports[irq];
-	done = 1;
-	done_work = 0;
-	pass_number = 0;
-	while (info) {
-		if (info->tty &&
-		    info->tty->termios &&
-		    (!pass_number ||
-		     !(serial_inp(info, UART_IIR) & UART_IIR_NO_INT))) {
+	if (!info)
+		return;
+	
+	while (1) {
+		if (!info->tty)
+			goto next;
+
+		serial_outp(info, UART_IER, 0);
+		status = serial_inp(info, UART_LSR);
+		if (status & UART_LSR_DR) {
+			receive_chars(info, &status);
 			done = 0;
-			status = serial_inp(info, UART_LSR);
-			if (status & UART_LSR_DR) {
-				receive_chars(info, &status);
-				done_work++;
-			}
-		recheck_count = 0;
-		recheck_write:
-			if (status & UART_LSR_THRE) {
-				wake_up_interruptible(&info->xmit_wait);
-				if (!info->tty->stopped &&
-				    !info->tty->hw_stopped)
-					transmit_chars(info, &done_work);
-			}
-			if (check_modem_status(info) &&
-			    (recheck_count++ <= 64))
-				goto recheck_write;
-#ifdef SERIAL_DEBUG_INTR
-			if (recheck_count > 16)
-				printk("recheck_count = %d\n", recheck_count);
-#endif
 		}
-#ifdef ISR_HACK
-		serial_outp(info, UART_IER, 0);
-		serial_out(info, UART_IER, info->IER);
-#endif
-		
-		info = info->next_port;
-		if (!info && !done) {
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, &done);
+
+	next:
+		info = info->next_port;		
+		if (!info) {
 			info = IRQ_ports[irq];
+			if (done)
+				break;
 			done = 1;
-			if (pass_number++ > 64)
-				break; 		/* Prevent infinite loops */
+			if (pass_counter++ > 64) {
+#if 0
+				printk("rs loop break\n");
+#endif
+				break; 	/* Prevent infinite loops */
+			}
 		}
 	}
-	if ((info = IRQ_ports[irq]) != NULL) {
-#ifdef 0
-		do {
-			serial_outp(info, UART_IER, 0);
-			serial_out(info, UART_IER, info->IER);
-			info = info->next_port;
-		} while (info);
-#endif			
-		if (irq && !done_work)
-			IRQ_timer[irq] = jiffies + 1500;
-		else
-			IRQ_timer[irq] = jiffies + IRQ_timeout[irq];
-		IRQ_active |= 1 << irq;
-	}
-	figure_RS_timer();
+
+	/*
+	 * Reset the IER registers; info is already set up from the
+	 * above while loop.
+	 */
+	do
+		serial_outp(info, UART_IER, info->IER);
+	while ((info = info->next_port) != NULL);
 }
 
 /*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
+ * This is the serial driver's interrupt routine for a single port
  */
+static void rs_interrupt_single(int irq)
+{
+	int status;
+	struct async_struct * info;
+
+	info = IRQ_ports[irq];
+	if (!info || !info->tty)
+		return;
+
+	serial_outp(info, UART_IER, 0);
+	status = serial_inp(info, UART_LSR);
+	if (status & UART_LSR_DR)
+		receive_chars(info, &status);
+	check_modem_status(info);
+	if (status & UART_LSR_THRE)
+		transmit_chars(info, 0);
+
+	/*
+	 * Reset the IER register
+	 */
+	serial_outp(info, UART_IER, info->IER);
+}
 
+#endif /* CONFIG_SERIAL_NEW_ISR */
+
 /*
- * This routine is called when we receive a break on a serial line.
- * It is executed out of the software interrupt routine.
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
  */
-static inline void handle_rs_break(struct async_struct *info)
-{
-	if (info->flags & ASYNC_SAK)
-		do_SAK(info->tty);
-		
-	if (!I_IGNBRK(info->tty) && I_BRKINT(info->tty)) {
-		flush_input(info->tty);
-		flush_output(info->tty);
-		if (info->tty->pgrp > 0)
-			kill_pg(info->tty->pgrp, SIGINT,1);
-	}
-}
 
 /*
  * This routine is used to handle the "bottom half" processing for the
@@ -562,65 +660,49 @@
  * interrupt driver proper are done; the interrupt driver schedules
  * them using rs_sched_event(), and they get done here.
  */
-static void do_softint(void *unused)
+static void do_serial_bh(void *unused)
 {
-	int			i;
-	struct async_struct	*info;
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private)
+{
+	struct async_struct	*info = (struct async_struct *) private;
+	struct tty_struct	*tty;
 	
-	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
-		if (clear_bit(i, rs_event)) {
-			if (!info->tty)	
-				continue;
-			if (clear_bit(RS_EVENT_READ_PROCESS, &info->event)) {
-				TTY_READ_FLUSH(info->tty);
-			}
-			if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
-				wake_up_interruptible(&info->tty->write_q.proc_list);
-			}
-			if (clear_bit(RS_EVENT_HANGUP, &info->event)) {
-				tty_hangup(info->tty);
-				wake_up_interruptible(&info->open_wait);
-				info->flags &= ~(ASYNC_NORMAL_ACTIVE|
-						 ASYNC_CALLOUT_ACTIVE);
-			}
-			if (clear_bit(RS_EVENT_BREAK, &info->event))
-				handle_rs_break(info);
-			if (clear_bit(RS_EVENT_OPEN_WAKEUP, &info->event)) {
-				wake_up_interruptible(&info->open_wait);
-			}
-		}
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (clear_bit(RS_EVENT_HANGUP, &info->event)) {
+		tty_hangup(tty);
+		wake_up_interruptible(&info->open_wait);
+		info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+				 ASYNC_CALLOUT_ACTIVE);
 	}
+	if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
 }
 
 /*
  * This subroutine is called when the RS_TIMER goes off.  It is used
- * by the serial driver to run the rs_interrupt routine at certain
- * intervals, either because a serial interrupt might have been lost,
- * or because (in the case of IRQ=0) the serial port does not have an
- * interrupt, and is being checked only via the timer interrupts.
+ * by the serial driver to handle ports that do not have an interrupt
+ * (irq=0).  This doesn't work very well for 16450's, but gives bearly
+ * passable results for a 16550A.  (Although at the expense of much
+ * CPU overhead).
  */
 static void rs_timer(void)
 {
-	int	i, mask;
-	int	timeout = 0;
+	if (IRQ_ports[0]) {
+		cli();
+		rs_interrupt(0);
+		sti();
 
-	for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
-		if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) {
-			IRQ_active &= ~mask;
-			cli();
-#ifdef SERIAL_DEBUG_TIMER
-			printk("rs_timer: rs_interrupt(%d)...", i);
-#endif
-			rs_interrupt(i);
-			sti();
-		}
-		if (mask & IRQ_active) {
-			if (!timeout || (IRQ_timer[i] < timeout))
-				timeout = IRQ_timer[i];
-		}
-	}
-	if (timeout) {
-		timer_table[RS_TIMER].expires = timeout;
+		timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
 		timer_active |= 1 << RS_TIMER;
 	}
 }
@@ -697,12 +779,12 @@
 	IRQ_timeout[irq] = timeout ? timeout : 1;
 }
 
-static int startup(struct async_struct * info, int get_irq)
+static int startup(struct async_struct * info)
 {
 	unsigned short ICP;
 	unsigned long flags;
-	struct sigaction	sa;
 	int			retval;
+	struct sigaction	sa;
 
 	if (info->flags & ASYNC_INITIALIZED)
 		return 0;
@@ -713,6 +795,12 @@
 		return 0;
 	}
 
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+		if (!info->xmit_buf)
+			return ENOMEM;
+	}
+
 	save_flags(flags); cli();
 
 #ifdef SERIAL_DEBUG_OPEN
@@ -720,10 +808,37 @@
 #endif
 
 	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	if (info->type == PORT_16550A) {
+		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+					     UART_FCR_CLEAR_XMIT));
+		info->xmit_fifo_size = 16;
+	} else
+		info->xmit_fifo_size = 1;
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (serial_inp(info, UART_LSR) == 0xff) {
+		restore_flags(flags);
+		return -ENODEV;
+	}
+	
+	/*
 	 * Allocate the IRQ if necessary
 	 */
-	if (get_irq && info->irq && !IRQ_ports[info->irq]) {
-		sa.sa_handler = rs_interrupt;
+	if (info->irq && (!IRQ_ports[info->irq] ||
+			  !IRQ_ports[info->irq]->next_port)) {
+		if (IRQ_ports[info->irq]) {
+			free_irq(info->irq);
+			sa.sa_handler = rs_interrupt;
+		} else 
+			sa.sa_handler = rs_interrupt_single;
+
 		sa.sa_flags = (SA_INTERRUPT);
 		sa.sa_mask = 0;
 		sa.sa_restorer = NULL;
@@ -735,45 +850,32 @@
 	}
 
 	/*
-	 * Clear the FIFO buffers and disable them
-	 * (they will be reenabled in change_speed())
-	 */
-	if (info->type == PORT_16550A) {
-		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
-					     UART_FCR_CLEAR_XMIT));
-		info->xmit_fifo_size = 16;
-	} else
-		info->xmit_fifo_size = 1;
-
-	/*
 	 * Clear the interrupt registers.
 	 */
-	(void)serial_inp(info, UART_LSR);
-	(void)serial_inp(info, UART_RX);
-	(void)serial_inp(info, UART_IIR);
-	(void)serial_inp(info, UART_MSR);
+     /* (void) serial_inp(info, UART_LSR); */   /* (see above) */
+	(void) serial_inp(info, UART_RX);
+	(void) serial_inp(info, UART_IIR);
+	(void) serial_inp(info, UART_MSR);
 
 	/*
 	 * Now, initialize the UART 
 	 */
 	serial_outp(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
-	if (info->flags & ASYNC_FOURPORT) 
-		serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
-	else
-		serial_outp(info, UART_MCR,
-			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+	if (info->flags & ASYNC_FOURPORT) {
+		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+		info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
+	} else {
+		info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+		info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
+	}
+	serial_outp(info, UART_MCR, info->MCR);
 	
 	/*
 	 * Finally, enable interrupts
 	 */
-#ifdef ISR_HACK
 	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
 	serial_outp(info, UART_IER, info->IER);	/* enable interrupts */
-#else
-	info->IER = (UART_IER_MSI | UART_IER_RLSI |
-		     UART_IER_THRI | UART_IER_RDI);
-	serial_outp(info, UART_IER, info->IER);	/* enable all intrs */
-#endif
+	
 	if (info->flags & ASYNC_FOURPORT) {
 		/* Enable interrupts on the AST Fourport board */
 		ICP = (info->port & 0xFE0) | 0x01F;
@@ -791,15 +893,7 @@
 
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
-	/*
-	 * Set up parity check flag
-	 */
-	if (info->tty && info->tty->termios && I_INPCK(info->tty))
-		info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
-					  UART_LSR_FE | UART_LSR_PE);
-	else
-		info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
-					  UART_LSR_FE);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
 
 	/*
 	 * Insert serial port into IRQ chain.
@@ -814,13 +908,15 @@
 	/*
 	 * Set up serial timers...
 	 */
-	IRQ_active |= 1 << info->irq;
-	figure_RS_timer();
+	if (info->irq == 0) {
+		timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0];
+		timer_active |= 1 << RS_TIMER;
+	}
 
 	/*
 	 * and set the speed of the serial port
 	 */
-	change_speed(info->line);
+	change_speed(info);
 
 	info->flags |= ASYNC_INITIALIZED;
 	restore_flags(flags);
@@ -831,9 +927,12 @@
  * This routine will shutdown a serial port; interrupts are disabled, and
  * DTR is dropped if the hangup on close termio flag is on.
  */
-static void shutdown(struct async_struct * info, int do_free_irq)
+static void shutdown(struct async_struct * info)
 {
-	unsigned long flags;
+	struct sigaction	sa;
+	unsigned long	flags;
+	unsigned long timeout;
+	int		retval;
 
 	if (!(info->flags & ASYNC_INITIALIZED))
 		return;
@@ -859,20 +958,58 @@
 	/*
 	 * Free the IRQ, if necessary
 	 */
-	if (do_free_irq && info->irq && !IRQ_ports[info->irq])
-		free_irq(info->irq);
-	
+	if (info->irq && (!IRQ_ports[info->irq] ||
+			  !IRQ_ports[info->irq]->next_port)) {
+		if (IRQ_ports[info->irq]) {
+			free_irq(info->irq);
+			sa.sa_flags = (SA_INTERRUPT);
+			sa.sa_mask = 0;
+			sa.sa_restorer = NULL;
+			sa.sa_handler = rs_interrupt_single;
+			retval = irqaction(info->irq, &sa);
+			
+			if (retval)
+				printk("serial shutdown: irqaction: error %d"
+				       "  Couldn't reacquire IRQ.\n", retval);
+		} else
+			free_irq(info->irq);
+	}
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+			
 	info->IER = 0;
 	serial_outp(info, UART_IER, 0x00);	/* disable all intrs */
 	if (info->flags & ASYNC_FOURPORT) {
 		/* reset interrupts on the AST Fourport board */
 		(void) inb((info->port & 0xFE0) | 0x01F);
 	}
-	if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
-		serial_outp(info, UART_MCR, UART_MCR_DTR);
-	else
-		/* reset DTR,RTS,OUT_2 */		
-		serial_outp(info, UART_MCR, 0x00);
+	
+	/*
+	 * Bebore we drop DTR, make sure the UART transmitter has
+	 * completely drained; this is especially important if there
+	 * is a transmit FIFO!
+	 * 
+	 * We busy loop here, which is not great; unfortunately the
+	 * UART does not provide an interrupt for TEMT, and putting it
+	 * in the interrupt handler would slow down normal accesses
+	 * anyway.
+	 */
+	sti();
+	timeout = jiffies + info->timeout;
+	while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
+		if (jiffies > timeout)
+			break;
+	}
+	cli();
+	
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->MCR &= ~UART_MCR_DTR;
+		info->MCR_noint &= ~UART_MCR_DTR;
+	}
+	serial_outp(info, UART_MCR, info->MCR_noint);
 
 	/* disable FIFO's */	
 	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
@@ -890,17 +1027,13 @@
  * This routine is called to set the UART divisor registers to match
  * the specified baud rate for a serial port.
  */
-static void change_speed(unsigned int line)
+static void change_speed(struct async_struct *info)
 {
-	struct async_struct * info;
 	unsigned short port;
 	int	quot = 0;
-	unsigned cflag,cval,mcr,fcr;
+	unsigned cflag,cval,fcr;
 	int	i;
 
-	if (line >= NR_PORTS)
-		return;
-	info = rs_table + line;
 	if (!info->tty || !info->tty->termios)
 		return;
 	cflag = info->tty->termios->c_cflag;
@@ -928,16 +1061,20 @@
 		quot = 0;
 		info->timeout = 0;
 	}
-	cli();
-	mcr = serial_in(info, UART_MCR);
 	if (quot) {
-		serial_out(info, UART_MCR, mcr | UART_MCR_DTR);
+		info->MCR |= UART_MCR_DTR;
+		info->MCR_noint |= UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
 	} else {
-		serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR);
+		info->MCR &= ~UART_MCR_DTR;
+		info->MCR_noint &= ~UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
 		sti();
 		return;
 	}
-	sti();
 	/* byte size and parity */
 	cval = cflag & (CSIZE | CSTOPB);
 	cval >>= 4;
@@ -953,6 +1090,38 @@
 	} else
 		fcr = 0;
 	
+	/* CTS flow control flag */
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	/*
+	 * Set up parity check flag
+	 */
+	info->read_status_mask = UART_LSR_OE;
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+		info->read_status_mask |= UART_LSR_BI;
+	
+	info->ignore_status_mask = 0;
+	if (I_IGNPAR(info->tty))
+		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignore parity and break indicators, ignore 
+		 * overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty))
+			info->ignore_status_mask |= UART_LSR_OE;
+	}
+	
 	cli();
 	serial_outp(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
 	serial_outp(info, UART_DLL, quot & 0xff);	/* LS of divisor */
@@ -962,70 +1131,115 @@
 	sti();
 }
 
-/*
- * ------------------------------------------------------------
- * rs_write() and friends
- * ------------------------------------------------------------
- */
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct async_struct *info = tty->driver_data;
 
-/*
- * This routine is used by rs_write to restart transmitter interrupts,
- * which are disabled after we have a transmitter interrupt which went
- * unacknowledged because we had run out of data to transmit.
- * 
- * Note: this subroutine must be called with the interrupts *off*
- */
-static inline void restart_port(struct async_struct *info)
+	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+		return;
+
+	if (!tty || tty->stopped || tty->hw_stopped || !info->xmit_buf)
+		return;
+
+	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
+		return;
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= SERIAL_XMIT_SIZE-1;
+	info->xmit_cnt++;
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
 {
-	struct tty_queue * queue;
-	int head, tail, count;
-	
-	if (!info)
+	struct async_struct *info = tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
 		return;
 
-	if (serial_inp(info, UART_LSR) & UART_LSR_THRE) {
-		if (info->x_char) {
-			serial_outp(info, UART_TX, info->x_char);
-			info->x_char = 0;
-		} else {
-			queue = &info->tty->write_q;
-			head = queue->head;
-			tail = queue->tail;
-			count = info->xmit_fifo_size;
-			while (count--) {
-				if (tail == head)
-					break;
-				serial_outp(info, UART_TX, queue->buf[tail++]);
-				tail &= TTY_BUF_SIZE-1;
-			}
-			queue->tail = tail;
-		}
+	if (info->xmit_cnt == 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	info->IER |= UART_IER_THRI;
+	serial_out(info, UART_IER, info->IER);
+	restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct async_struct *info = tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf)
+		return 0;
+	    
+	while (1) {
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		if (!c)
+			break;
+
+		if (from_user)
+			memcpy_fromfs(info->xmit_buf + info->xmit_head,
+				      buf, c);
+		else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		cli();
+		info->xmit_cnt += c;
+		sti();
+		buf += c;
+		count -= c;
+		total += c;
 	}
-}	
+	save_flags(flags); cli();
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+	restore_flags(flags);
+	return total;
+}
 
-/*
- * This routine gets called when tty_write has put something into
- * the write_queue.  
- */
-void rs_write(struct tty_struct * tty)
+static int rs_write_room(struct tty_struct *tty)
 {
-	struct async_struct *info;
+	struct async_struct *info = tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+		return 0;
+	return SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
 
-	if (!tty || tty->stopped || tty->hw_stopped)
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
 		return;
-	info = rs_table + DEV_TO_SL(tty->line);
 	cli();
-	if (!info || !info->tty || !(info->flags & ASYNC_INITIALIZED)) {
-		sti();
-		return;
-	}
-	restart_port(info);
-	info->IER = (UART_IER_MSI | UART_IER_RLSI |
-		     UART_IER_THRI | UART_IER_RDI);
-#ifdef ISR_HACK
-	serial_out(info, UART_IER, info->IER);
-#endif
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
 	sti();
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
 }
 
 /*
@@ -1033,47 +1247,56 @@
  * rs_throttle()
  * 
  * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled (and that the throttle
- * should be released).
+ * incoming characters should be throttled.
  * ------------------------------------------------------------
  */
-static void rs_throttle(struct tty_struct * tty, int status)
+static void rs_throttle(struct tty_struct * tty)
 {
-	struct async_struct *info;
-	unsigned char mcr;
-	unsigned long flags;
+	struct async_struct *info = tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
 
-	save_flags(flags); cli();
-#if SERIAL_DEBUG_THROTTLE
-	printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
-	       status, LEFT(&tty->read_q), LEFT(&tty->secondary));
-#endif
-	switch (status) {
-	case TTY_THROTTLE_RQ_FULL:
-		info = rs_table + DEV_TO_SL(tty->line);
-		if (I_IXOFF(tty)) {
-			info->x_char = STOP_CHAR(tty);
-		} else {
-			mcr = serial_inp(info, UART_MCR);
-			mcr &= ~UART_MCR_RTS;
-			serial_out(info, UART_MCR, mcr);
-		}
-		break;
-	case TTY_THROTTLE_RQ_AVAIL:
-		info = rs_table + DEV_TO_SL(tty->line);
-		if (I_IXOFF(tty)) {
-			if (info->x_char)
-				info->x_char = 0;
-			else
-				info->x_char = START_CHAR(tty);
-		} else {
-			mcr = serial_in(info, UART_MCR);
-			mcr |= UART_MCR_RTS;
-			serial_out(info, UART_MCR, mcr);
-		}
-		break;
+	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+
+	info->MCR &= ~UART_MCR_RTS;
+	info->MCR_noint &= ~UART_MCR_RTS;
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct async_struct *info = tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
 	}
-	restore_flags(flags);
+	info->MCR |= UART_MCR_RTS;
+	info->MCR_noint |= UART_MCR_RTS;
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
 }
 
 /*
@@ -1109,8 +1332,7 @@
 	struct serial_struct new_serial;
 	struct async_struct old_info;
 	unsigned int		i,change_irq,change_port;
-	int 			retval;
-	struct 			sigaction sa;
+	int 			retval = 0;
 
 	if (!new_info)
 		return -EFAULT;
@@ -1148,24 +1370,6 @@
 		    (rs_table[i].port == new_serial.port) && rs_table[i].type)
 			return -EADDRINUSE;
 
-	/*
-	 * If necessary, first we try to grab the new IRQ for serial
-	 * interrupts.  (We have to do this early, since we may get an
-	 * error trying to do this.)
-	 */
-	if (new_serial.port && new_serial.type && new_serial.irq &&
-	    (change_irq || !(info->flags & ASYNC_INITIALIZED))) {
-		if (!IRQ_ports[new_serial.irq]) {
-			sa.sa_handler = rs_interrupt;
-			sa.sa_flags = (SA_INTERRUPT);
-			sa.sa_mask = 0;
-			sa.sa_restorer = NULL;
-			retval = irqaction(new_serial.irq,&sa);
-			if (retval)
-				return retval;
-		}
-	}
-
 	if ((change_port || change_irq) && (info->count > 1))
 		return -EBUSY;
 
@@ -1186,7 +1390,7 @@
 		 * We need to shutdown the serial port at the old
 		 * port/irq combination.
 		 */
-		shutdown(info, change_irq);
+		shutdown(info);
 		info->irq = new_serial.irq;
 		info->port = new_serial.port;
 		info->hub6 = new_serial.hub6;
@@ -1199,10 +1403,10 @@
 		if (((old_info.flags & ASYNC_SPD_MASK) !=
 		     (info->flags & ASYNC_SPD_MASK)) ||
 		    (old_info.custom_divisor != info->custom_divisor))
-			change_speed(info->line);
+			change_speed(info);
 	} else
-		(void) startup(info, 0);
-	return 0;
+		retval = startup(info);
+	return retval;
 }
 
 static int get_modem_info(struct async_struct * info, unsigned int *value)
@@ -1210,8 +1414,8 @@
 	unsigned char control, status;
 	unsigned int result;
 
+	control = info->MCR;
 	cli();
-	control = serial_in(info, UART_MCR);
 	status = serial_in(info, UART_MSR);
 	sti();
 	result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
@@ -1227,36 +1431,43 @@
 static int set_modem_info(struct async_struct * info, unsigned int cmd,
 			  unsigned int *value)
 {
-	unsigned char control;
 	unsigned int arg = get_fs_long((unsigned long *) value);
-	
-	cli();
-	control = serial_in(info, UART_MCR);
-	sti();
 
 	switch (cmd) {
-		case TIOCMBIS:
-			if (arg & TIOCM_RTS)
-				control |= UART_MCR_RTS;
-			if (arg & TIOCM_DTR)
-				control |= UART_MCR_DTR;
-			break;
-		case TIOCMBIC:
-			if (arg & TIOCM_RTS)
-				control &= ~UART_MCR_RTS;
-			if (arg & TIOCM_DTR)
-				control &= ~UART_MCR_DTR;
-			break;
-		case TIOCMSET:
-			control = (control & ~(UART_MCR_RTS | UART_MCR_DTR))
-				| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-				| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0);
-			break;
-		default:
-			return -EINVAL;
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS) {
+			info->MCR |= UART_MCR_RTS;
+			info->MCR_noint |= UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR |= UART_MCR_DTR;
+			info->MCR_noint |= UART_MCR_DTR;
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			info->MCR &= ~UART_MCR_RTS;
+			info->MCR_noint &= ~UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR &= ~UART_MCR_DTR;
+			info->MCR_noint &= ~UART_MCR_DTR;
+		}
+		break;
+	case TIOCMSET:
+		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		info->MCR_noint = ((info->MCR_noint
+				    & ~(UART_MCR_RTS | UART_MCR_DTR))
+				   | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+				   | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		break;
+	default:
+		return -EINVAL;
 	}
 	cli();
-	serial_out(info, UART_MCR, control);
+	serial_out(info, UART_MCR, info->MCR);
 	sti();
 	return 0;
 }
@@ -1271,13 +1482,13 @@
 	if (info->count > 1)
 		return -EBUSY;
 	
-	shutdown(info, 1);
+	shutdown(info);
 
 	cli();
 	autoconfig(info);
 	sti();
 
-	retval = startup(info, 1);
+	retval = startup(info);
 	if (retval)
 		return retval;
 	return 0;
@@ -1347,20 +1558,27 @@
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
-	int error, line;
-	struct async_struct * info;
+	int error;
+	struct async_struct * info = tty->driver_data;
+	int retval;
 
-	line = DEV_TO_SL(tty->line);
-	if (line < 0 || line >= NR_PORTS)
+	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
 		return -ENODEV;
-	info = rs_table + line;
 	
 	switch (cmd) {
 		case TCSBRK:	/* SVID version: non-zero arg --> no break */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			wait_until_sent(tty, 0);
 			if (!arg)
 				send_break(info, HZ/4);	/* 1/4 second */
 			return 0;
 		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			wait_until_sent(tty, 0);
 			send_break(info, arg ? arg*(HZ/10) : HZ/4);
 			return 0;
 		case TIOCGSOFTCAR:
@@ -1416,38 +1634,29 @@
 			return 0;
 
 		default:
-			return -EINVAL;
+			return -ENOIOCTLCMD;
 		}
 	return 0;
 }
 
 static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
 {
-	struct async_struct *info;
+	struct async_struct *info = tty->driver_data;
 
 	if (tty->termios->c_cflag == old_termios->c_cflag)
 		return;
 
-	info = &rs_table[DEV_TO_SL(tty->line)];
+	change_speed(info);
 
-	change_speed(DEV_TO_SL(tty->line));
-	
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
-		rs_write(tty);
+		rs_start(tty);
 	}
 
 	if (!(old_termios->c_cflag & CLOCAL) &&
 	    (tty->termios->c_cflag & CLOCAL))
 		wake_up_interruptible(&info->open_wait);
-
-	if (I_INPCK(tty))
-		info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
-					  UART_LSR_FE | UART_LSR_PE);
-	else
-		info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
-					  UART_LSR_FE);
 }
 
 /*
@@ -1462,16 +1671,14 @@
  */
 static void rs_close(struct tty_struct *tty, struct file * filp)
 {
-	struct async_struct * info;
-	int line;
+	struct async_struct * info = tty->driver_data;
 
-	if (tty_hung_up_p(filp))
+	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
 		return;
 	
-	line = DEV_TO_SL(tty->line);
-	if ((line < 0) || (line >= NR_PORTS))
+	if (tty_hung_up_p(filp))
 		return;
-	info = rs_table + line;
+	
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
 #endif
@@ -1495,6 +1702,7 @@
 	if (info->count)
 		return;
 	info->flags |= ASYNC_CLOSING;
+	info->flags &= ~ASYNC_CTS_FLOW;
 	/*
 	 * Save the termios structure, since this port may have
 	 * separate termios for callout and dialin.
@@ -1508,23 +1716,22 @@
 	if (info->flags & ASYNC_INITIALIZED) {
 		rs_start(tty);
 		wait_until_sent(tty, 6000); /* 60 seconds timeout */
-	} else
-		flush_output(tty);
-	flush_input(tty);
-	cli();
-	/*
-	 * Make sure the UART transmitter has completely drained; this
-	 * is especially important if there is a transmit FIFO!
-	 */
-	if (!(serial_inp(info, UART_LSR) & UART_LSR_THRE)) {
-		rs_start(tty);	/* Make sure THRI interrupt enabled */
-		interruptible_sleep_on(&info->xmit_wait);
 	}
-	sti();
-	shutdown(info, 1);
-	clear_bit(line, rs_event);
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
 	info->event = 0;
 	info->tty = 0;
+	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+		if (tty->ldisc.close)
+			(tty->ldisc.close)(tty);
+		tty->ldisc = ldiscs[N_TTY];
+		tty->termios->c_line = N_TTY;
+		if (tty->ldisc.open)
+			(tty->ldisc.open)(tty);
+	}
 	if (info->blocked_open) {
 		if (info->close_delay) {
 			tty->count++; /* avoid race condition */
@@ -1545,16 +1752,12 @@
  */
 void rs_hangup(struct tty_struct *tty)
 {
-	struct async_struct * info;
-	int line;
-
-	line = DEV_TO_SL(tty->line);
-	if ((line < 0) || (line >= NR_PORTS))
+	struct async_struct * info = tty->driver_data;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
 		return;
-	info = rs_table + line;
 	
-	shutdown(info, 1);
-	clear_bit(line, rs_event);
+	shutdown(info);
 	info->event = 0;
 	info->count = 0;
 	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
@@ -1594,7 +1797,7 @@
 	 * If this is a callout device, then just make sure the normal
 	 * device isn't being used.
 	 */
-	if (MAJOR(filp->f_rdev) == TTYAUX_MAJOR) {
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
 		if (info->flags & ASYNC_NORMAL_ACTIVE)
 			return -EBUSY;
 		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
@@ -1649,7 +1852,7 @@
 			if (info->flags & ASYNC_HUP_NOTIFY)
 				retval = -EAGAIN;
 			else
-				retval = -ERESTARTSYS;
+				retval = -ERESTARTSYS;	
 #else
 			retval = -EAGAIN;
 #endif
@@ -1696,26 +1899,23 @@
 	struct async_struct	*info;
 	int 			retval, line;
 
-	line = DEV_TO_SL(tty->line);
+	line = MINOR(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= NR_PORTS))
 		return -ENODEV;
 	info = rs_table + line;
+	if (serial_paranoia_check(info, tty->device, "rs_open"))
+		return -ENODEV;
+	
 #ifdef SERIAL_DEBUG_OPEN
-	printk("rs_open ttys%d, count = %d\n", info->line, info->count);
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
 #endif
 	info->count++;
+	tty->driver_data = info;
 	info->tty = tty;
 	
-	tty->write = rs_write;
-	tty->close = rs_close;
-	tty->ioctl = rs_ioctl;
-	tty->throttle = rs_throttle;
-	tty->set_termios = rs_set_termios;
-	tty->stop = rs_stop;
-	tty->start = rs_start;
-	tty->hangup = rs_hangup;
 	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
-		if (MAJOR(filp->f_rdev) == TTY_MAJOR)
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
 			*tty->termios = info->normal_termios;
 		else 
 			*tty->termios = info->callout_termios;
@@ -1723,7 +1923,7 @@
 	/*
 	 * Start up serial port
 	 */
-	retval = startup(info, 1);
+	retval = startup(info);
 	if (retval)
 		return retval;
 
@@ -1760,23 +1960,11 @@
  */
 static void show_serial_version(void)
 {
-	printk("Serial driver version 3.99a with");
-#ifdef CONFIG_AST_FOURPORT
-	printk(" AST_FOURPORT");
-#define SERIAL_OPT
-#endif
-#ifdef CONFIG_ACCENT_ASYNC
-	printk(" ACCENT_ASYNC");
-#define SERIAL_OPT
-#endif
+	printk("Serial driver version 4.00 with");
 #ifdef CONFIG_HUB6
 	printk(" HUB-6");
 #define SERIAL_OPT
 #endif
-#ifdef CONFIG_AUTO_IRQ
-	printk (" AUTO_IRQ");
-#define SERIAL_OPT
-#endif
 #ifdef SERIAL_OPT
 	printk(" enabled\n");
 #else
@@ -1989,11 +2177,9 @@
 	int i;
 	struct async_struct * info;
 	
-	memset(&rs_event, 0, sizeof(rs_event));
-	bh_base[SERIAL_BH].routine = do_softint;
+	bh_base[SERIAL_BH].routine = do_serial_bh;
 	timer_table[RS_TIMER].fn = rs_timer;
 	timer_table[RS_TIMER].expires = 0;
-	IRQ_active = 0;
 #ifdef CONFIG_AUTO_IRQ
 	rs_wild_int_mask = check_wild_interrupts(1);
 #endif
@@ -2004,7 +2190,58 @@
 	}
 	
 	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = NR_PORTS;
+	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	serial_driver.init_termios = tty_std_termios;
+	serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = rs_open;
+	serial_driver.close = rs_close;
+	serial_driver.write = rs_write;
+	serial_driver.put_char = rs_put_char;
+	serial_driver.flush_chars = rs_flush_chars;
+	serial_driver.write_room = rs_write_room;
+	serial_driver.chars_in_buffer = rs_chars_in_buffer;
+	serial_driver.flush_buffer = rs_flush_buffer;
+	serial_driver.ioctl = rs_ioctl;
+	serial_driver.throttle = rs_throttle;
+	serial_driver.unthrottle = rs_unthrottle;
+	serial_driver.set_termios = rs_set_termios;
+	serial_driver.stop = rs_stop;
+	serial_driver.start = rs_start;
+	serial_driver.hangup = rs_hangup;
+
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+	if (tty_register_driver(&serial_driver))
+		panic("Couldn't register serial driver\n");
+	if (tty_register_driver(&callout_driver))
+		panic("Couldn't register callout driver\n");
+	
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		info->magic = SERIAL_MAGIC;
 		info->line = i;
 		info->tty = 0;
 		info->type = PORT_UNKNOWN;
@@ -2014,10 +2251,11 @@
 		info->event = 0;
 		info->count = 0;
 		info->blocked_open = 0;
-		memset(&info->callout_termios, 0, sizeof(struct termios));
-		memset(&info->normal_termios, 0, sizeof(struct termios));
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->callout_termios =callout_driver.init_termios;
+		info->normal_termios = serial_driver.init_termios;
 		info->open_wait = 0;
-		info->xmit_wait = 0;
 		info->close_wait = 0;
 		info->next_port = 0;
 		info->prev_port = 0;
diff -u --recursive --new-file v1.1.12/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.1.12/linux/drivers/char/tty_io.c	Sat May  7 14:54:02 1994
+++ linux/drivers/char/tty_io.c	Mon May 23 07:55:14 1994
@@ -43,7 +43,9 @@
 #include <linux/signal.h>
 #include <linux/fcntl.h>
 #include <linux/sched.h>
+#include <linux/interrupt.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
 #include <linux/kd.h>
@@ -59,15 +61,19 @@
 #include "vt_kern.h"
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
 
-#define MAX_TTYS 256
+#undef TTY_DEBUG_HANGUP
 
-struct tty_struct *tty_table[MAX_TTYS];
-struct termios *tty_termios[MAX_TTYS];	/* We need to keep the termios state */
-				  	/* around, even when a tty is closed */
-struct termios *termios_locked[MAX_TTYS]; /* Bitfield of locked termios flags*/
+#ifdef CONFIG_SELECTION
+extern int set_selection(const int arg);
+extern int paste_selection(struct tty_struct *tty);
+#endif /* CONFIG_SELECTION */
+extern int do_screendump(int arg);
+
+struct termios tty_std_termios;		/* for the benefit of tty drivers  */
+struct tty_driver *tty_drivers;		/* linked list of tty drivers */
 struct tty_ldisc ldiscs[NR_LDISCS];	/* line disc dispatch table	*/
-int tty_check_write[MAX_TTYS/32];	/* bitfield for the bh handler */
 
 /*
  * fg_console is the current virtual console,
@@ -78,14 +84,63 @@
 struct tty_struct * redirect = NULL;
 struct wait_queue * keypress_wait = NULL;
 
-static void initialize_tty_struct(int line, struct tty_struct *tty);
-static void initialize_termios(int line, struct termios *tp);
+static void initialize_tty_struct(struct tty_struct *tty);
 
 static int tty_read(struct inode *, struct file *, char *, int);
 static int tty_write(struct inode *, struct file *, char *, int);
 static int tty_select(struct inode *, struct file *, int, select_table *);
 static int tty_open(struct inode *, struct file *);
 static void tty_release(struct inode *, struct file *);
+static int tty_ioctl(struct inode * inode, struct file * file,
+		     unsigned int cmd, unsigned long arg);
+static int tty_fasync(struct inode * inode, struct file * filp, int on);
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * These two routines return the name of tty.  tty_name() should NOT
+ * be used in interrupt drivers, since it's not re-entrant.  Use
+ * _tty_name() instead.
+ */
+char *_tty_name(struct tty_struct *tty, char *buf)
+{
+	sprintf(buf, "%s%d", tty->driver.name,
+		MINOR(tty->device) - tty->driver.minor_start +
+		tty->driver.name_base);
+	return buf;
+}
+
+char *tty_name(struct tty_struct *tty)
+{
+	static char buf[64];
+
+	return(_tty_name(tty, buf));
+}
+
+#define TTY_PARANOIA_CHECK
+
+inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
+			      const char *routine)
+{
+#ifdef TTY_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for tty struct (%d, %d) in %s\n";
+	static const char *badtty =
+		"Warning: null TTY for (%d, %d) in %s\n";
+
+	if (!tty) {
+		printk(badtty, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (tty->magic != TTY_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
 
 int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
 {
@@ -95,94 +150,102 @@
 	if (new_ldisc) {
 		ldiscs[disc] = *new_ldisc;
 		ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
+		ldiscs[disc].num = disc;
 	} else
 		memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
 	
 	return 0;
 }
 
-void put_tty_queue(unsigned char c, struct tty_queue * queue)
+/* Set the discipline of a tty line. */
+static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 {
-	int head;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	head = (queue->head + 1) & (TTY_BUF_SIZE-1);
-	if (head != queue->tail) {
-		queue->buf[queue->head] = c;
-		queue->head = head;
-	}
-	restore_flags(flags);
-}
+	int	retval;
+	struct	tty_ldisc o_ldisc;
 
-int get_tty_queue(struct tty_queue * queue)
-{
-	int result = -1;
-	unsigned long flags;
+	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
+	    !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
+		return -EINVAL;
 
-	save_flags(flags);
-	cli();
-	if (queue->tail != queue->head) {
-		result = queue->buf[queue->tail];
-		INC(queue->tail);
+	if (tty->ldisc.num == ldisc)
+		return 0;	/* We are already in the desired discipline */
+	o_ldisc = tty->ldisc;
+
+	/* Shutdown the current discipline. */
+	if (tty->ldisc.close)
+		(tty->ldisc.close)(tty);
+
+	/* Now set up the new line discipline. */
+	tty->ldisc = ldiscs[ldisc];
+	tty->termios->c_line = ldisc;
+	if (tty->ldisc.open) {
+		retval = (tty->ldisc.open)(tty);
+		if (!retval)
+			return 0;
+		
+		tty->ldisc = o_ldisc;
+		tty->termios->c_line = tty->ldisc.num;
+		if (tty->ldisc.open && tty->ldisc.open(tty)) {
+			tty->ldisc = ldiscs[N_TTY];
+			tty->termios->c_line = N_TTY;
+			if (tty->ldisc.open) {
+				int r = tty->ldisc.open(tty);
+
+				if (r)
+					panic("Couldn't open N_TTY ldisc for "
+					      "%s --- error %d.",
+					      tty_name(tty), r);
+			}
+		}
+		return retval;
 	}
-	restore_flags(flags);
-	return result;
+	return 0;
 }
 
 /*
- * This routine copies out a maximum of buflen characters from the
- * read_q; it is a convenience for line disciplines so they can grab a
- * large block of data without calling get_tty_char directly.  It
- * returns the number of characters actually read. Return terminates
- * if an error character is read from the queue and the return value
- * is negated.
+ * This routine returns a tty driver structure, given a device number
  */
-int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen)
+struct tty_driver *get_tty_driver(dev_t device)
 {
-	int	result = 0;
-	unsigned char	*p = bufp;
-	unsigned long flags;
-	int head, tail;
-	int ok = 1;
+	int	major, minor;
+	struct tty_driver *p;
+	
+	minor = MINOR(device);
+	major = MAJOR(device);
 
-	save_flags(flags);
-	cli();
-	tail = tty->read_q.tail;
-	head = tty->read_q.head;
-	while ((result < buflen) && (tail!=head) && ok) {
-		ok = !clear_bit (tail, &tty->readq_flags);
-		*p++ =  tty->read_q.buf[tail++];
-		tail &= TTY_BUF_SIZE-1;
-		result++;
+	for (p = tty_drivers; p; p = p->next) {
+		if (p->major != major)
+			continue;
+		if (minor < p->minor_start)
+			continue;
+		if (minor >= p->minor_start + p->num)
+			continue;
+		return p;
 	}
-	tty->read_q.tail = tail;
-	restore_flags(flags);
-	return (ok) ? result : -result;
+	return NULL;
 }
 
-
-void tty_write_flush(struct tty_struct * tty)
-{
-	if (!tty->write || EMPTY(&tty->write_q))
-		return;
-	if (set_bit(TTY_WRITE_BUSY,&tty->flags))
-		return;
-	tty->write(tty);
-	if (!clear_bit(TTY_WRITE_BUSY,&tty->flags))
-		printk("tty_write_flush: bit already cleared\n");
-}
-
-void tty_read_flush(struct tty_struct * tty)
+/*
+ * If we try to write to, or set the state of, a terminal and we're
+ * not in the foreground, send a SIGTTOU.  If the signal is blocked or
+ * ignored, go ahead and perform the operation.  (POSIX 7.2)
+ */
+int tty_check_change(struct tty_struct * tty)
 {
-	if (!tty || EMPTY(&tty->read_q))
-		return;
-	if (set_bit(TTY_READ_BUSY, &tty->flags))
-		return;
-	ldiscs[tty->disc].handler(tty);
-	if (!clear_bit(TTY_READ_BUSY, &tty->flags))
-		printk("tty_read_flush: bit already cleared\n");
+	if (current->tty != tty)
+		return 0;
+	if (tty->pgrp <= 0) {
+		printk("tty_check_change: tty->pgrp <= 0!\n");
+		return 0;
+	}
+	if (current->pgrp == tty->pgrp)
+		return 0;
+	if (is_ignored(SIGTTOU))
+		return 0;
+	if (is_orphaned_pgrp(current->pgrp))
+		return -EIO;
+	(void) kill_pg(current->pgrp,SIGTTOU,1);
+	return -ERESTARTSYS;
 }
 
 static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count)
@@ -220,7 +283,9 @@
 	tty_ioctl,
 	NULL,		/* tty_mmap */
 	tty_open,
-	tty_release
+	tty_release,
+	NULL,		/* tty_fsync */
+	tty_fasync
 };
 
 static struct file_operations hung_up_tty_fops = {
@@ -232,7 +297,9 @@
 	hung_up_tty_ioctl,
 	NULL,		/* hung_up_tty_mmap */
 	NULL,		/* hung_up_tty_open */
-	tty_release	/* hung_up_tty_release */
+	tty_release,	/* hung_up_tty_release */
+	NULL,		/* hung_up_tty_fsync  */
+	NULL		/* hung_up_tty_fasync */
 };
 
 void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
@@ -240,43 +307,68 @@
 	int i;
 	struct file * filp;
 	struct task_struct *p;
-	int dev;
 
 	if (!tty)
 		return;
-	dev = MKDEV(TTY_MAJOR,tty->line);
 	for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
 		if (!filp->f_count)
 			continue;
-		if (filp->f_rdev != dev)
+		if (filp->private_data != tty)
 			continue;
 		if (filp->f_inode && filp->f_inode->i_rdev == CONSOLE_DEV)
 			continue;
 		if (filp->f_op != &tty_fops)
 			continue;
+		tty_fasync(filp->f_inode, filp, 0);
 		filp->f_op = fops;
 	}
-	flush_input(tty);
-	flush_output(tty);
-	wake_up_interruptible(&tty->secondary.proc_list);
+	
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
+	wake_up_interruptible(&tty->read_wait);
+
+	/*
+	 * Shutdown the current line discipline, and reset it to
+	 * N_TTY.
+	 */
+	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+		if (tty->ldisc.close)
+			(tty->ldisc.close)(tty);
+		tty->ldisc = ldiscs[N_TTY];
+		tty->termios->c_line = N_TTY;
+		if (tty->ldisc.open) {
+			i = (tty->ldisc.open)(tty);
+			if (i)
+				printk("do_tty_hangup: N_TTY open: error %d\n",
+				       i);
+		}
+	}
+	
 	if (tty->session > 0) {
 		kill_sl(tty->session,SIGHUP,1);
 		kill_sl(tty->session,SIGCONT,1);
 	}
+	tty->flags = 0;
 	tty->session = 0;
 	tty->pgrp = -1;
  	for_each_task(p) {
-		if (p->tty == tty->line)
-			p->tty = -1;
+		if (p->tty == tty)
+			p->tty = NULL;
 	}
-	if (tty->hangup)
-		(tty->hangup)(tty);
+	if (tty->driver.hangup)
+		(tty->driver.hangup)(tty);
 }
 
 void tty_hangup(struct tty_struct * tty)
 {
 #ifdef TTY_DEBUG_HANGUP
-	printk("tty%d hangup...\n", tty->line);
+	printk("%s hangup...\n", tty_name(tty));
 #endif
 	do_tty_hangup(tty, &hung_up_tty_fops);
 }
@@ -284,7 +376,7 @@
 void tty_vhangup(struct tty_struct * tty)
 {
 #ifdef TTY_DEBUG_HANGUP
-	printk("tty%d vhangup...\n", tty->line);
+	printk("%s vhangup...\n", tty_name(tty));
 #endif
 	do_tty_hangup(tty, &hung_up_tty_fops);
 }
@@ -306,25 +398,22 @@
  */
 void disassociate_ctty(int priv)
 {
-	struct tty_struct *tty;
+	struct tty_struct *tty = current->tty;
 	struct task_struct *p;
 
-	if (current->tty >= 0) {
-		tty = tty_table[current->tty];
-		if (tty) {
-			if (tty->pgrp > 0) {
-				kill_pg(tty->pgrp, SIGHUP, priv);
-				kill_pg(tty->pgrp, SIGCONT, priv);
-			}
-			tty->session = 0;
-			tty->pgrp = -1;
-		} else
-			printk("disassociate_ctty: ctty is NULL?!?");
+	if (!tty)
+		return;
+
+	if (tty->pgrp > 0) {
+		kill_pg(tty->pgrp, SIGHUP, priv);
+		kill_pg(tty->pgrp, SIGCONT, priv);
 	}
+	tty->session = 0;
+	tty->pgrp = -1;
 
 	for_each_task(p)
 	  	if (p->session == current->session)
-			p->tty = -1;
+			p->tty = NULL;
 }
 
 /*
@@ -516,14 +605,10 @@
 	if (tty->link && tty->link->packet) {
 		tty->ctrl_status &= ~TIOCPKT_START;
 		tty->ctrl_status |= TIOCPKT_STOP;
-		wake_up_interruptible(&tty->link->secondary.proc_list);
-	}
-	if (tty->stop)
-		(tty->stop)(tty);
-	if (IS_A_CONSOLE(tty->line)) {
-		set_vc_kbd_led(kbd_table + fg_console, VC_SCROLLOCK);
-		set_leds();
+		wake_up_interruptible(&tty->link->read_wait);
 	}
+	if (tty->driver.stop)
+		(tty->driver.stop)(tty);
 }
 
 void start_tty(struct tty_struct *tty)
@@ -534,627 +619,24 @@
 	if (tty->link && tty->link->packet) {
 		tty->ctrl_status &= ~TIOCPKT_STOP;
 		tty->ctrl_status |= TIOCPKT_START;
-		wake_up_interruptible(&tty->link->secondary.proc_list);
-	}
-	if (tty->start)
-		(tty->start)(tty);
-	TTY_WRITE_FLUSH(tty);
-	if (IS_A_CONSOLE(tty->line)) {
-		clr_vc_kbd_led(kbd_table + fg_console, VC_SCROLLOCK);
-		set_leds();
-	}
-}
-
-/* Perform OPOST processing.  Returns -1 when the write_q becomes full
-   and the character must be retried. */
-
-static int opost(unsigned char c, struct tty_struct *tty)
-{
-	if (FULL(&tty->write_q))
-		return -1;
-	if (O_OPOST(tty)) {
-		switch (c) {
-		case '\n':
-			if (O_ONLRET(tty))
-				tty->column = 0;
-			if (O_ONLCR(tty)) {
-				if (LEFT(&tty->write_q) < 2)
-					return -1;
-				put_tty_queue('\r', &tty->write_q);
-				tty->column = 0;
-			}
-			tty->canon_column = tty->column;
-			break;
-		case '\r':
-			if (O_ONOCR(tty) && tty->column == 0)
-				return 0;
-			if (O_OCRNL(tty)) {
-				c = '\n';
-				if (O_ONLRET(tty))
-					tty->canon_column = tty->column = 0;
-				break;
-			}
-			tty->canon_column = tty->column = 0;
-			break;
-		case '\t':
-			if (O_TABDLY(tty) == XTABS) {
-				if (LEFT(&tty->write_q) < 8)
-					return -1;
-				do
-					put_tty_queue(' ', &tty->write_q);
-				while (++tty->column % 8);
-				return 0;
-			}
-			tty->column = (tty->column | 7) + 1;
-			break;
-		case '\b':
-			if (tty->column > 0)
-				tty->column--;
-			break;
-		default:
-			if (O_OLCUC(tty))
-				c = toupper(c);
-			if (!iscntrl(c))
-				tty->column++;
-			break;
-		}
-	}
-	put_tty_queue(c, &tty->write_q);
-	return 0;
-}
-
-/* Must be called only when L_ECHO(tty) is true. */
-
-static void echo_char(unsigned char c, struct tty_struct *tty)
-{
-	if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
-		opost('^', tty);
-		opost(c ^ 0100, tty);
-	} else
-		opost(c, tty);
-}
-
-static void eraser(unsigned char c, struct tty_struct *tty)
-{
-	enum { ERASE, WERASE, KILL } kill_type;
-	int seen_alnums;
-
-	if (tty->secondary.head == tty->canon_head) {
-		/* opost('\a', tty); */		/* what do you think? */
-		return;
-	}
-	if (c == ERASE_CHAR(tty))
-		kill_type = ERASE;
-	else if (c == WERASE_CHAR(tty))
-		kill_type = WERASE;
-	else {
-		if (!L_ECHO(tty)) {
-			tty->secondary.head = tty->canon_head;
-			return;
-		}
-		if (!L_ECHOK(tty) || !L_ECHOKE(tty)) {
-			tty->secondary.head = tty->canon_head;
-			if (tty->erasing) {
-				opost('/', tty);
-				tty->erasing = 0;
-			}
-			echo_char(KILL_CHAR(tty), tty);
-			/* Add a newline if ECHOK is on and ECHOKE is off. */
-			if (L_ECHOK(tty))
-				opost('\n', tty);
-			return;
-		}
-		kill_type = KILL;
-	}
-
-	seen_alnums = 0;
-	while (tty->secondary.head != tty->canon_head) {
-		c = LAST(&tty->secondary);
-		if (kill_type == WERASE) {
-			/* Equivalent to BSD's ALTWERASE. */
-			if (isalnum(c) || c == '_')
-				seen_alnums++;
-			else if (seen_alnums)
-				break;
-		}
-		DEC(tty->secondary.head);
-		if (L_ECHO(tty)) {
-			if (L_ECHOPRT(tty)) {
-				if (!tty->erasing) {
-					opost('\\', tty);
-					tty->erasing = 1;
-				}
-				echo_char(c, tty);
-			} else if (!L_ECHOE(tty)) {
-				echo_char(ERASE_CHAR(tty), tty);
-			} else if (c == '\t') {
-				unsigned int col = tty->canon_column;
-				unsigned long tail = tty->canon_head;
-
-				/* Find the column of the last char. */
-				while (tail != tty->secondary.head) {
-					c = tty->secondary.buf[tail];
-					if (c == '\t')
-						col = (col | 7) + 1;
-					else if (iscntrl(c)) {
-						if (L_ECHOCTL(tty))
-							col += 2;
-					} else
-						col++;
-					INC(tail);
-				}
-
-				/* Now backup to that column. */
-				while (tty->column > col) {
-					/* Can't use opost here. */
-					put_tty_queue('\b', &tty->write_q);
-					tty->column--;
-				}
-			} else {
-				if (iscntrl(c) && L_ECHOCTL(tty)) {
-					opost('\b', tty);
-					opost(' ', tty);
-					opost('\b', tty);
-				}
-				if (!iscntrl(c) || L_ECHOCTL(tty)) {
-					opost('\b', tty);
-					opost(' ', tty);
-					opost('\b', tty);
-				}
-			}
-		}
-		if (kill_type == ERASE)
-			break;
-	}
-	if (tty->erasing && tty->secondary.head == tty->canon_head) {
-		opost('/', tty);
-		tty->erasing = 0;
-	}
-}
-
-static void isig(int sig, struct tty_struct *tty)
-{
-	kill_pg(tty->pgrp, sig, 1);
-	if (!L_NOFLSH(tty)) {
-		flush_input(tty);
-		flush_output(tty);
-	}
-}
-
-static void copy_to_cooked(struct tty_struct * tty)
-{
-	int c, special_flag;
-	unsigned long flags;
-
-	if (!tty) {
-		printk("copy_to_cooked: called with NULL tty\n");
-		return;
-	}
-	if (!tty->write) {
-		printk("copy_to_cooked: tty %d has null write routine\n",
-		       tty->line);
-	}
-	while (1) {
-		/*
-		 * Check to see how much room we have left in the
-		 * secondary queue.  Send a throttle command or abort
-		 * if necessary.
-		 */
-		c = LEFT(&tty->secondary);
-		if (tty->throttle && (c < SQ_THRESHOLD_LW)
-		    && !set_bit(TTY_SQ_THROTTLED, &tty->flags))
-			tty->throttle(tty, TTY_THROTTLE_SQ_FULL);
-		if (c == 0)
-			break;
-		save_flags(flags); cli();
-		if (!EMPTY(&tty->read_q)) {
-			c = tty->read_q.buf[tty->read_q.tail];
-			special_flag = clear_bit(tty->read_q.tail,
-						 &tty->readq_flags);
-			INC(tty->read_q.tail);
-			restore_flags(flags);
-		} else {
-			restore_flags(flags);
-			break;
-		}
-		if (special_flag) {
-			tty->char_error = c;
-			continue;
-		}
-		if (tty->char_error) {
-			if (tty->char_error == TTY_BREAK) {
-				tty->char_error = 0;
-				if (I_IGNBRK(tty))
-					continue;
-				/* A break is handled by the lower levels. */
-				if (I_BRKINT(tty))
-					continue;
-				if (I_PARMRK(tty)) {
-					put_tty_queue('\377', &tty->secondary);
-					put_tty_queue('\0', &tty->secondary);
-				}
-				put_tty_queue('\0', &tty->secondary);
-				continue;
-			}
-			if (tty->char_error == TTY_OVERRUN) {
-				tty->char_error = 0;
-				printk("tty%d: input overrun\n", tty->line);
-				continue;
-			}
-			/* Must be a parity or frame error */
-			tty->char_error = 0;
-			if (I_IGNPAR(tty)) {
-				continue;
-			}
-			if (I_PARMRK(tty)) {
-				put_tty_queue('\377', &tty->secondary);
-				put_tty_queue('\0', &tty->secondary);
-				put_tty_queue(c, &tty->secondary);
-			} else
-				put_tty_queue('\0', &tty->secondary);
-			continue;
-		}
-		if (I_ISTRIP(tty))
-			c &= 0x7f;
-		if (!tty->lnext) {
-			if (c == '\r') {
-				if (I_IGNCR(tty))
-					continue;
-				if (I_ICRNL(tty))
-					c = '\n';
-			} else if (c == '\n' && I_INLCR(tty))
-				c = '\r';
-		}
-		if (I_IUCLC(tty) && L_IEXTEN(tty))
-			c=tolower(c);
-		if (c == __DISABLED_CHAR)
-			tty->lnext = 1;
-		if (L_ICANON(tty) && !tty->lnext) {
-			if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
-			    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
-				eraser(c, tty);
-				continue;
-			}
-			if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
-				tty->lnext = 1;
-				if (L_ECHO(tty)) {
-					if (tty->erasing) {
-						opost('/', tty);
-						tty->erasing = 0;
-					}
-					if (L_ECHOCTL(tty)) {
-						opost('^', tty);
-						opost('\b', tty);
-					}
-				}
-				continue;
-			}
-			if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
-			    L_IEXTEN(tty)) {
-				unsigned long tail = tty->canon_head;
-
-				if (tty->erasing) {
-					opost('/', tty);
-					tty->erasing = 0;
-				}
-				echo_char(c, tty);
-				opost('\n', tty);
-				while (tail != tty->secondary.head) {
-					echo_char(tty->secondary.buf[tail],
-						  tty);
-					INC(tail);
-				}
-				continue;
-			}
-		}
-		if (I_IXON(tty) && !tty->lnext) {
-			if ((tty->stopped && I_IXANY(tty) && L_IEXTEN(tty)) ||
-			    c == START_CHAR(tty)) {
-				start_tty(tty);
-				continue;
-			}
-			if (c == STOP_CHAR(tty)) {
-				stop_tty(tty);
-				continue;
-			}
-		}
-		if (L_ISIG(tty) && !tty->lnext) {
-			if (c == INTR_CHAR(tty)) {
-				isig(SIGINT, tty);
-				continue;
-			}
-			if (c == QUIT_CHAR(tty)) {
-				isig(SIGQUIT, tty);
-				continue;
-			}
-			if (c == SUSP_CHAR(tty)) {
-				if (!is_orphaned_pgrp(tty->pgrp))
-					isig(SIGTSTP, tty);
-				continue;
-			}
-		}
-
-		if (tty->erasing) {
-			opost('/', tty);
-			tty->erasing = 0;
-		}
-		if (c == '\n' && !tty->lnext) {
-			if (L_ECHO(tty) || (L_ICANON(tty) && L_ECHONL(tty)))
-				opost('\n', tty);
-		} else if (L_ECHO(tty)) {
-			/* Don't echo the EOF char in canonical mode.  Sun
-			   handles this differently by echoing the char and
-			   then backspacing, but that's a hack. */
-			if (c != EOF_CHAR(tty) || !L_ICANON(tty) ||
-			    tty->lnext) {
-				/* Record the column of first canon char. */
-				if (tty->canon_head == tty->secondary.head)
-					tty->canon_column = tty->column;
-				echo_char(c, tty);
-			}
-		}
-
-		if (I_PARMRK(tty) && c == (unsigned char) '\377' &&
-		    (c != EOF_CHAR(tty) || !L_ICANON(tty) || tty->lnext))
-			put_tty_queue(c, &tty->secondary);
-
-		if (L_ICANON(tty) && !tty->lnext &&
-		    (c == '\n' || c == EOF_CHAR(tty) || c == EOL_CHAR(tty) ||
-		     (c == EOL2_CHAR(tty) && L_IEXTEN(tty)))) {
-			if (c == EOF_CHAR(tty))
-				c = __DISABLED_CHAR;
-			set_bit(tty->secondary.head, &tty->secondary_flags);
-			put_tty_queue(c, &tty->secondary);
-			tty->canon_head = tty->secondary.head;
-			tty->canon_data++;
-		} else
-			put_tty_queue(c, &tty->secondary);
-		tty->lnext = 0;
-	}
-	if (!EMPTY(&tty->write_q))
-		TTY_WRITE_FLUSH(tty);
-	if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary))
-		wake_up_interruptible(&tty->secondary.proc_list);
-
-	if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW)
-	    && clear_bit(TTY_RQ_THROTTLED, &tty->flags))
-		tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL);
-}
-
-int is_ignored(int sig)
-{
-	return ((current->blocked & (1<<(sig-1))) ||
-	        (current->sigaction[sig-1].sa_handler == SIG_IGN));
-}
-
-static inline int input_available_p(struct tty_struct *tty)
-{
-	/* Avoid calling TTY_READ_FLUSH unnecessarily. */
-	if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary))
-		return 1;
-
-	/* Shuffle any pending data down the queues. */
-	TTY_READ_FLUSH(tty);
-	if (tty->link)
-		TTY_WRITE_FLUSH(tty->link);
-
-	if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary))
-		return 1;
-	return 0;
-}
-
-static int read_chan(struct tty_struct *tty, struct file *file,
-		     unsigned char *buf, unsigned int nr)
-{
-	struct wait_queue wait = { current, NULL };
-	int c;
-	unsigned char *b = buf;
-	int minimum, time;
-	int retval = 0;
-
-	/* Job control check -- must be done at start and after
-	   every sleep (POSIX.1 7.1.1.4). */
-	/* NOTE: not yet done after every sleep pending a thorough
-	   check of the logic of this change. -- jlc */
-	/* don't stop on /dev/console */
-	if (file->f_inode->i_rdev != CONSOLE_DEV &&
-	    current->tty == tty->line) {
-		if (tty->pgrp <= 0)
-			printk("read_chan: tty->pgrp <= 0!\n");
-		else if (current->pgrp != tty->pgrp) {
-			if (is_ignored(SIGTTIN) ||
-			    is_orphaned_pgrp(current->pgrp))
-				return -EIO;
-			kill_pg(current->pgrp, SIGTTIN, 1);
-			return -ERESTARTSYS;
-		}
-	}
-
-	if (L_ICANON(tty)) {
-		minimum = time = 0;
-		current->timeout = (unsigned long) -1;
-	} else {
-		time = (HZ / 10) * TIME_CHAR(tty);
-		minimum = MIN_CHAR(tty);
-		if (minimum)
-		  	current->timeout = (unsigned long) -1;
-		else {
-			if (time) {
-				current->timeout = time + jiffies;
-				time = 0;
-			} else
-				current->timeout = 0;
-			minimum = 1;
-		}
-	}
-
-	add_wait_queue(&tty->secondary.proc_list, &wait);
-	while (1) {
-		/* First test for status change. */
-		if (tty->packet && tty->link->ctrl_status) {
-			if (b != buf)
-				break;
-			put_fs_byte(tty->link->ctrl_status, b++);
-			tty->link->ctrl_status = 0;
-			break;
-		}
-		/* This statement must be first before checking for input
-		   so that any interrupt will set the state back to
-		   TASK_RUNNING. */
-		current->state = TASK_INTERRUPTIBLE;
-		if (!input_available_p(tty)) {
-			if (tty->flags & (1 << TTY_SLAVE_CLOSED)) {
-				retval = -EIO;
-				break;
-			}
-			if (tty_hung_up_p(file))
-				break;
-			if (!current->timeout)
-				break;
-			if (file->f_flags & O_NONBLOCK) {
-				retval = -EAGAIN;
-				break;
-			}
-			if (current->signal & ~current->blocked) {
-				retval = -ERESTARTSYS;
-				break;
-			}
-			schedule();
-			continue;
-		}
-		current->state = TASK_RUNNING;
-
-		/* Deal with packet mode. */
-		if (tty->packet && b == buf) {
-			put_fs_byte(TIOCPKT_DATA, b++);
-			nr--;
-		}
-
-		while (1) {
-			int eol;
-
-			cli();
-			if (EMPTY(&tty->secondary)) {
-				sti();
-				break;
-			}
-			eol = clear_bit(tty->secondary.tail,
-					&tty->secondary_flags);
-			c = tty->secondary.buf[tty->secondary.tail];
-			if (!nr) {
-				/* Gobble up an immediately following EOF if
-				   there is no more room in buf (this can
-				   happen if the user "pushes" some characters
-				   using ^D).  This prevents the next read()
-				   from falsely returning EOF. */
-				if (eol) {
-					if (c == __DISABLED_CHAR) {
-						tty->canon_data--;
-						INC(tty->secondary.tail);
-					} else {
-						set_bit(tty->secondary.tail,
-							&tty->secondary_flags);
-					}
-				}
-				sti();
-				break;
-			}
-			INC(tty->secondary.tail);
-			sti();
-			if (eol) {
-				if (--tty->canon_data < 0) {
-					printk("read_chan: canon_data < 0!\n");
-					tty->canon_data = 0;
-				}
-				if (c == __DISABLED_CHAR)
-					break;
-				put_fs_byte(c, b++);
-				nr--;
-				break;
-			}
-			put_fs_byte(c, b++);
-			nr--;
-		}
-
-		/* If there is enough space in the secondary queue now, let the
-		   low-level driver know. */
-		if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
-		    && clear_bit(TTY_SQ_THROTTLED, &tty->flags))
-			tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
-
-		if (b - buf >= minimum || !nr)
-			break;
-		if (time)
-			current->timeout = time + jiffies;
-	}
-	remove_wait_queue(&tty->secondary.proc_list, &wait);
-	current->state = TASK_RUNNING;
-	current->timeout = 0;
-	return (b - buf) ? b - buf : retval;
-}
-
-static int write_chan(struct tty_struct * tty, struct file * file,
-		      unsigned char * buf, unsigned int nr)
-{
-	struct wait_queue wait = { current, NULL };
-	int c;
-	unsigned char *b = buf;
-	int retval = 0;
-
-	/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
-	if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
-		retval = check_change(tty, tty->line);
-		if (retval)
-			return retval;
-	}
-
-	add_wait_queue(&tty->write_q.proc_list, &wait);
-	while (1) {
-		current->state = TASK_INTERRUPTIBLE;
-		if (current->signal & ~current->blocked) {
-			retval = -ERESTARTSYS;
-			break;
-		}
-		if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
-			retval = -EIO;
-			break;
-		}
-		while (nr > 0) {
-			c = get_fs_byte(b);
-			/* Care is needed here: opost() can abort even
-			   if the write_q is not full. */
-			if (opost(c, tty) < 0)
-				break;
-			b++; nr--;
-		}
-		TTY_WRITE_FLUSH(tty);
-		if (!nr)
-			break;
-		if (EMPTY(&tty->write_q) && !need_resched)
-			continue;
-		if (file->f_flags & O_NONBLOCK) {
-			retval = -EAGAIN;
-			break;
-		}
-		schedule();
+		wake_up_interruptible(&tty->link->read_wait);
 	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&tty->write_q.proc_list, &wait);
-	return (b - buf) ? b - buf : retval;
+	if (tty->driver.start)
+		(tty->driver.start)(tty);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
 }
 
 static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
 {
-	int i, dev;
+	int i;
 	struct tty_struct * tty;
 
-	dev = file->f_rdev;
-	if (MAJOR(dev) != TTY_MAJOR) {
-		printk("tty_read: bad pseudo-major nr #%d\n", MAJOR(dev));
-		return -EINVAL;
-	}
-	dev = MINOR(dev);
-	tty = TTY_TABLE(dev);
+	tty = file->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
+		return -EIO;
 	if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
 		return -EIO;
 
@@ -1165,7 +647,7 @@
 #if 0
 	if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
 	    (tty->pgrp > 0) &&
-	    (current->tty == dev) &&
+	    (current->tty == tty) &&
 	    (tty->pgrp != current->pgrp))
 		if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
 			return -EIO;
@@ -1174,9 +656,9 @@
 			return -ERESTARTSYS;
 		}
 #endif
-	if (ldiscs[tty->disc].read)
+	if (tty->ldisc.read)
 		/* XXX casts are for what kernel-wide prototypes should be. */
-		i = (ldiscs[tty->disc].read)(tty,file,(unsigned char *)buf,(unsigned int)count);
+		i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count);
 	else
 		i = -EIO;
 	if (i > 0)
@@ -1186,25 +668,22 @@
 
 static int tty_write(struct inode * inode, struct file * file, char * buf, int count)
 {
-	int dev, i, is_console;
+	int i, is_console;
 	struct tty_struct * tty;
 
-	dev = file->f_rdev;
 	is_console = (inode->i_rdev == CONSOLE_DEV);
-	if (MAJOR(dev) != TTY_MAJOR) {
-		printk("tty_write: pseudo-major != TTY_MAJOR\n");
-		return -EINVAL;
-	}
-	dev = MINOR(dev);
+
 	if (is_console && redirect)
 		tty = redirect;
 	else
-		tty = TTY_TABLE(dev);
-	if (!tty || !tty->write || (tty->flags & (1 << TTY_IO_ERROR)))
+		tty = file->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
 		return -EIO;
+	if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
+		return -EIO;
 #if 0
 	if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
-	    (current->tty == dev) && (tty->pgrp != current->pgrp)) {
+	    (current->tty == tty) && (tty->pgrp != current->pgrp)) {
 		if (is_orphaned_pgrp(current->pgrp))
 			return -EIO;
 		if (!is_ignored(SIGTTOU)) {
@@ -1213,9 +692,9 @@
 		}
 	}
 #endif
-	if (ldiscs[tty->disc].write)
+	if (tty->ldisc.write)
 		/* XXX casts are for what kernel-wide prototypes should be. */
-		i = (ldiscs[tty->disc].write)(tty,file,(unsigned char *)buf,(unsigned int)count);
+		i = (tty->ldisc.write)(tty,file,(unsigned char *)buf,(unsigned int)count);
 	else
 		i = -EIO;
 	if (i > 0)
@@ -1228,37 +707,54 @@
  * unless you know exactly what you are doing. All the changes have to be
  * made atomically, or there may be incorrect pointers all over the place.
  */
-static int init_dev(int dev)
+static int init_dev(dev_t device, struct tty_struct **ret_tty)
 {
-	struct tty_struct *tty, *o_tty;
-	struct termios *tp, *o_tp, *ltp, *o_ltp;
+	struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+	struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
+	struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
+	struct tty_driver *driver;	
 	int retval;
-	int o_dev;
+	int idx;
 
-	o_dev = PTY_OTHER(dev);
+	driver = get_tty_driver(device);
+	if (!driver)
+		return -ENODEV;
+
+	idx = MINOR(device) - driver->minor_start;
 	tty = o_tty = NULL;
 	tp = o_tp = NULL;
 	ltp = o_ltp = NULL;
-repeat:
+	o_tty_loc = NULL;
+	o_tp_loc = o_ltp_loc = NULL;
+
+	tty_loc = &driver->table[idx];
+	tp_loc = &driver->termios[idx];
+	ltp_loc = &driver->termios_locked[idx];
+
 	retval = -EAGAIN;
-	if (IS_A_PTY_MASTER(dev) && tty_table[dev] && tty_table[dev]->count)
+	if (driver->type == TTY_DRIVER_TYPE_PTY &&
+	    driver->subtype == PTY_TYPE_MASTER &&
+	    *tty_loc && (*tty_loc)->count)
 		goto end_init;
+repeat:
 	retval = -ENOMEM;
-	if (!tty_table[dev] && !tty) {
+	if (!*tty_loc && !tty) {
 		if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
 			goto end_init;
-		initialize_tty_struct(dev, tty);
+		initialize_tty_struct(tty);
+		tty->device = device;
+		tty->driver = *driver;
 		goto repeat;
 	}
-	if (!tty_termios[dev] && !tp) {
+	if (!*tp_loc && !tp) {
 		tp = (struct termios *) kmalloc(sizeof(struct termios),
 						GFP_KERNEL);
 		if (!tp)
 			goto end_init;
-		initialize_termios(dev, tp);
+		*tp = driver->init_termios;
 		goto repeat;
 	}
-	if (!termios_locked[dev] && !ltp) {
+	if (!*ltp_loc && !ltp) {
 		ltp = (struct termios *) kmalloc(sizeof(struct termios),
 						 GFP_KERNEL);
 		if (!ltp)
@@ -1266,24 +762,34 @@
 		memset(ltp, 0, sizeof(struct termios));
 		goto repeat;
 	}
-	if (IS_A_PTY(dev)) {
-		if (!tty_table[o_dev] && !o_tty) {
+	if (driver->type == TTY_DRIVER_TYPE_PTY) {
+		o_tty_loc = &driver->other->table[idx];
+		o_tp_loc = &driver->other->termios[idx];
+		o_ltp_loc = &driver->other->termios_locked[idx];
+
+		if (!*o_tty_loc && !o_tty) {
+			dev_t 	o_device;
+			
 			o_tty = (struct tty_struct *)
 				get_free_page(GFP_KERNEL);
 			if (!o_tty)
 				goto end_init;
-			initialize_tty_struct(o_dev, o_tty);
+			o_device = MKDEV(driver->other->major,
+					 driver->other->minor_start + idx);
+			initialize_tty_struct(o_tty);
+			o_tty->device = o_device;
+			o_tty->driver = *driver->other;
 			goto repeat;
 		}
-		if (!tty_termios[o_dev] && !o_tp) {
+		if (!*o_tp_loc && !o_tp) {
 			o_tp = (struct termios *)
 				kmalloc(sizeof(struct termios), GFP_KERNEL);
 			if (!o_tp)
 				goto end_init;
-			initialize_termios(o_dev, o_tp);
+			*o_tp = driver->other->init_termios;
 			goto repeat;
 		}
-		if (!termios_locked[o_dev] && !o_ltp) {
+		if (!*o_ltp_loc && !o_ltp) {
 			o_ltp = (struct termios *)
 				kmalloc(sizeof(struct termios), GFP_KERNEL);
 			if (!o_ltp)
@@ -1294,39 +800,56 @@
 		
 	}
 	/* Now we have allocated all the structures: update all the pointers.. */
-	if (!tty_termios[dev]) {
-		tty_termios[dev] = tp;
+	if (!*tp_loc) {
+		*tp_loc = tp;
 		tp = NULL;
 	}
-	if (!tty_table[dev]) {
-		tty->termios = tty_termios[dev];
-		tty_table[dev] = tty;
-		tty = NULL;
-	}
-	if (!termios_locked[dev]) {
-		termios_locked[dev] = ltp;
+	if (!*ltp_loc) {
+		*ltp_loc = ltp;
 		ltp = NULL;
 	}
-	if (IS_A_PTY(dev)) {
-		if (!tty_termios[o_dev]) {
-			tty_termios[o_dev] = o_tp;
+	if (!*tty_loc) {
+		tty->termios = *tp_loc;
+		tty->termios_locked = *ltp_loc;
+		*tty_loc = tty;
+		(*driver->refcount)++;
+		if (tty->ldisc.open) {
+			retval = (tty->ldisc.open)(tty);
+			if (retval)
+				goto end_init;
+		}
+		tty = NULL;
+	}
+	if (driver->type == TTY_DRIVER_TYPE_PTY) {
+		if (!*o_tp_loc) {
+			*o_tp_loc = o_tp;
 			o_tp = NULL;
 		}
-		if (!termios_locked[o_dev]) {
-			termios_locked[o_dev] = o_ltp;
+		if (!*o_ltp_loc) {
+			*o_ltp_loc = o_ltp;
 			o_ltp = NULL;
 		}
-		if (!tty_table[o_dev]) {
-			o_tty->termios = tty_termios[o_dev];
-			tty_table[o_dev] = o_tty;
+		if (!*o_tty_loc) {
+			o_tty->termios = *o_tp_loc;
+			o_tty->termios_locked = *o_ltp_loc;
+			*o_tty_loc = o_tty;
+			(*driver->other->refcount)++;
+			if (o_tty->ldisc.open) {
+				retval = (o_tty->ldisc.open)(o_tty);
+				if (retval)
+					goto end_init;
+			}
 			o_tty = NULL;
 		}
-		tty_table[dev]->link = tty_table[o_dev];
-		tty_table[o_dev]->link = tty_table[dev];
+		(*tty_loc)->link = *o_tty_loc;
+		(*o_tty_loc)->link = *tty_loc;
 	}
-	tty_table[dev]->count++;
-	if (IS_A_PTY_MASTER(dev))
-		tty_table[o_dev]->count++;
+	(*tty_loc)->count++;
+	(*tty_loc)->driver = *driver;
+	if (driver->type == TTY_DRIVER_TYPE_PTY &&
+	    driver->subtype == PTY_TYPE_MASTER)
+		(*o_tty_loc)->count++;
+	*ret_tty = *tty_loc;
 	retval = 0;
 end_init:
 	if (tty)
@@ -1349,103 +872,169 @@
  * to be very careful that the structures are all released at the
  * same time, as interrupts might otherwise get the wrong pointers.
  */
-static void release_dev(int dev, struct file * filp)
+static void release_dev(struct file * filp)
 {
 	struct tty_struct *tty, *o_tty;
-	struct termios *tp, *o_tp;
+	struct termios *tp, *o_tp, *ltp, *o_ltp;
 	struct task_struct **p;
+	int	idx;
+	
 
-	tty = tty_table[dev];
-	tp = tty_termios[dev];
-	o_tty = NULL;
-	o_tp = NULL;
-	if (!tty) {
-		printk("release_dev: tty_table[%d] was NULL\n", dev);
+	tty = filp->private_data;
+	if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
+		return;
+
+	tty_fasync(filp->f_inode, filp, 0);
+
+	tp = tty->termios;
+	ltp = tty->termios_locked;
+
+	idx = MINOR(tty->device) - tty->driver.minor_start;
+#ifdef TTY_PARANOIA_CHECK
+	if (idx < 0 || idx >= tty->driver.num) {
+		printk("release_dev: bad idx when trying to free (%d, %d)\n",
+		       MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (tty != tty->driver.table[idx]) {
+		printk("release_dev: driver.table[%d] not tty for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (tp != tty->driver.termios[idx]) {
+		printk("release_dev: driver.termios[%d] not termios for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
 		return;
 	}
-	if (!tp) {
-		printk("release_dev: tty_termios[%d] was NULL\n", dev);
+	if (ltp != tty->driver.termios_locked[idx]) {
+		printk("release_dev: driver.termios_locked[%d] not termios_locked for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
 		return;
 	}
+#endif
+
 #ifdef TTY_DEBUG_HANGUP
-	printk("release_dev of tty%d (tty count=%d)...", dev, tty->count);
+	printk("release_dev of %s (tty count=%d)...", tty_name(tty),
+	       tty->count);
 #endif
-	if (IS_A_PTY(dev)) {
-		o_tty = tty_table[PTY_OTHER(dev)];
-		o_tp = tty_termios[PTY_OTHER(dev)];
-		if (!o_tty) {
-			printk("release_dev: pty pair(%d) was NULL\n", dev);
+
+	o_tty = tty->link;
+	o_tp = (o_tty) ? o_tty->termios : NULL;
+	o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
+
+#ifdef TTY_PARANOIA_CHECK
+	if (tty->driver.other) {
+		if (o_tty != tty->driver.other->table[idx]) {
+			printk("release_dev: other->table[%d] not o_tty for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
 			return;
 		}
-		if (!o_tp) {
-			printk("release_dev: pty pair(%d) termios was NULL\n", dev);
+		if (o_tp != tty->driver.other->termios[idx]) {
+			printk("release_dev: other->termios[%d] not o_termios for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
 			return;
 		}
-		if (tty->link != o_tty || o_tty->link != tty) {
+		if (o_ltp != tty->driver.other->termios_locked[idx]) {
+			printk("release_dev: other->termios_locked[%d] not o_termios_locked for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
+			return;
+		}
+
+		if (o_tty->link != tty) {
 			printk("release_dev: bad pty pointers\n");
 			return;
 		}
 	}
-	tty->write_data_cnt = 0; /* Clear out pending trash */
-	if (tty->close)
-		tty->close(tty, filp);
-	if (IS_A_PTY_MASTER(dev)) {
+#endif
+	
+	if (tty->driver.close)
+		tty->driver.close(tty, filp);
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER) {
 		if (--tty->link->count < 0) {
-			printk("release_dev: bad tty slave count (dev = %d): %d\n",
-			       dev, tty->count);
+			printk("release_dev: bad pty slave count (%d) for %s\n",
+			       tty->count, tty_name(tty));
 			tty->link->count = 0;
 		}
 	}
 	if (--tty->count < 0) {
-		printk("release_dev: bad tty_table[%d]->count: %d\n",
-		       dev, tty->count);
+		printk("release_dev: bad tty->count (%d) for %s\n",
+		       tty->count, tty_name(tty));
 		tty->count = 0;
 	}
 	if (tty->count)
 		return;
 	
-#ifdef TTY_DEBUG_HANGUP
-	printk("freeing tty structure...");
-#endif
-
 	/*
 	 * Make sure there aren't any processes that still think this
 	 * tty is their controlling tty.
 	 */
 	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
-		if ((*p) && (*p)->tty == tty->line)
-		(*p)->tty = -1;
+		if ((*p) && (*p)->tty == tty)
+		(*p)->tty = NULL;
+	}
+
+	if (o_tty) {
+		if (o_tty->count)
+			return;
+		tty->driver.other->table[idx] = NULL;
+		tty->driver.other->termios[idx] = NULL;
+		tty->driver.other->termios_locked[idx] = NULL;
 	}
+	
+#ifdef TTY_DEBUG_HANGUP
+	printk("freeing tty structure...");
+#endif
 
 	/*
 	 * Shutdown the current line discipline, and reset it to
 	 * N_TTY.
 	 */
-	if (ldiscs[tty->disc].close != NULL)
-		ldiscs[tty->disc].close(tty);
-	tty->disc = N_TTY;
+	if (tty->ldisc.close)
+		(tty->ldisc.close)(tty);
+	tty->ldisc = ldiscs[N_TTY];
 	tty->termios->c_line = N_TTY;
 	
-	if (o_tty) {
-		if (o_tty->count)
-			return;
-		else {
-			tty_table[PTY_OTHER(dev)] = NULL;
-			tty_termios[PTY_OTHER(dev)] = NULL;
-		}
-	}
-	tty_table[dev] = NULL;
-	if (IS_A_PTY(dev)) {
-		tty_termios[dev] = NULL;
+	tty->driver.table[idx] = NULL;
+	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+		tty->driver.termios[idx] = NULL;
+		tty->driver.termios_locked[idx] = NULL;
 		kfree_s(tp, sizeof(struct termios));
+		kfree_s(ltp, sizeof(struct termios));
 	}
 	if (tty == redirect || o_tty == redirect)
 		redirect = NULL;
+	/*
+	 * Make sure that the tty's task queue isn't activated.  If it
+	 * is, take it out of the linked list.
+	 */
+	cli();
+	if (tty->flip.tqueue.sync) {
+		struct tq_struct *tq, *prev;
+
+		for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+			if (tq == &tty->flip.tqueue) {
+				if (prev)
+					prev->next = tq->next;
+				else
+					tq_timer = tq->next;
+				break;
+			}
+		}
+	}
+	sti();
+	tty->magic = 0;
+	(*tty->driver.refcount)--;
 	free_page((unsigned long) tty);
-	if (o_tty)
+	if (o_tty) {
+		o_tty->magic = 0;
+		(*o_tty->driver.refcount)--;
 		free_page((unsigned long) o_tty);
+	}
 	if (o_tp)
 		kfree_s(o_tp, sizeof(struct termios));
+	if (o_ltp)
+		kfree_s(o_ltp, sizeof(struct termios));
 }
 
 /*
@@ -1463,87 +1052,67 @@
 static int tty_open(struct inode * inode, struct file * filp)
 {
 	struct tty_struct *tty;
-	int major, minor;
+	int minor;
 	int noctty, retval;
+	dev_t device;
 
 retry_open:
-	minor = MINOR(inode->i_rdev);
-	major = MAJOR(inode->i_rdev);
 	noctty = filp->f_flags & O_NOCTTY;
-	if (major == TTYAUX_MAJOR) {
-		if (!minor) {
-			major = TTY_MAJOR;
-			minor = current->tty;
-		}
+	device = inode->i_rdev;
+	if (device == TTY_DEV) {
+		if (!current->tty)
+			return -ENXIO;
+		device = current->tty->device;
 		/* noctty = 1; */
-	} else if (major == TTY_MAJOR) {
-		if (!minor) {
-			minor = fg_console + 1;
-			noctty = 1;
-		}
-	} else {
-		printk("Bad major #%d in tty_open\n", MAJOR(inode->i_rdev));
-		return -ENODEV;
 	}
-	if (minor <= 0)
-		return -ENXIO;
-	if (IS_A_PTY_MASTER(minor))
+	if (device == CONSOLE_DEV) {
+		device = MKDEV(TTY_MAJOR, fg_console+1);
 		noctty = 1;
-	filp->f_rdev = (major << 8) | minor;
-	retval = init_dev(minor);
+	}
+	minor = MINOR(device);
+	
+	retval = init_dev(device, &tty);
+	filp->private_data = tty;
 	if (retval)
 		return retval;
-	tty = tty_table[minor];
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER)
+		noctty = 1;
 #ifdef TTY_DEBUG_HANGUP
-	printk("opening tty%d...", tty->line);
+	printk("opening %s...", tty_name(tty));
 #endif
 	if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
-		return -EBUSY;
-
-#if 0
-	/* clean up the packet stuff. */
-	/*
-	 *  Why is this not done in init_dev?  Right here, if another 
-	 * process opens up a tty in packet mode, all the packet 
-	 * variables get cleared.  Come to think of it, is anything 
-	 * using the packet mode at all???  - Ted, 1/27/93
-	 *
-	 * Not to worry, a pty master can only be opened once.
-	 * And rlogind and telnetd both use packet mode.  -- jrs
-	 *
-	 * Not needed.  These are cleared in initialize_tty_struct. -- jlc
-	 */
-	tty->ctrl_status = 0;
-	tty->packet = 0;
-#endif
-
-	if (tty->open) {
-		retval = tty->open(tty, filp);
-	} else {
+		retval = -EBUSY;
+	else if (tty->driver.open)
+		retval = tty->driver.open(tty, filp);
+	else
 		retval = -ENODEV;
-	}
+
 	if (retval) {
 #ifdef TTY_DEBUG_HANGUP
-		printk("error %d in opening tty%d...", retval, tty->line);
+		printk("error %d in opening %s...", retval, tty_name(tty));
 #endif
 
-		release_dev(minor, filp);
+		release_dev(filp);
 		if (retval != -ERESTARTSYS)
 			return retval;
 		if (current->signal & ~current->blocked)
 			return retval;
 		schedule();
+		/*
+		 * Need to reset f_op in case a hangup happened.
+		 */
+		filp->f_op = &tty_fops;
 		goto retry_open;
 	}
 	if (!noctty &&
 	    current->leader &&
-	    current->tty<0 &&
-	    tty->session==0) {
-		current->tty = minor;
+	    !current->tty &&
+	    tty->session == 0) {
+		current->tty = tty;
 		tty->session = current->session;
 		tty->pgrp = current->pgrp;
 	}
-	filp->f_rdev = MKDEV(TTY_MAJOR,minor); /* Set it to something normal */
 	return 0;
 }
 
@@ -1554,69 +1123,285 @@
  */
 static void tty_release(struct inode * inode, struct file * filp)
 {
-	int dev;
-
-	dev = filp->f_rdev;
-	if (MAJOR(dev) != TTY_MAJOR) {
-		printk("tty_release: tty pseudo-major != TTY_MAJOR\n");
-		return;
-	}
-	dev = MINOR(filp->f_rdev);
-	if (!dev) {
-		printk("tty_release: bad f_rdev\n");
-		return;
-	}
-	release_dev(dev, filp);
+	release_dev(filp);
 }
 
 static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
 {
-	int dev;
 	struct tty_struct * tty;
 
-	dev = filp->f_rdev;
-	if (MAJOR(dev) != TTY_MAJOR) {
-		printk("tty_select: tty pseudo-major != TTY_MAJOR\n");
-		return 0;
-	}
-	dev = MINOR(filp->f_rdev);
-	tty = TTY_TABLE(dev);
-	if (!tty) {
-		printk("tty_select: tty struct for dev %d was NULL\n", dev);
+	tty = filp->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_select"))
 		return 0;
-	}
-	if (ldiscs[tty->disc].select)
-		return (ldiscs[tty->disc].select)(tty, inode, filp,
-						  sel_type, wait);
+
+	if (tty->ldisc.select)
+		return (tty->ldisc.select)(tty, inode, filp, sel_type, wait);
 	return 0;
 }
 
-static int normal_select(struct tty_struct * tty, struct inode * inode,
-			 struct file * file, int sel_type, select_table *wait)
+static int tty_fasync(struct inode * inode, struct file * filp, int on)
 {
-	switch (sel_type) {
-		case SEL_IN:
-			if (input_available_p(tty))
-				return 1;
-			/* fall through */
-		case SEL_EX:
-			if (tty->packet && tty->link->ctrl_status)
-				return 1;
-			if (tty->flags & (1 << TTY_SLAVE_CLOSED))
-				return 1;
-			if (tty_hung_up_p(file))
-				return 1;
-			select_wait(&tty->secondary.proc_list, wait);
+	struct tty_struct * tty;
+	struct fasync_struct *fa, *prev;
+
+	tty = filp->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
+		return 0;
+
+	for (fa = tty->fasync, prev = 0; fa; prev= fa, fa = fa->fa_next) {
+		if (fa->fa_file == filp)
+			break;
+	}
+
+	if (on) {
+		if (fa)
 			return 0;
-		case SEL_OUT:
-			if (LEFT(&tty->write_q) > WAKEUP_CHARS)
-				return 1;
-			select_wait(&tty->write_q.proc_list, wait);
+		fa = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
+		if (!fa)
+			return -ENOMEM;
+		fa->magic = FASYNC_MAGIC;
+		fa->fa_file = filp;
+		fa->fa_next = tty->fasync;
+		tty->fasync = fa;
+		if (!tty->read_wait)
+			tty->minimum_to_wake = 1;
+	} else {
+		if (!fa)
 			return 0;
+		if (prev)
+			prev->fa_next = fa->fa_next;
+		else
+			tty->fasync = fa->fa_next;
+		kfree_s(fa, sizeof(struct fasync_struct));
+		if (!tty->fasync && !tty->read_wait)
+			tty->minimum_to_wake = N_TTY_BUF_SIZE;
 	}
-	return 0;
+	return 0;	
+}
+
+/*
+ * XXX does anyone use this anymore?!?
+ */
+static int do_get_ps_info(int arg)
+{
+	struct tstruct {
+		int flag;
+		int present[NR_TASKS];
+		struct task_struct tasks[NR_TASKS];
+	};
+	struct tstruct *ts = (struct tstruct *)arg;
+	struct task_struct **p;
+	char *c, *d;
+	int i, n = 0;
+	
+	i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
+	if (i)
+		return i;
+	for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
+		if (*p)
+		{
+			c = (char *)(*p);
+			d = (char *)(ts->tasks+n);
+			for (i=0 ; i<sizeof(struct task_struct) ; i++)
+				put_fs_byte(*c++, d++);
+			put_fs_long(1, (unsigned long *)(ts->present+n));
+		}
+		else	
+			put_fs_long(0, (unsigned long *)(ts->present+n));
+	return(0);			
+}
+
+static int tty_ioctl(struct inode * inode, struct file * file,
+		     unsigned int cmd, unsigned long arg)
+{
+	int	retval;
+	struct tty_struct * tty;
+	struct tty_struct * real_tty;
+	struct winsize tmp_ws;
+	pid_t pgrp;
+	unsigned char	ch;
+	char	mbz = 0;
+	
+	tty = file->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
+		return -EINVAL;
+
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER)
+		real_tty = tty->link;
+	else
+		real_tty = tty;
+
+	switch (cmd) {
+		case TIOCSTI:
+			if ((current->tty != tty) && !suser())
+				return -EPERM;
+			ch = get_fs_byte((char *) arg);
+			tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
+			return 0;
+		case TIOCGWINSZ:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (struct winsize));
+			if (retval)
+				return retval;
+			memcpy_tofs((struct winsize *) arg, &tty->winsize,
+				    sizeof (struct winsize));
+			return 0;
+		case TIOCSWINSZ:
+			memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
+				      sizeof (struct winsize));
+			if (memcmp(&tmp_ws, &tty->winsize,
+				   sizeof(struct winsize))) {
+				if (tty->pgrp > 0)
+					kill_pg(tty->pgrp, SIGWINCH, 1);
+				if ((real_tty->pgrp != tty->pgrp) &&
+				    (real_tty->pgrp > 0))
+					kill_pg(real_tty->pgrp, SIGWINCH, 1);
+			}
+			tty->winsize = tmp_ws;
+			real_tty->winsize = tmp_ws;
+			return 0;
+		case TIOCCONS:
+			if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
+				if (!suser())
+					return -EPERM;
+				redirect = NULL;
+				return 0;
+			}
+			if (redirect)
+				return -EBUSY;
+			redirect = real_tty;
+			return 0;
+		case FIONBIO:
+			arg = get_fs_long((unsigned long *) arg);
+			if (arg)
+				file->f_flags |= O_NONBLOCK;
+			else
+				file->f_flags &= ~O_NONBLOCK;
+			return 0;
+		case TIOCEXCL:
+			set_bit(TTY_EXCLUSIVE, &tty->flags);
+			return 0;
+		case TIOCNXCL:
+			clear_bit(TTY_EXCLUSIVE, &tty->flags);
+			return 0;
+		case TIOCNOTTY:
+			if (current->tty != tty)
+				return -ENOTTY;
+			if (current->leader)
+				disassociate_ctty(0);
+			current->tty = NULL;
+			return 0;
+		case TIOCSCTTY:
+			if (current->leader &&
+			    (current->session == tty->session))
+				return 0;
+			/*
+			 * The process must be a session leader and
+			 * not have a controlling tty already.
+			 */
+			if (!current->leader || current->tty)
+				return -EPERM;
+			if (tty->session > 0) {
+				/*
+				 * This tty is already the controlling
+				 * tty for another session group!
+				 */
+				if ((arg == 1) && suser()) {
+					/*
+					 * Steal it away
+					 */
+					struct task_struct *p;
+
+					for_each_task(p)
+						if (p->tty == tty)
+							p->tty = NULL;
+				} else
+					return -EPERM;
+			}
+			current->tty = tty;
+			tty->session = current->session;
+			tty->pgrp = current->pgrp;
+			return 0;
+		case TIOCGPGRP:
+			/*
+			 * (tty == real_tty) is a cheap way of
+			 * testing if the tty is NOT a master pty.
+			 */
+			if (tty == real_tty && current->tty != real_tty)
+				return -ENOTTY;
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (pid_t));
+			if (retval)
+				return retval;
+			put_fs_long(real_tty->pgrp, (pid_t *) arg);
+			return 0;
+		case TIOCSPGRP:
+			retval = tty_check_change(real_tty);
+			if (retval)
+				return retval;
+			if (!current->tty ||
+			    (current->tty != real_tty) ||
+			    (real_tty->session != current->session))
+				return -ENOTTY;
+			pgrp = get_fs_long((pid_t *) arg);
+			if (pgrp < 0)
+				return -EINVAL;
+			if (session_of_pgrp(pgrp) != current->session)
+				return -EPERM;
+			real_tty->pgrp = pgrp;
+			return 0;
+		case TIOCGETD:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (unsigned long));
+			if (retval)
+				return retval;
+			put_fs_long(tty->ldisc.num, (unsigned long *) arg);
+			return 0;
+		case TIOCSETD:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			arg = get_fs_long((unsigned long *) arg);
+			return tty_set_ldisc(tty, arg);
+		case TIOCLINUX:
+			switch (get_fs_byte((char *)arg))
+			{
+				case 0: 
+					return do_screendump(arg);
+				case 1:
+					printk("Deprecated TIOCLINUX (1) ioctl\n");
+					return do_get_ps_info(arg);
+#ifdef CONFIG_SELECTION
+				case 2:
+					return set_selection(arg);
+				case 3:
+					return paste_selection(tty);
+				case 4:
+					unblank_screen();
+					return 0;
+#endif /* CONFIG_SELECTION */
+				default: 
+					return -EINVAL;
+			}
+		default:
+			if (tty->driver.ioctl) {
+				retval = (tty->driver.ioctl)(tty, file,
+							     cmd, arg);
+				if (retval != -ENOIOCTLCMD)
+					return retval;
+			}
+			if (tty->ldisc.ioctl) {
+				retval = (tty->ldisc.ioctl)(tty, file,
+							    cmd, arg);
+				if (retval != -ENOIOCTLCMD)
+					return retval;
+			}
+			return -EINVAL;
+		}
 }
 
+
 /*
  * This implements the "Secure Attention Key" ---  the idea is to
  * prevent trojan horses by killing all processes associated with this
@@ -1635,24 +1420,25 @@
 	tty_hangup(tty);
 #else
 	struct task_struct **p;
-	int line = tty->line;
 	int session = tty->session;
 	int		i;
 	struct file	*filp;
 	
-	flush_input(tty);
-	flush_output(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
  	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
 		if (!(*p))
 			continue;
-		if (((*p)->tty == line) ||
+		if (((*p)->tty == tty) ||
 		    ((session > 0) && ((*p)->session == session)))
 			send_sig(SIGKILL, *p, 1);
 		else {
 			for (i=0; i < NR_OPEN; i++) {
 				filp = (*p)->files->fd[i];
 				if (filp && (filp->f_op == &tty_fops) &&
-				    (MINOR(filp->f_rdev) == line)) {
+				    (filp->private_data == tty)) {
 					send_sig(SIGKILL, *p, 1);
 					break;
 				}
@@ -1663,183 +1449,119 @@
 }
 
 /*
- * This routine allows a kernel routine to send a large chunk of data
- * to a particular tty; if all of the data can be queued up for ouput
- * immediately, tty_write_data() will return 0.  If, however, not all
- * of the data can be immediately queued for delivery, the number of
- * bytes left to be queued up will be returned, and the rest of the
- * data will be queued up when there is room.  The callback function
- * will be called (with the argument callarg) when the last of the
- * data is finally in the queue.
- *
- * Note that the callback routine will _not_ be called if all of the
- * data could be queued immediately.  This is to avoid a problem with
- * the kernel stack getting too deep, which might happen if the
- * callback routine calls tty_write_data with itself as an argument.
+ * This routine is called out of the software interrupt to flush data
+ * from the flip buffer to the line discpline.
  */
-int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
-		    void (*callback)(void * data), void * callarg)
+static void flush_to_ldisc(void *private)
 {
-	int head, tail, count;
-	unsigned long flags;
-	char *p;
-
-#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
-
-	save_flags(flags);
-	cli();
-	if (tty->write_data_cnt) {
-		restore_flags(flags);
-		return -EBUSY;
-	}
-
-	head = tty->write_q.head;
-	tail = tty->write_q.tail;
-	count = buflen;
-	p = bufp;
-
-	while (count && VLEFT > 0) {
-		tty->write_q.buf[head++] = *p++;
-		head &= TTY_BUF_SIZE-1;
-		count--;
-	}
-	tty->write_q.head = head;
-	if (count) {
-		tty->write_data_cnt = count;
-		tty->write_data_ptr = (unsigned char *) p;
-		tty->write_data_callback = callback;
-		tty->write_data_arg = callarg;
-	}
-	restore_flags(flags);
-	tty->write(tty);
-	return count;
+	struct tty_struct *tty = private;
+	unsigned char	*cp;
+	char		*fp;
+	int		count;
+
+	if (tty->flip.buf_num) {
+		cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+		fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
+		tty->flip.buf_num = 0;
+
+		cli();
+		tty->flip.char_buf_ptr = tty->flip.char_buf;
+		tty->flip.flag_buf_ptr = tty->flip.flag_buf;
+	} else {
+		cp = tty->flip.char_buf;
+		fp = tty->flip.flag_buf;
+		tty->flip.buf_num = 1;
+
+		cli();
+		tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+		tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
+	}
+	count = tty->flip.count;
+	tty->flip.count = 0;
+	sti();
+	
+#if 0
+	if (count > tty->max_flip_cnt)
+		tty->max_flip_cnt = count;
+#endif
+	tty->ldisc.receive_buf(tty, cp, fp, count);
 }
 
 /*
- * This routine routine is called after an interrupt has drained a
- * tty's write queue, so that there is more space for data waiting to
- * be sent in tty->write_data_ptr.
- *
- * tty_check_write[8] is a bitstring which indicates which ttys
- * needs to be processed.
+ * This subroutine initializes a tty structure.
  */
-void tty_bh_routine(void * unused)
+static void initialize_tty_struct(struct tty_struct *tty)
 {
-	int	i, j, line, mask;
-	int	head, tail, count;
-	unsigned char * p;
-	struct tty_struct * tty;
-
-	for (i = 0, line = 0; i < MAX_TTYS / 32; i++) {
-		if (!tty_check_write[i]) {
-			line += 32;
-			continue;
-		}
-		for (j=0, mask=0; j < 32; j++, line++, mask <<= 1) {
-			if (clear_bit(j, &tty_check_write[i])) {
-				tty = tty_table[line];
-				if (!tty || !tty->write_data_cnt)
-					continue;
-				cli();
-				head = tty->write_q.head;
-				tail = tty->write_q.tail;
-				count = tty->write_data_cnt;
-				p = tty->write_data_ptr;
-
-				while (count && VLEFT > 0) {
-					tty->write_q.buf[head++] = *p++;
-					head &= TTY_BUF_SIZE-1;
-					count--;
-				}
-				tty->write_q.head = head;
-				tty->write_data_ptr = p;
-				tty->write_data_cnt = count;
-				sti();
-				if (!count)
-					(tty->write_data_callback)
-						(tty->write_data_arg);
-			}
-		}
-	}
-	
+	memset(tty, 0, sizeof(struct tty_struct));
+	tty->magic = TTY_MAGIC;
+	tty->ldisc = ldiscs[N_TTY];
+	tty->pgrp = -1;
+	tty->flip.char_buf_ptr = tty->flip.char_buf;
+	tty->flip.flag_buf_ptr = tty->flip.flag_buf;
+	tty->flip.tqueue.routine = flush_to_ldisc;
+	tty->flip.tqueue.data = tty;
 }
 
 /*
- * This subroutine initializes a tty structure.  We have to set up
- * things correctly for each different type of tty.
+ * The default put_char routine if the driver did not define one.
  */
-static void initialize_tty_struct(int line, struct tty_struct *tty)
+void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
 {
-	memset(tty, 0, sizeof(struct tty_struct));
-	tty->line = line;
-	tty->disc = N_TTY;
-	tty->pgrp = -1;
-	if (IS_A_CONSOLE(line)) {
-		tty->open = con_open;
-		tty->winsize.ws_row = video_num_lines;
-		tty->winsize.ws_col = video_num_columns;
-	} else if IS_A_SERIAL(line) {
-		tty->open = rs_open;
-	} else if IS_A_PTY(line) {
-		tty->open = pty_open;
-	}
+	tty->driver.write(tty, 0, &ch, 1);
 }
 
-static void initialize_termios(int line, struct termios * tp)
-{
-	memset(tp, 0, sizeof(struct termios));
-	memcpy(tp->c_cc, INIT_C_CC, NCCS);
-	if (IS_A_CONSOLE(line) || IS_A_PTY_SLAVE(line)) {
-		tp->c_iflag = ICRNL | IXON;
-		tp->c_oflag = OPOST | ONLCR;
-		tp->c_cflag = B38400 | CS8 | CREAD;
-		tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
-			ECHOCTL | ECHOKE | IEXTEN;
-	} else if (IS_A_SERIAL(line)) {
-		tp->c_iflag = ICRNL | IXON;
-		tp->c_oflag = OPOST | ONLCR | XTABS;
-		tp->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-		tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
-			ECHOCTL | ECHOKE | IEXTEN;
-	} else if (IS_A_PTY_MASTER(line))
-		tp->c_cflag = B9600 | CS8 | CREAD;
-}
-
-static struct tty_ldisc tty_ldisc_N_TTY = {
-	0,			/* flags */
-	NULL,			/* open */
-	NULL,			/* close */
-	read_chan,		/* read */
-	write_chan,		/* write */
-	NULL,			/* ioctl */
-	normal_select,		/* select */
-	copy_to_cooked		/* handler */
-};
+/*
+ * Called by a tty driver to register itself.
+ */
+int tty_register_driver(struct tty_driver *driver)
+{
+	if (driver->flags & TTY_DRIVER_INSTALLED)
+		return 0;
+	/*
+	 * XXX need to check to see if major device already
+	 * registered, and then handle error checking.
+	 */
+	(void) register_chrdev(driver->major, driver->name, &tty_fops);
 
+	if (!driver->put_char)
+		driver->put_char = tty_default_put_char;
 	
+	driver->prev = 0;
+	driver->next = tty_drivers;
+	tty_drivers->prev = driver;
+	tty_drivers = driver;
+	return 0;
+}
+
 long tty_init(long kmem_start)
 {
-	int i;
-
 	if (sizeof(struct tty_struct) > PAGE_SIZE)
 		panic("size of tty structure > PAGE_SIZE!");
 	if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
 		panic("unable to get major %d for tty device", TTY_MAJOR);
 	if (register_chrdev(TTYAUX_MAJOR,"tty",&tty_fops))
 		panic("unable to get major %d for tty device", TTYAUX_MAJOR);
-	for (i=0 ; i< MAX_TTYS ; i++) {
-		tty_table[i] =  0;
-		tty_termios[i] = 0;
-	}
-	memset(tty_check_write, 0, sizeof(tty_check_write));
-	bh_base[TTY_BH].routine = tty_bh_routine;
+	tty_drivers = 0;
 
 	/* Setup the default TTY line discipline. */
 	memset(ldiscs, 0, sizeof(ldiscs));
 	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
 
-	kmem_start = kbd_init(kmem_start);
+	/*
+	 * Set up the standard termios.  Individual tty drivers may 
+	 * deviate from this; this is used as a template.
+	 */
+	memset(&tty_std_termios, 0, sizeof(struct termios));
+	memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
+	tty_std_termios.c_iflag = ICRNL | IXON;
+	tty_std_termios.c_oflag = OPOST | ONLCR;
+	tty_std_termios.c_cflag = B38400 | CS8 | CREAD;
+	tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
+		ECHOCTL | ECHOKE | IEXTEN;
+	
 	kmem_start = con_init(kmem_start);
+	kmem_start = kbd_init(kmem_start);
 	kmem_start = rs_init(kmem_start);
+	kmem_start = pty_init(kmem_start);
 	return kmem_start;
 }
diff -u --recursive --new-file v1.1.12/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v1.1.12/linux/drivers/char/tty_ioctl.c	Sat Mar 26 15:13:09 1994
+++ linux/drivers/char/tty_ioctl.c	Mon May 23 07:55:14 1994
@@ -32,64 +32,16 @@
 #endif
 
 extern int session_of_pgrp(int pgrp);
-extern int do_screendump(int arg);
 extern int kill_pg(int pgrp, int sig, int priv);
 
-#ifdef CONFIG_SELECTION
-extern int set_selection(const int arg);
-extern int paste_selection(struct tty_struct *tty);
-#endif /* CONFIG_SELECTION */
-
-static int tty_set_ldisc(struct tty_struct *tty, int ldisc);
-
-void flush_input(struct tty_struct * tty)
-{
-	cli();
-	tty->read_q.head = tty->read_q.tail = 0;
-	tty->secondary.head = tty->secondary.tail = 0;
-	tty->canon_head = tty->canon_data = tty->erasing = 0;
-	memset(&tty->readq_flags, 0, sizeof tty->readq_flags);
-	memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
-	sti();
-	if (!tty->link)
-		return;
-	/* No cli() since ptys don't use interrupts. */
-	tty->link->write_q.head = tty->link->write_q.tail = 0;
-	wake_up_interruptible(&tty->link->write_q.proc_list);
-	if (tty->link->packet) {
-		tty->ctrl_status |= TIOCPKT_FLUSHREAD;
-		wake_up_interruptible(&tty->link->secondary.proc_list);
-	}
-}
-
-void flush_output(struct tty_struct * tty)
-{
-	cli();
-	tty->write_q.head = tty->write_q.tail = 0;
-	sti();
-	wake_up_interruptible(&tty->write_q.proc_list);
-	if (!tty->link)
-		return;
-	/* No cli() since ptys don't use interrupts. */
-	tty->link->read_q.head = tty->link->read_q.tail = 0;
-	tty->link->secondary.head = tty->link->secondary.tail = 0;
-	tty->link->canon_head = tty->link->canon_data = tty->link->erasing = 0;
-	memset(&tty->link->readq_flags, 0, sizeof tty->readq_flags);
-	memset(&tty->link->secondary_flags, 0, sizeof tty->secondary_flags);
-	if (tty->link->packet) {
-		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
-		wake_up_interruptible(&tty->link->secondary.proc_list);
-	}
-}
-
 void wait_until_sent(struct tty_struct * tty, int timeout)
 {
 	struct wait_queue wait = { current, NULL };
 
-	TTY_WRITE_FLUSH(tty);
-	if (EMPTY(&tty->write_q))
+	if (!tty->driver.chars_in_buffer ||
+	    !tty->driver.chars_in_buffer(tty))
 		return;
-	add_wait_queue(&tty->write_q.proc_list, &wait);
+	add_wait_queue(&tty->write_wait, &wait);
 	current->counter = 0;	/* make us low-priority */
 	if (timeout)
 		current->timeout = timeout + jiffies;
@@ -99,42 +51,12 @@
 		current->state = TASK_INTERRUPTIBLE;
 		if (current->signal & ~current->blocked)
 			break;
-		TTY_WRITE_FLUSH(tty);
-		if (EMPTY(&tty->write_q))
+		if (!tty->driver.chars_in_buffer(tty))
 			break;
 		schedule();
 	} while (current->timeout);
 	current->state = TASK_RUNNING;
-	remove_wait_queue(&tty->write_q.proc_list, &wait);
-}
-
-static int do_get_ps_info(int arg)
-{
-	struct tstruct {
-		int flag;
-		int present[NR_TASKS];
-		struct task_struct tasks[NR_TASKS];
-	};
-	struct tstruct *ts = (struct tstruct *)arg;
-	struct task_struct **p;
-	char *c, *d;
-	int i, n = 0;
-	
-	i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
-	if (i)
-		return i;
-	for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
-		if (*p)
-		{
-			c = (char *)(*p);
-			d = (char *)(ts->tasks+n);
-			for (i=0 ; i<sizeof(struct task_struct) ; i++)
-				put_fs_byte(*c++, d++);
-			put_fs_long(1, (unsigned long *)(ts->present+n));
-		}
-		else	
-			put_fs_long(0, (unsigned long *)(ts->present+n));
-	return(0);			
+	remove_wait_queue(&tty->write_wait, &wait);
 }
 
 static void unset_locked_termios(struct termios *termios,
@@ -160,45 +82,25 @@
 			old->c_cc[i] : termios->c_cc[i];
 }
 
-int check_change(struct tty_struct * tty, int channel)
-{
-	/* If we try to set the state of terminal and we're not in the
-	   foreground, send a SIGTTOU.  If the signal is blocked or
-	   ignored, go ahead and perform the operation.  POSIX 7.2) */
-	if (current->tty != channel)
-		return 0;
-	if (tty->pgrp <= 0) {
-		printk("check_change: tty->pgrp <= 0!\n");
-		return 0;
-	}
-	if (current->pgrp == tty->pgrp)
-		return 0;
-	if (is_ignored(SIGTTOU))
-		return 0;
-	if (is_orphaned_pgrp(current->pgrp))
-		return -EIO;
-	(void) kill_pg(current->pgrp,SIGTTOU,1);
-	return -ERESTARTSYS;
-}
-
 static int set_termios_2(struct tty_struct * tty, struct termios * termios)
 {
 	struct termios old_termios = *tty->termios;
 	int canon_change;
 
-	canon_change = (old_termios.c_lflag ^ termios->c_lflag) & ICANON;
 	cli();
 	*tty->termios = *termios;
+	unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
+	canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
 	if (canon_change) {
-		memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
-		tty->canon_head = tty->secondary.tail;
+		memset(&tty->read_flags, 0, sizeof tty->read_flags);
+		tty->canon_head = tty->read_tail;
 		tty->canon_data = 0;
 		tty->erasing = 0;
 	}
 	sti();
-	if (canon_change && !L_ICANON(tty) && !EMPTY(&tty->secondary))
+	if (canon_change && !L_ICANON(tty) && tty->read_cnt)
 		/* Get characters left over from canonical mode. */
-		wake_up_interruptible(&tty->secondary.proc_list);
+		wake_up_interruptible(&tty->read_wait);
 
 	/* see if packet mode change of state */
 
@@ -215,21 +117,20 @@
 				tty->ctrl_status |= TIOCPKT_DOSTOP;
 			else
 				tty->ctrl_status |= TIOCPKT_NOSTOP;
-			wake_up_interruptible(&tty->link->secondary.proc_list);
+			wake_up_interruptible(&tty->link->read_wait);
 		}
 	}
 
-	unset_locked_termios(tty->termios, &old_termios,
-			     termios_locked[tty->line]);
+	if (tty->driver.set_termios)
+		(*tty->driver.set_termios)(tty, &old_termios);
 
-	if (tty->set_termios)
-		(*tty->set_termios)(tty, &old_termios);
-
+	if (tty->ldisc.set_termios)
+		(*tty->ldisc.set_termios)(tty, &old_termios);
+		
 	return 0;
 }
 
-static int set_termios(struct tty_struct * tty, struct termios * termios,
-		       int channel)
+static int set_termios(struct tty_struct * tty, struct termios * termios)
 {
 	struct termios tmp_termios;
 
@@ -256,8 +157,7 @@
 	return 0;
 }
 
-static int set_termio(struct tty_struct * tty, struct termio * termio,
-		      int channel)
+static int set_termio(struct tty_struct * tty, struct termio * termio)
 {
 	struct termio tmp_termio;
 	struct termios tmp_termios;
@@ -278,92 +178,37 @@
 	return set_termios_2(tty, &tmp_termios);
 }
 
-static int set_window_size(struct tty_struct * tty, struct winsize * ws)
-{
-	struct winsize tmp_ws;
-
-	memcpy_fromfs(&tmp_ws, ws, sizeof (struct winsize));
-	if (memcmp(&tmp_ws, &tty->winsize, sizeof (struct winsize)) &&
-	    tty->pgrp > 0)
-		kill_pg(tty->pgrp, SIGWINCH, 1);
-	tty->winsize = tmp_ws;
-	return 0;
-}
-
-/* Set the discipline of a tty line. */
-static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
-{
-	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
-	    !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
-		return -EINVAL;
-
-	if (tty->disc == ldisc)
-		return 0;	/* We are already in the desired discipline */
-
-	/* Shutdown the current discipline. */
-	wait_until_sent(tty, 0);
-	flush_input(tty);
-	if (ldiscs[tty->disc].close)
-		ldiscs[tty->disc].close(tty);
-
-	/* Now set up the new line discipline. */
-	tty->disc = ldisc;
-	tty->termios->c_line = ldisc;
-	if (ldiscs[tty->disc].open)
-		return(ldiscs[tty->disc].open(tty));
-	else
-		return 0;
-}
-
 static unsigned long inq_canon(struct tty_struct * tty)
 {
 	int nr, head, tail;
 
-	if (!tty->canon_data)
+	if (!tty->canon_data || !tty->read_buf)
 		return 0;
 	head = tty->canon_head;
-	tail = tty->secondary.tail;
-	nr = (head - tail) & (TTY_BUF_SIZE-1);
+	tail = tty->read_tail;
+	nr = (head - tail) & (N_TTY_BUF_SIZE-1);
 	/* Skip EOF-chars.. */
 	while (head != tail) {
-		if (test_bit(tail, &tty->secondary_flags) &&
-		    tty->secondary.buf[tail] == __DISABLED_CHAR)
+		if (test_bit(tail, &tty->read_flags) &&
+		    tty->read_buf[tail] == __DISABLED_CHAR)
 			nr--;
-		INC(tail);
+		tail = (tail+1) & (N_TTY_BUF_SIZE-1);
 	}
 	return nr;
 }
 
-int tty_ioctl(struct inode * inode, struct file * file,
-	unsigned int cmd, unsigned long arg)
+int n_tty_ioctl(struct tty_struct * tty, struct file * file,
+		       unsigned int cmd, unsigned long arg)
 {
-	struct tty_struct * tty;
-	struct tty_struct * other_tty;
-	struct tty_struct * termios_tty;
-	pid_t pgrp;
-	int dev;
-	int termios_dev;
+	struct tty_struct * real_tty;
 	int retval;
 
-	if (MAJOR(file->f_rdev) != TTY_MAJOR) {
-		printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n");
-		return -EINVAL;
-	}
-	dev = MINOR(file->f_rdev);
-	tty = TTY_TABLE(dev);
-	if (!tty)
-		return -EINVAL;
-	if (IS_A_PTY(dev))
-		other_tty = tty_table[PTY_OTHER(dev)];
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER)
+		real_tty = tty->link;
 	else
-		other_tty = NULL;
-	if (IS_A_PTY_MASTER(dev)) {
-		termios_tty = other_tty;
-		termios_dev = PTY_OTHER(dev);
-	} else {
-		termios_tty = tty;
-		termios_dev = dev;
-	}
+		real_tty = tty;
+
 	switch (cmd) {
 		case TCGETS:
 			retval = verify_area(VERIFY_WRITE, (void *) arg,
@@ -371,39 +216,40 @@
 			if (retval)
 				return retval;
 			memcpy_tofs((struct termios *) arg,
-				    termios_tty->termios,
+				    real_tty->termios,
 				    sizeof (struct termios));
 			return 0;
 		case TCSETSF:
 		case TCSETSW:
 		case TCSETS:
-			retval = check_change(termios_tty, termios_dev);
+			retval = tty_check_change(real_tty);
 			if (retval)
 				return retval;
 			if (cmd == TCSETSF || cmd == TCSETSW) {
-				if (cmd == TCSETSF)
-					flush_input(termios_tty);
-				wait_until_sent(termios_tty, 0);
+				if (cmd == TCSETSF &&
+				    real_tty->ldisc.flush_buffer)
+					real_tty->ldisc.flush_buffer(real_tty);
+				wait_until_sent(real_tty, 0);
 			}
-			return set_termios(termios_tty, (struct termios *) arg,
-					   termios_dev);
+			return set_termios(real_tty,
+					   (struct termios *) arg);
 		case TCGETA:
-			return get_termio(termios_tty,(struct termio *) arg);
+			return get_termio(real_tty,(struct termio *) arg);
 		case TCSETAF:
 		case TCSETAW:
 		case TCSETA:
-			retval = check_change(termios_tty, termios_dev);
+			retval = tty_check_change(real_tty);
 			if (retval)
 				return retval;
 			if (cmd == TCSETAF || cmd == TCSETAW) {
-				if (cmd == TCSETAF)
-					flush_input(termios_tty);
-				wait_until_sent(termios_tty, 0);
+				if (cmd == TCSETAF &&
+				    real_tty->ldisc.flush_buffer)
+					real_tty->ldisc.flush_buffer(real_tty);
+				wait_until_sent(real_tty, 0);
 			}
-			return set_termio(termios_tty, (struct termio *) arg,
-					  termios_dev);
+			return set_termio(real_tty, (struct termio *) arg);
 		case TCXONC:
-			retval = check_change(tty, dev);
+			retval = tty_check_change(tty);
 			if (retval)
 				return retval;
 			switch (arg) {
@@ -415,107 +261,49 @@
 				break;
 			case TCIOFF:
 				if (STOP_CHAR(tty) != __DISABLED_CHAR)
-					put_tty_queue(STOP_CHAR(tty),
-						      &tty->write_q);
+					tty->driver.write(tty, 0,
+							  &STOP_CHAR(tty), 1);
 				break;
 			case TCION:
 				if (START_CHAR(tty) != __DISABLED_CHAR)
-					put_tty_queue(START_CHAR(tty),
-						      &tty->write_q);
+					tty->driver.write(tty, 0,
+							  &START_CHAR(tty), 1);
 				break;
 			default:
 				return -EINVAL;
 			}
 			return 0;
 		case TCFLSH:
-			retval = check_change(tty, dev);
+			retval = tty_check_change(tty);
 			if (retval)
 				return retval;
 			switch (arg) {
 			case TCIFLUSH:
-				flush_input(tty);
+				if (tty->ldisc.flush_buffer)
+					tty->ldisc.flush_buffer(tty);
 				break;
 			case TCIOFLUSH:
-				flush_input(tty);
+				if (tty->ldisc.flush_buffer)
+					tty->ldisc.flush_buffer(tty);
 				/* fall through */
 			case TCOFLUSH:
-				flush_output(tty);
+				if (tty->driver.flush_buffer)
+					tty->driver.flush_buffer(tty);
 				break;
 			default:
 				return -EINVAL;
 			}
 			return 0;
-		case TIOCEXCL:
-			set_bit(TTY_EXCLUSIVE, &tty->flags);
-			return 0;
-		case TIOCNXCL:
-			clear_bit(TTY_EXCLUSIVE, &tty->flags);
-			return 0;
-		case TIOCSCTTY:
-			if (current->leader &&
-			    (current->session == tty->session))
-				return 0;
-			/*
-			 * The process must be a session leader and
-			 * not have a controlling tty already.
-			 */
-			if (!current->leader || (current->tty >= 0))
-				return -EPERM;
-			if (tty->session > 0) {
-				/*
-				 * This tty is already the controlling
-				 * tty for another session group!
-				 */
-				if ((arg == 1) && suser()) {
-					/*
-					 * Steal it away
-					 */
-					struct task_struct *p;
-
-					for_each_task(p)
-						if (p->tty == dev)
-							p->tty = -1;
-				} else
-					return -EPERM;
-			}
-			current->tty = dev;
-			tty->session = current->session;
-			tty->pgrp = current->pgrp;
-			return 0;
-		case TIOCGPGRP:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (pid_t));
-			if (retval)
-				return retval;
-			/* If a master pty, return the slave's tpgid.
-			   If not, only return the tpgid if this is
-			   the controlling tty. */
-			if (tty == termios_tty && current->tty != dev)
-				return -ENOTTY;
-			put_fs_long(termios_tty->pgrp, (pid_t *) arg);
-			return 0;
-		case TIOCSPGRP:
-			retval = check_change(termios_tty, termios_dev);
-			if (retval)
-				return retval;
-			if ((current->tty < 0) ||
-			    (current->tty != termios_dev) ||
-			    (termios_tty->session != current->session))
-				return -ENOTTY;
-			pgrp = get_fs_long((pid_t *) arg);
-			if (pgrp < 0)
-				return -EINVAL;
-			if (session_of_pgrp(pgrp) != current->session)
-				return -EPERM;
-			termios_tty->pgrp = pgrp;
-			return 0;
 		case TIOCOUTQ:
 			retval = verify_area(VERIFY_WRITE, (void *) arg,
 					     sizeof (unsigned long));
 			if (retval)
 				return retval;
-			put_fs_long(CHARS(&tty->write_q),
-				    (unsigned long *) arg);
+			if (tty->driver.chars_in_buffer)
+				put_fs_long(tty->driver.chars_in_buffer(tty),
+					    (unsigned long *) arg);
+			else
+				put_fs_long(0, (unsigned long *) arg);
 			return 0;
 		case TIOCINQ:
 			retval = verify_area(VERIFY_WRITE, (void *) arg,
@@ -526,94 +314,9 @@
 				put_fs_long(inq_canon(tty),
 					(unsigned long *) arg);
 			else
-				put_fs_long(CHARS(&tty->secondary),
-					(unsigned long *) arg);
-			return 0;
-		case TIOCSTI:
-			if ((current->tty != dev) && !suser())
-				return -EPERM;
-			retval = verify_area(VERIFY_READ, (void *) arg, 1);
-			if (retval)
-				return retval;
-			put_tty_queue(get_fs_byte((char *) arg), &tty->read_q);
-			TTY_READ_FLUSH(tty);
-			return 0;
-		case TIOCGWINSZ:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (struct winsize));
-			if (retval)
-				return retval;
-			memcpy_tofs((struct winsize *) arg, &tty->winsize,
-				    sizeof (struct winsize));
-			return 0;
-		case TIOCSWINSZ:
-			if (IS_A_PTY_MASTER(dev))
-				set_window_size(other_tty,(struct winsize *) arg);
-			return set_window_size(tty,(struct winsize *) arg);
-		case TIOCLINUX:
-			switch (get_fs_byte((char *)arg))
-			{
-				case 0: 
-					return do_screendump(arg);
-				case 1: 
-					return do_get_ps_info(arg);
-#ifdef CONFIG_SELECTION
-				case 2:
-					return set_selection(arg);
-				case 3:
-					return paste_selection(tty);
-				case 4:
-					unblank_screen();
-					return 0;
-#endif /* CONFIG_SELECTION */
-				default: 
-					return -EINVAL;
-			}
-		case TIOCCONS:
-			if (IS_A_CONSOLE(dev)) {
-				if (!suser())
-					return -EPERM;
-				redirect = NULL;
-				return 0;
-			}
-			if (redirect)
-				return -EBUSY;
-			if (!suser())
-				return -EPERM;
-			if (IS_A_PTY_MASTER(dev))
-				redirect = other_tty;
-			else if (IS_A_PTY_SLAVE(dev))
-				redirect = tty;
-			else
-				return -ENOTTY;
-			return 0;
-		case FIONBIO:
-			arg = get_fs_long((unsigned long *) arg);
-			if (arg)
-				file->f_flags |= O_NONBLOCK;
-			else
-				file->f_flags &= ~O_NONBLOCK;
-			return 0;
-		case TIOCNOTTY:
-			if (current->tty != dev)
-				return -ENOTTY;
-			if (current->leader)
-				disassociate_ctty(0);
-			current->tty = -1;
-			return 0;
-		case TIOCGETD:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (unsigned long));
-			if (retval)
-				return retval;
-			put_fs_long(tty->disc, (unsigned long *) arg);
+				put_fs_long(tty->read_cnt,
+					    (unsigned long *) arg);
 			return 0;
-		case TIOCSETD:
-			retval = check_change(tty, dev);
-			if (retval)
-				return retval;
-			arg = get_fs_long((unsigned long *) arg);
-			return tty_set_ldisc(tty, arg);
 		case TIOCGLCKTRMIOS:
 			arg = get_fs_long((unsigned long *) arg);
 			retval = verify_area(VERIFY_WRITE, (void *) arg,
@@ -621,19 +324,20 @@
 			if (retval)
 				return retval;
 			memcpy_tofs((struct termios *) arg,
-				    &termios_locked[termios_dev],
+				    &real_tty->termios_locked,
 				    sizeof (struct termios));
 			return 0;
 		case TIOCSLCKTRMIOS:
 			if (!suser())
 				return -EPERM;
 			arg = get_fs_long((unsigned long *) arg);
-			memcpy_fromfs(&termios_locked[termios_dev],
+			memcpy_fromfs(&real_tty->termios_locked,
 				      (struct termios *) arg,
 				      sizeof (struct termios));
 			return 0;
 		case TIOCPKT:
-			if (!IS_A_PTY_MASTER(dev))
+			if (tty->driver.type != TTY_DRIVER_TYPE_PTY ||
+			    tty->driver.subtype != PTY_TYPE_MASTER)
 				return -ENOTTY;
 			retval = verify_area(VERIFY_READ, (void *) arg,
 					     sizeof (unsigned long));
@@ -647,26 +351,18 @@
 			} else
 				tty->packet = 0;
 			return 0;
+		/* These two ioctl's always return success; even if */
+		/* the driver doesn't support them. */
 		case TCSBRK: case TCSBRKP:
-			retval = check_change(tty, dev);
+			retval = tty_check_change(tty);
 			if (retval)
 				return retval;
 			wait_until_sent(tty, 0);
-			if (!tty->ioctl)
+			if (!tty->driver.ioctl)
 				return 0;
-			tty->ioctl(tty, file, cmd, arg);
+			tty->driver.ioctl(tty, file, cmd, arg);
 			return 0;
 		default:
-			if (tty->ioctl) {
-				retval = (tty->ioctl)(tty, file, cmd, arg);
-				if (retval != -EINVAL)
-					return retval;
-			}
-			if (ldiscs[tty->disc].ioctl) {
-				retval = (ldiscs[tty->disc].ioctl)
-					(tty, file, cmd, arg);
-				return retval;
-			}
-			return -EINVAL;
-	}
+			return -ENOIOCTLCMD;
+		}
 }
diff -u --recursive --new-file v1.1.12/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v1.1.12/linux/drivers/char/vt.c	Fri Feb 11 13:06:42 1994
+++ linux/drivers/char/vt.c	Mon May 23 07:55:14 1994
@@ -22,6 +22,8 @@
 #include "vt_kern.h"
 #include "diacr.h"
 
+extern struct tty_driver console_driver;
+
 /*
  * Console (vt and kd) routines, as defined by USL SVR4 manual, and by
  * experimentation and study of X386 SYSV handling.
@@ -119,11 +121,12 @@
 	int console, i;
 	unsigned char ucval;
 	struct kbd_struct * kbd;
+	struct vt_struct *vt = tty->driver_data;
 
-	console = tty->line - 1;
+	console = vt->vc_num;
 
 	if (console < 0 || console >= NR_CONSOLES)
-		return -EINVAL;
+		return -ENOIOCTLCMD;
 
 	kbd = kbd_table + console;
 	switch (cmd) {
@@ -234,7 +237,8 @@
 		  default:
 			return -EINVAL;
 		}
-		flush_input(tty);
+		if (tty->ldisc.flush_buffer)
+			tty->ldisc.flush_buffer(tty);
 		return 0;
 
 	case KDGKBMODE:
@@ -304,6 +308,13 @@
 			return -EINVAL;
 		if (KVAL(v) > max_vals[KTYP(v)])
 			return -EINVAL;
+		/*
+		 * Only the Superuser can set or unset the Secure
+		 * Attention Key.
+		 */
+		if (((key_map[s][i] == K_SAK) || (v == K_SAK)) &&
+		    !suser())
+			return -EPERM;
 		key_map[s][i] = v;
 		return 0;
 	}
@@ -473,8 +484,9 @@
 			return i;
 		put_fs_word(fg_console + 1, &vtstat->v_active);
 		state = 1;	/* /dev/tty0 is always open */
-		for (i = 1, mask = 2; i <= NR_CONSOLES; ++i, mask <<= 1)
-			if (tty_table[i] && tty_table[i]->count > 0)
+		for (i = 0, mask = 2; i < NR_CONSOLES; ++i, mask <<= 1)
+			if (console_driver.table[i] &&
+			    console_driver.table[i]->count > 0)
 				state |= mask;
 		put_fs_word(state, &vtstat->v_state);
 		return 0;
@@ -487,10 +499,12 @@
 		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
 		if (i)
 			return i;
-		for (i = 1; i <= NR_CONSOLES; ++i)
-			if (!tty_table[i] || tty_table[i]->count == 0)
+		for (i = 0; i < NR_CONSOLES; ++i)
+			if (!console_driver.table[i] ||
+			    console_driver.table[i]->count == 0)
 				break;
-		put_fs_long(i <= NR_CONSOLES ? i : -1, (unsigned long *)arg);
+		put_fs_long(i < NR_CONSOLES ? (i+1) : -1,
+			    (unsigned long *)arg);
 		return 0;
 
 	/*
@@ -586,6 +600,6 @@
 		/* con_get_trans() defined in console.c */
 
 	default:
-		return -EINVAL;
+		return -ENOIOCTLCMD;
 	}
 }
diff -u --recursive --new-file v1.1.12/linux/drivers/char/vt_kern.h linux/drivers/char/vt_kern.h
--- v1.1.12/linux/drivers/char/vt_kern.h	Wed Dec  1 14:44:15 1993
+++ linux/drivers/char/vt_kern.h	Mon May 23 07:55:14 1994
@@ -9,6 +9,7 @@
 #include <linux/vt.h>
 
 extern struct vt_struct {
+	int vc_num;				/* The console number */
 	unsigned char	vc_mode;		/* KD_TEXT, ... */
 	unsigned char	vc_kbdraw;
 	unsigned char	vc_kbde0;
@@ -16,6 +17,7 @@
 	struct vt_mode	vt_mode;
 	int		vt_pid;
 	int		vt_newvt;
+	struct wait_queue *paste_wait;
 } vt_cons[NR_CONSOLES];
 
 void kd_mksound(unsigned int count, unsigned int ticks);
diff -u --recursive --new-file v1.1.12/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.1.12/linux/drivers/net/3c501.c	Tue Apr 19 10:53:40 1994
+++ linux/drivers/net/3c501.c	Mon May 23 08:16:49 1994
@@ -327,7 +327,7 @@
 		   " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
 		   inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
 	    dev->tbusy = 0;
-	    mark_bh(INET_BH);
+	    mark_bh(NET_BH);
 	} else if (txsr & TX_16COLLISIONS) {
 	    if (el_debug)
 		printk("%s: Transmit failed 16 times, ethernet jammed?\n",
@@ -349,7 +349,7 @@
 		printk(" Tx succeeded %s\n",
 		       (txsr & TX_RDY) ? "." : "but tx is busy!");
 	    dev->tbusy = 0;
-	    mark_bh(INET_BH);
+	    mark_bh(NET_BH);
 	}
     } else {
 	int rxsr = inb(RX_STATUS);
diff -u --recursive --new-file v1.1.12/linux/drivers/net/3c507.c linux/drivers/net/3c507.c
--- v1.1.12/linux/drivers/net/3c507.c	Tue Apr 19 10:53:41 1994
+++ linux/drivers/net/3c507.c	Mon May 23 08:16:49 1994
@@ -530,7 +530,7 @@
 		lp->stats.tx_packets++;
 		lp->stats.collisions += tx_status & 0xf;
 		dev->tbusy = 0;
-		mark_bh(INET_BH);		/* Inform upper layers. */
+		mark_bh(NET_BH);		/* Inform upper layers. */
 	  } else {
 		lp->stats.tx_errors++;
 		if (tx_status & 0x0600)  lp->stats.tx_carrier_errors++;
diff -u --recursive --new-file v1.1.12/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.1.12/linux/drivers/net/3c509.c	Sun May  1 12:12:22 1994
+++ linux/drivers/net/3c509.c	Mon May 23 08:16:49 1994
@@ -31,6 +31,10 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include "../../tools/version.h"
+#endif
 
 
 
@@ -312,6 +316,9 @@
 		printk("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
 			   dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
 
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
 	return 0;					/* Always succeed */
 }
 
@@ -436,7 +443,7 @@
 			/* There's room in the FIFO for a full-sized packet. */
 			outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */
 			dev->tbusy = 0;
-			mark_bh(INET_BH);
+			mark_bh(NET_BH);
 		}
 		if (status & 0x80)				/* Statistics full. */
 			update_stats(ioaddr, dev);
@@ -645,6 +652,9 @@
 	irq2dev_map[dev->irq] = 0;
 
 	update_stats(ioaddr, dev);
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
 	return 0;
 }
 
@@ -656,3 +666,29 @@
  *  tab-width: 4
  * End:
  */
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device dev_3c509 = {
+	"" /*"3c509"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el3_probe };
+
+int
+init_module(void)
+{
+	if (register_netdev(&dev_3c509) != 0)
+		return -EIO;
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	if (MOD_IN_USE)
+		printk("3c509: device busy, remove delayed\n");
+	else
+	{
+		unregister_netdev(&dev_3c509);
+		kfree_s(dev_3c509.priv,sizeof(struct el3_private));
+		dev_3c509.priv=NULL;
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v1.1.12/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v1.1.12/linux/drivers/net/8390.c	Tue Apr 19 10:53:41 1994
+++ linux/drivers/net/8390.c	Mon May 23 08:16:50 1994
@@ -373,7 +373,7 @@
 		if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
 	}
     
-    mark_bh (INET_BH);
+    mark_bh (NET_BH);
 }
 
 /* We have a good packet(s), get it/them out of the buffers. */
@@ -476,7 +476,7 @@
 		outb(next_frame-1, e8390_base+EN0_BOUNDARY);
     }
     /* If any worth-while packets have been received, dev_rint()
-       has done a mark_bh(INET_BH) for us and will work on them
+       has done a mark_bh(NET_BH) for us and will work on them
        when we get to the bottom-half routine. */
 
 	/* Record the maximum Rx packet queue. */
diff -u --recursive --new-file v1.1.12/linux/drivers/net/CONFIG linux/drivers/net/CONFIG
--- v1.1.12/linux/drivers/net/CONFIG	Tue Feb 22 08:40:09 1994
+++ linux/drivers/net/CONFIG	Mon May 23 08:16:50 1994
@@ -26,10 +26,10 @@
 #  CONFIG_PLIP		The Crynwr-protocol PL/IP driver
 #	INITIALTIMEOUTFACTOR	Timing parameters.
 #	MAXTIMEOUTFACTOR
-#  D_LINK		The D-Link DE-600 Portable Ethernet Adaptor.
-#	  D_LINK_IO	The D-Link I/O address (0x378 == typical)
-#	  D_LINK_IRQ	The D-Link IRQ number to use (IRQ7 == typical)
-#	  D_LINK_DEBUG	Enable or disable D-Link debugging
+#  DE600		The D-Link DE-600 Portable Ethernet Adaptor.
+#	  DE600_IO	The DE600 I/O-port address (0x378 == default)
+#	  DE600_IRQ	The DE600 IRQ number to use (IRQ7 == default)
+#	  DE600_DEBUG	Enable or disable DE600 debugging (default off)
 #  DEPCA		The DIGITAL series of AT Ethernet Cards (DE100, DE200)
 #	  DEPCA_IRQ	Set the desired IRQ (=0, for autoprobe)
 #	  DEPCA_DEBUG	Set the desired debug level
@@ -54,6 +54,3 @@
 HP_OPTS		=
 PLIP_OPTS	=
 DEPCA_OPTS	= -DDEPCA_IRQ=0 -DDEPCA_DEBUG=1
-
-# The following are the only parameters that must be set in this file.
-DL_OPTS		= -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG
diff -u --recursive --new-file v1.1.12/linux/drivers/net/MODULES linux/drivers/net/MODULES
--- v1.1.12/linux/drivers/net/MODULES	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/MODULES	Mon May 23 08:16:50 1994
@@ -0,0 +1,3 @@
+MODULES = \
+	3c509.o \
+	de600.o
diff -u --recursive --new-file v1.1.12/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.1.12/linux/drivers/net/Makefile	Tue Apr 19 10:53:41 1994
+++ linux/drivers/net/Makefile	Mon May 23 08:16:50 1994
@@ -6,16 +6,17 @@
 # This will go away in some future future: hidden configuration files
 # are difficult for users to deal with.
 include CONFIG
+include MODULES
 
 NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o) net.a(loopback.o)
 CFLAGS := $(CFLAGS) -I../../net/inet
 CPP := $(CPP) -I../../net/inet
 
 # The point of the makefile...
-all: net.a
+all: net.a modules
 
 Space.o: Space.c ../../include/linux/autoconf.h
-	$(CC) $(CFLAGS) $(OPTS) $(DL_OPTS) -c $< -o $@
+	$(CC) $(CFLAGS) $(OPTS) -c $< -o $@
 
 net_init.o: ../../include/linux/autoconf.h
 
@@ -74,11 +75,8 @@
 endif
 
 ifdef CONFIG_DE600
-NETDRV_OBJS := $(NETDRV_OBJS) net.a(d_link.o)
-d_link.o: d_link.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c $<
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(de600.o)
 endif
-
 ifdef CONFIG_AT1500
 NETDRV_OBJS := $(NETDRV_OBJS) net.a(lance.o)
 endif
@@ -129,8 +127,11 @@
 NETDRV_OBJS := $(NETDRV_OBJS) net.a(8390.o)
 endif
 
-ifdef CONFIG_IP_DEFRAG
-NETDRV_OBJS := $(NETDRV_OBJS) net.a(ip-frag.o)
+ifdef CONFIG_PI
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(pi2.o)
+CONFIG_PI = CONFIG_PI
+pi2.o:  pi2.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(PI_OPTS) -c $<
 endif
 
 net.a: $(NETDRV_OBJS)
@@ -144,6 +145,18 @@
 
 tar:
 
+ifdef MODULES
+
+modules:
+	echo $(MODULES) > ../../modules/NET_MODULES
+	$(MAKE) CFLAGS="$(CFLAGS) -DMODULE" $(MODULES)
+	(cd ../../modules;for i in $(MODULES); do ln -sf ../drivers/net/$$i .; done)
+
+else
+
+modules:
+
+endif
 
 # include a dependency file if one exists
 
diff -u --recursive --new-file v1.1.12/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.1.12/linux/drivers/net/Space.c	Tue Apr 19 10:53:42 1994
+++ linux/drivers/net/Space.c	Mon May 23 08:16:50 1994
@@ -25,7 +25,6 @@
  *		2 of the License, or (at your option) any later version.
  */
 #include <linux/config.h>
-#include <linux/ddi.h>
 #include <linux/netdevice.h>
 
 #define LOOPBACK			/* always present, right?	*/
@@ -56,7 +55,7 @@
 
 /* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
 extern int atp_init(struct device *);
-extern int d_link_init(struct device *);
+extern int de600_probe(struct device *);
 
 static int
 ethif_probe(struct device *dev)
@@ -115,20 +114,15 @@
 #ifdef CONFIG_E2100		/* Cabletron E21xx series. */
 	&& e2100_probe(dev)
 #endif
+#ifdef CONFIG_DE600
+	&& de600_probe(dev)
+#endif
 	&& 1 ) {
 	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
     }
     return 0;
 }
 
-
-/* This remains seperate because it requires the addr and IRQ to be set. */
-#if defined(D_LINK) || defined(CONFIG_DE600)
-static struct device d_link_dev = {
-    "dl0", 0, 0, 0, 0, D_LINK_IO, D_LINK_IRQ, 0, 0, 0, NEXT_DEV, d_link_init };
-#   undef NEXT_DEV
-#   define NEXT_DEV	(&d_link_dev)
-#endif
 
 /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
 #ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */
diff -u --recursive --new-file v1.1.12/linux/drivers/net/at1700.c linux/drivers/net/at1700.c
--- v1.1.12/linux/drivers/net/at1700.c	Tue Apr 19 10:53:42 1994
+++ linux/drivers/net/at1700.c	Mon May 23 08:16:50 1994
@@ -448,7 +448,7 @@
 				lp->tx_queue_len = 0;
 				dev->trans_start = jiffies;
 				dev->tbusy = 0;
-				mark_bh(INET_BH);	/* Inform upper layers. */
+				mark_bh(NET_BH);	/* Inform upper layers. */
 			} else {
 				lp->tx_started = 0;
 				/* Turn on Tx interrupts off. */
@@ -529,7 +529,7 @@
 	}
 
 	/* If any worth-while packets have been received, dev_rint()
-	   has done a mark_bh(INET_BH) for us and will work on them
+	   has done a mark_bh(NET_BH) for us and will work on them
 	   when we get to the bottom-half routine. */
 	{
 		int i;
diff -u --recursive --new-file v1.1.12/linux/drivers/net/atp.c linux/drivers/net/atp.c
--- v1.1.12/linux/drivers/net/atp.c	Tue Apr 19 10:53:42 1994
+++ linux/drivers/net/atp.c	Mon May 23 08:16:50 1994
@@ -558,7 +558,7 @@
 				} else
 					lp->tx_unit_busy = 0;
 				dev->tbusy = 0;
-				mark_bh(INET_BH);	/* Inform upper layers. */
+				mark_bh(NET_BH);	/* Inform upper layers. */
 			}
 			num_tx_since_rx++;
 		} else if (num_tx_since_rx > 8
diff -u --recursive --new-file v1.1.12/linux/drivers/net/de600.c linux/drivers/net/de600.c
--- v1.1.12/linux/drivers/net/de600.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/de600.c	Mon May 23 08:16:51 1994
@@ -0,0 +1,772 @@
+static char *version =
+	"de600.c: $Revision: 1.35 $,  Bjorn Ekwall (bj0rn@blox.se)\n";
+/*
+ *	de600.c
+ *
+ *	Linux driver for the D-Link DE-600 Ethernet pocket adapter.
+ *
+ *	Portions (C) Copyright 1993 by Bjorn Ekwall
+ *	The Author may be reached as bj0rn@blox.se
+ *
+ *	Based on adapter information gathered from DE600.ASM by D-Link Inc.,
+ *	as included on disk C in the v.2.11 of PC/TCP from FTP Software.
+ *	For DE600.asm:
+ *		Portions (C) Copyright 1990 D-Link, Inc.
+ *		Copyright, 1988-1992, Russell Nelson, Crynwr Software
+ *
+ *	Adapted to the sample network driver core for linux,
+ *	written by: Donald Becker <becker@super.org>
+ *	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ *
+ *	compile-command:
+ *	"gcc -D__KERNEL__  -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer \
+ *	 -m486 -c de600.c
+ *
+ **************************************************************/
+/*
+ *	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, or (at your option)
+ *	any later version.
+ *
+ *	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. 
+ *
+ **************************************************************/
+/* Add another "; SLOW_DOWN_IO" here if your adapter won't work OK: */
+#define DE600_SLOW_DOWN SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO
+
+ /*
+ * If you still have trouble reading/writing to the adapter,
+ * modify the following "#define": (see <asm/io.h> for more info)
+#define REALLY_SLOW_IO
+ */
+#define SLOW_IO_BY_JUMPING /* Looks "better" than dummy write to port 0x80 :-) */
+
+/*
+ * For fix to TCP "slowdown", take a look at the "#define DE600_MAX_WINDOW"
+ * near the end of the file...
+ */
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifdef DE600_DEBUG
+#define PRINTK(x) if (de600_debug >= 2) printk x
+#else
+#define DE600_DEBUG 0
+#define PRINTK(x) /**/
+#endif
+static unsigned int de600_debug = DE600_DEBUG;
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <netinet/in.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <errno.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include "../../tools/version.h"
+#endif
+
+#define netstats enet_statistics
+
+/**************************************************
+ *                                                *
+ * Definition of D-Link Ethernet Pocket adapter   *
+ *                                                *
+ **************************************************/
+/*
+ * D-Link Ethernet pocket adapter ports
+ */
+/*
+ * OK, so I'm cheating, but there are an awful lot of
+ * reads and writes in order to get anything in and out
+ * of the DE-600 with 4 bits at a time in the parallel port,
+ * so every saved instruction really helps :-)
+ *
+ * That is, I don't care what the device struct says
+ * but hope that Space.c will keep the rest of the drivers happy.
+ */
+#ifndef DE600_IO
+#define DE600_IO 0x378
+#endif
+
+#define DATA_PORT	(DE600_IO)
+#define STATUS_PORT	(DE600_IO + 1)
+#define COMMAND_PORT	(DE600_IO + 2)
+
+#ifndef DE600_IRQ
+#define DE600_IRQ	7
+#endif
+/*
+ * It really should look like this, and autoprobing as well...
+ *
+#define DATA_PORT	(dev->base_addr + 0)
+#define STATUS_PORT	(dev->base_addr + 1)
+#define COMMAND_PORT	(dev->base_addr + 2)
+#define DE600_IRQ	dev->irq
+ */
+
+/*
+ * D-Link COMMAND_PORT commands
+ */
+#define SELECT_NIC	0x04 /* select Network Interface Card */
+#define SELECT_PRN	0x1c /* select Printer */
+#define NML_PRN		0xec /* normal Printer situation */
+#define IRQEN		0x10 /* enable IRQ line */
+
+/*
+ * D-Link STATUS_PORT
+ */
+#define RX_BUSY		0x80
+#define RX_GOOD		0x40
+#define TX_FAILED16	0x10
+#define TX_BUSY		0x08
+
+/*
+ * D-Link DATA_PORT commands
+ * command in low 4 bits
+ * data in high 4 bits
+ * select current data nibble with HI_NIBBLE bit
+ */
+#define WRITE_DATA	0x00 /* write memory */
+#define READ_DATA	0x01 /* read memory */
+#define STATUS		0x02 /* read  status register */
+#define COMMAND		0x03 /* write command register (see COMMAND below) */
+#define NULL_COMMAND	0x04 /* null command */
+#define RX_LEN		0x05 /* read  received packet length */
+#define TX_ADDR		0x06 /* set adapter transmit memory address */
+#define RW_ADDR		0x07 /* set adapter read/write memory address */
+#define HI_NIBBLE	0x08 /* read/write the high nibble of data,
+				or-ed with rest of command */
+
+/*
+ * command register, accessed through DATA_PORT with low bits = COMMAND
+ */
+#define RX_ALL		0x01 /* PROMISCIOUS */
+#define RX_BP		0x02 /* default: BROADCAST & PHYSICAL ADRESS */
+#define RX_MBP		0x03 /* MULTICAST, BROADCAST & PHYSICAL ADRESS */
+
+#define TX_ENABLE	0x04 /* bit 2 */
+#define RX_ENABLE	0x08 /* bit 3 */
+
+#define RESET		0x80 /* set bit 7 high */
+#define STOP_RESET	0x00 /* set bit 7 low */
+
+/*
+ * data to command register
+ * (high 4 bits in write to DATA_PORT)
+ */
+#define RX_PAGE2_SELECT	0x10 /* bit 4, only 2 pages to select */
+#define RX_BASE_PAGE	0x20 /* bit 5, always set when specifying RX_ADDR */
+#define FLIP_IRQ	0x40 /* bit 6 */
+
+/*
+ * D-Link adapter internal memory:
+ *
+ * 0-2K 1:st transmit page (send from pointer up to 2K)
+ * 2-4K	2:nd transmit page (send from pointer up to 4K)
+ *
+ * 4-6K 1:st receive page (data from 4K upwards)
+ * 6-8K 2:nd receive page (data from 6K upwards)
+ *
+ * 8K+	Adapter ROM (contains magic code and last 3 bytes of Ethernet address)
+ */
+#define MEM_2K		0x0800 /* 2048 */
+#define MEM_4K		0x1000 /* 4096 */
+#define MEM_6K		0x1800 /* 6144 */
+#define NODE_ADDRESS	0x2000 /* 8192 */
+
+#define RUNT 60		/* Too small Ethernet packet */
+
+/**************************************************
+ *                                                *
+ *             End of definition                  *
+ *                                                *
+ **************************************************/
+
+/*
+ * Index to functions, as function prototypes.
+ */
+#if 0
+/* For tricking tcp.c to announce a small max window (max 2 fast packets please :-) */
+static unsigned long	de600_rspace(struct sock *sk);
+#endif
+
+/* Routines used internally. (See "convenience macros") */
+static int		de600_read_status(struct device *dev);
+static unsigned	char	de600_read_byte(unsigned char type, struct device *dev);
+
+/* Put in the device structure. */
+static int	de600_open(struct device *dev);
+static int	de600_close(struct device *dev);
+static struct netstats *get_stats(struct device *dev);
+static int	de600_start_xmit(struct sk_buff *skb, struct device *dev);
+
+/* Dispatch from interrupts. */
+static void	de600_interrupt(int reg_ptr);
+static int	de600_tx_intr(struct device *dev, int irq_status);
+static void	de600_rx_intr(struct device *dev);
+
+/* Initialization */
+static void	trigger_interrupt(struct device *dev);
+int		de600_probe(struct device *dev);
+static void	adapter_init(struct device *dev);
+
+/*
+ * D-Link driver variables:
+ */
+extern struct device		*irq2dev_map[16];
+static volatile int		rx_page		= 0;
+
+#define TX_PAGES 2
+static volatile int		tx_fifo[TX_PAGES];
+static volatile int		tx_fifo_in = 0;
+static volatile int		tx_fifo_out = 0;
+static volatile int		free_tx_pages = TX_PAGES;
+
+/*
+ * Convenience macros/functions for D-Link adapter
+ */
+
+#define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); DE600_SLOW_DOWN
+#define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); DE600_SLOW_DOWN
+
+/* Thanks for hints from Mark Burton <markb@ordern.demon.co.uk> */
+#define de600_put_byte(data) ( \
+	outb_p(((data) << 4)   | WRITE_DATA            , DATA_PORT), \
+	outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT))
+
+/*
+ * The first two outb_p()'s below could perhaps be deleted if there
+ * would be more delay in the last two. Not certain about it yet...
+ */
+#define de600_put_command(cmd) ( \
+	outb_p(( rx_page        << 4)   | COMMAND            , DATA_PORT), \
+	outb_p(( rx_page        & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \
+	outb_p(((rx_page | cmd) << 4)   | COMMAND            , DATA_PORT), \
+	outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT))
+
+#define de600_setup_address(addr,type) ( \
+	outb_p((((addr) << 4) & 0xf0) | type            , DATA_PORT), \
+	outb_p(( (addr)       & 0xf0) | type | HI_NIBBLE, DATA_PORT), \
+	outb_p((((addr) >> 4) & 0xf0) | type            , DATA_PORT), \
+	outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT))
+
+#define rx_page_adr() ((rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K))
+
+/* Flip bit, only 2 pages */
+#define next_rx_page() (rx_page ^= RX_PAGE2_SELECT)
+
+#define tx_page_adr(a) (((a) + 1) * MEM_2K)
+
+static inline int
+de600_read_status(struct device *dev)
+{
+	int	status;
+
+	outb_p(STATUS, DATA_PORT);
+	status = inb(STATUS_PORT);
+	outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
+
+	return status;
+}
+
+static inline unsigned char
+de600_read_byte(unsigned char type, struct device *dev) { /* dev used by macros */
+	unsigned char	lo;
+
+	(void)outb_p((type), DATA_PORT);
+	lo = ((unsigned char)inb(STATUS_PORT)) >> 4;
+	(void)outb_p((type) | HI_NIBBLE, DATA_PORT);
+	return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo;
+}
+
+/*
+ * Open/initialize the board.  This is called (in the current kernel)
+ * after booting when 'ifconfig <dev->name> $IP_ADDR' is run (in rc.inet1).
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is a non-reboot way to recover if something goes wrong.
+ */
+static int
+de600_open(struct device *dev)
+{
+#if 0
+	extern struct proto tcp_prot;
+#endif
+
+	if (request_irq(DE600_IRQ, de600_interrupt)) {
+		printk ("%s: unable to get IRQ %d\n", dev->name, DE600_IRQ);
+		return 1;
+	}
+	irq2dev_map[DE600_IRQ] = dev;
+
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	adapter_init(dev);
+
+	/*
+	 * Yes, I know!
+	 * This is really not nice, but since a machine that uses DE-600
+	 * rarely uses any other TCP/IP connection device simultaneously,
+	 * this hack shouldn't really slow anything up.
+	 * (I don't know about slip though... but it won't break it)
+	 *
+	 * This fix is better than changing in tcp.h IMHO
+	 */
+#if 0	 
+	tcp_prot.rspace = de600_rspace; /* was: sock_rspace */
+#endif
+
+
+	return 0;
+}
+
+/*
+ * The inverse routine to de600_open().
+ */
+static int
+de600_close(struct device *dev)
+{
+	select_nic();
+	rx_page = 0;
+	de600_put_command(RESET);
+	de600_put_command(STOP_RESET);
+	de600_put_command(0);
+	select_prn();
+
+	free_irq(DE600_IRQ);
+	irq2dev_map[DE600_IRQ] = NULL;
+	dev->start = 0;
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+#if 0
+	tcp_prot.rspace = sock_rspace; /* see comment above! */
+#endif
+	return 0;
+}
+
+static struct netstats *
+get_stats(struct device *dev)
+{
+    return (struct netstats *)(dev->priv);
+}
+
+static inline void
+trigger_interrupt(struct device *dev)
+{
+	de600_put_command(FLIP_IRQ);
+	select_prn();
+	DE600_SLOW_DOWN;
+	select_nic();
+	de600_put_command(0);
+}
+
+/*
+ * Copy a buffer to the adapter transmit page memory.
+ * Start sending.
+ */
+static int
+de600_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	int		transmit_from;
+	int		len;
+	int		tickssofar;
+	unsigned char	*buffer = skb->data;
+
+	/*
+	 * If some higher layer thinks we've missed a
+	 * tx-done interrupt we are passed NULL.
+	 * Caution: dev_tint() handles the cli()/sti() itself.
+	 */
+
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (free_tx_pages <= 0) {	/* Do timeouts, to avoid hangs. */
+		tickssofar = jiffies - dev->trans_start;
+
+		if (tickssofar < 5)
+			return 1;
+
+		/* else */
+		printk("%s: transmit timed out (%d), %s?\n",
+			dev->name,
+			tickssofar,
+			"network cable problem"
+			);
+		/* Restart the adapter. */
+		adapter_init(dev);
+	}
+
+	/* Start real output */
+	PRINTK(("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages));
+
+	if ((len = skb->len) < RUNT)
+		len = RUNT;
+
+	cli();
+	select_nic();
+
+	tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len;
+	tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */
+
+	de600_setup_address(transmit_from, RW_ADDR);
+	for ( ; len > 0; --len, ++buffer)
+		de600_put_byte(*buffer);
+
+	if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */
+		dev->trans_start = jiffies;
+		dev->tbusy = 0;	/* allow more packets into adapter */
+		/* Send page and generate an interrupt */
+		de600_setup_address(transmit_from, TX_ADDR);
+		de600_put_command(TX_ENABLE);
+	}
+	else {
+		dev->tbusy = !free_tx_pages;
+		select_prn();
+	}
+	
+	sti(); /* interrupts back on */
+	
+	if (skb->free)
+		kfree_skb (skb, FREE_WRITE);
+
+	return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void
+de600_interrupt(int reg_ptr)
+{
+	int		irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+	struct device	*dev = irq2dev_map[irq];
+	unsigned char	irq_status;
+	int		retrig = 0;
+	int		boguscount = 0;
+
+	/* This might just as well be deleted now, no crummy drivers present :-) */
+	if ((dev == NULL) || (dev->start == 0) || (DE600_IRQ != irq)) {
+		printk("%s: bogus interrupt %d\n", dev?dev->name:"DE-600", irq);
+		return;
+	}
+
+	dev->interrupt = 1;
+	select_nic();
+	irq_status = de600_read_status(dev);
+
+	do {
+		PRINTK(("de600_interrupt (%2.2X)\n", irq_status));
+
+		if (irq_status & RX_GOOD)
+			de600_rx_intr(dev);
+		else if (!(irq_status & RX_BUSY))
+			de600_put_command(RX_ENABLE);
+
+		/* Any transmission in progress? */
+		if (free_tx_pages < TX_PAGES)
+			retrig = de600_tx_intr(dev, irq_status);
+		else
+			retrig = 0;
+
+		irq_status = de600_read_status(dev);
+	} while ( (irq_status & RX_GOOD) || ((++boguscount < 10) && retrig) );
+	/*
+	 * Yeah, it _looks_ like busy waiting, smells like busy waiting
+	 * and I know it's not PC, but please, it will only occur once
+	 * in a while and then only for a loop or so (< 1ms for sure!)
+	 */
+
+	/* Enable adapter interrupts */
+	dev->interrupt = 0;
+	select_prn();
+
+	if (retrig)
+		trigger_interrupt(dev);
+
+	sti();
+	return;
+}
+
+static int
+de600_tx_intr(struct device *dev, int irq_status)
+{
+	/*
+	 * Returns 1 if tx still not done
+	 */
+
+	mark_bh(NET_BH);
+	/* Check if current transmission is done yet */
+	if (irq_status & TX_BUSY)
+		return 1; /* tx not done, try again */
+
+	/* else */
+	/* If last transmission OK then bump fifo index */
+	if (!(irq_status & TX_FAILED16)) {
+		tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES;
+		++free_tx_pages;
+		((struct netstats *)(dev->priv))->tx_packets++;
+		dev->tbusy = 0;
+	}
+
+	/* More to send, or resend last packet? */
+	if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) {
+		dev->trans_start = jiffies;
+		de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR);
+		de600_put_command(TX_ENABLE);
+		return 1;
+	}
+	/* else */
+
+	return 0;
+}
+
+/*
+ * We have a good packet, get it out of the adapter.
+ */
+static void
+de600_rx_intr(struct device *dev)
+{
+	struct sk_buff	*skb;
+	int		i;
+	int		read_from;
+	int		size;
+	register unsigned char	*buffer;
+
+	cli();
+	/* Get size of received packet */
+	size = de600_read_byte(RX_LEN, dev);	/* low byte */
+	size += (de600_read_byte(RX_LEN, dev) << 8);	/* high byte */
+	size -= 4;	/* Ignore trailing 4 CRC-bytes */
+
+	/* Tell adapter where to store next incoming packet, enable receiver */
+	read_from = rx_page_adr();
+	next_rx_page();
+	de600_put_command(RX_ENABLE);
+	sti();
+
+	if ((size < 32)  ||  (size > 1535))
+		printk("%s: Bogus packet size %d.\n", dev->name, size);
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	sti();
+	if (skb == NULL) {
+		printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+			dev->name, size);
+		return;
+	}
+	/* else */
+
+	skb->lock = 0;
+	/* 'skb->data' points to the start of sk_buff data area. */
+	buffer = skb->data;
+
+	/* copy the packet into the buffer */
+	de600_setup_address(read_from, RW_ADDR);
+	for (i = size; i > 0; --i, ++buffer)
+		*buffer = de600_read_byte(READ_DATA, dev);
+	
+	((struct netstats *)(dev->priv))->rx_packets++; /* count all receives */
+
+	if (dev_rint((unsigned char *)skb, size, IN_SKBUFF, dev))
+		printk("%s: receive buffers full.\n", dev->name);
+	/*
+	 * If any worth-while packets have been received, dev_rint()
+	 * has done a mark_bh(INET_BH) for us and will work on them
+	 * when we get to the bottom-half routine.
+	 */
+}
+
+int
+de600_probe(struct device *dev)
+{
+	int	i;
+	static struct netstats de600_netstats;
+	/*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+
+	printk("%s: D-Link DE-600 pocket adapter", dev->name);
+	/* Alpha testers must have the version number to report bugs. */
+	if (de600_debug > 1)
+		printk(version);
+
+	/* probe for adapter */
+	rx_page = 0;
+	select_nic();
+	(void)de600_read_status(dev);
+	de600_put_command(RESET);
+	de600_put_command(STOP_RESET);
+	if (de600_read_status(dev) & 0xf0) {
+		printk(": not at I/O %#3x.\n", DATA_PORT);
+		return ENODEV;
+	}
+
+	/*
+	 * Maybe we found one,
+	 * have to check if it is a D-Link DE-600 adapter...
+	 */
+
+	/* Get the adapter ethernet address from the ROM */
+	de600_setup_address(NODE_ADDRESS, RW_ADDR);
+	for (i = 0; i < ETH_ALEN; i++) {
+		dev->dev_addr[i] = de600_read_byte(READ_DATA, dev);
+		dev->broadcast[i] = 0xff;
+	}
+
+	/* Check magic code */
+	if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) {
+		/* OK, install real address */
+		dev->dev_addr[0] = 0x00;
+		dev->dev_addr[1] = 0x80;
+		dev->dev_addr[2] = 0xc8;
+		dev->dev_addr[3] &= 0x0f;
+		dev->dev_addr[3] |= 0x70;
+	} else {
+		printk(" not identified in the printer port\n");
+		return ENODEV;
+	}
+
+	printk(", Ethernet Address: %2.2X", dev->dev_addr[0]);
+	for (i = 1; i < ETH_ALEN; i++)
+		printk(":%2.2X",dev->dev_addr[i]);
+	printk("\n");
+
+	/* Initialize the device structure. */
+	/*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+	dev->priv = &de600_netstats;
+
+	memset(dev->priv, 0, sizeof(struct netstats));
+	dev->get_stats = get_stats;
+
+	dev->open = de600_open;
+	dev->stop = de600_close;
+	dev->hard_start_xmit = &de600_start_xmit;
+
+	ether_setup(dev);
+	
+	select_prn();
+	return 0;
+}
+
+static void
+adapter_init(struct device *dev)
+{
+	int	i;
+
+	cli();
+	dev->tbusy = 0;		/* Transmit busy...  */
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	select_nic();
+	rx_page = 0; /* used by RESET */
+	de600_put_command(RESET);
+	de600_put_command(STOP_RESET);
+
+	tx_fifo_in = 0;
+	tx_fifo_out = 0;
+	free_tx_pages = TX_PAGES;
+
+	/* set the ether address. */
+	de600_setup_address(NODE_ADDRESS, RW_ADDR);
+	for (i = 0; i < ETH_ALEN; i++)
+		de600_put_byte(dev->dev_addr[i]);
+
+	/* where to start saving incoming packets */
+	rx_page = RX_BP | RX_BASE_PAGE;
+	de600_setup_address(MEM_4K, RW_ADDR);
+	/* Enable receiver */
+	de600_put_command(RX_ENABLE);
+	select_prn();
+	sti();
+}
+
+#if 0
+/*
+ *	The new router code (coming soon 8-) ) will fix this properly.
+ */
+#define DE600_MIN_WINDOW 1024
+#define DE600_MAX_WINDOW 2048
+#define DE600_TCP_WINDOW_DIFF 1024
+/*
+ * Copied from sock.c
+ *
+ * Sets a lower max receive window in order to achieve <= 2
+ * packets arriving at the adapter in fast succession.
+ * (No way that a DE-600 can cope with an ethernet saturated with its packets :-)
+ *
+ * Since there are only 2 receive buffers in the DE-600
+ * and it takes some time to copy from the adapter,
+ * this is absolutely necessary for any TCP performance whatsoever!
+ *
+ */
+#define min(a,b)	((a)<(b)?(a):(b))
+static unsigned long
+de600_rspace(struct sock *sk)
+{
+  int amt;
+
+  if (sk != NULL) {
+/*
+ * Hack! You might want to play with commenting away the following line,
+ * if you know what you do!
+ */
+  	sk->max_unacked = DE600_MAX_WINDOW - DE600_TCP_WINDOW_DIFF;
+
+	if (sk->rmem_alloc >= SK_RMEM_MAX-2*DE600_MIN_WINDOW) return(0);
+	amt = min((SK_RMEM_MAX-sk->rmem_alloc)/2-DE600_MIN_WINDOW, DE600_MAX_WINDOW);
+	if (amt < 0) return(0);
+	return(amt);
+  }
+  return(0);
+}
+#endif
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device de600_dev = {
+	"" /*"de600"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de600_probe };
+
+int
+init_module(void)
+{
+	if (register_netdev(&de600_dev) != 0)
+		return -EIO;
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	if (MOD_IN_USE)
+		printk("de600: device busy, remove delayed\n");
+	else
+		unregister_netdev(&de600_dev);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v1.1.12/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.1.12/linux/drivers/net/eexpress.c	Tue Apr 19 10:53:43 1994
+++ linux/drivers/net/eexpress.c	Mon May 23 08:16:51 1994
@@ -545,7 +545,7 @@
 			lp->stats.tx_packets++;
 			lp->stats.collisions += tx_status & 0xf;
 			dev->tbusy = 0;
-			mark_bh(INET_BH);	/* Inform upper layers. */
+			mark_bh(NET_BH);	/* Inform upper layers. */
 		} else {
 			lp->stats.tx_errors++;
 			if (tx_status & 0x0600)  lp->stats.tx_carrier_errors++;
diff -u --recursive --new-file v1.1.12/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v1.1.12/linux/drivers/net/lance.c	Tue Apr 19 10:53:44 1994
+++ linux/drivers/net/lance.c	Mon May 23 08:16:51 1994
@@ -196,6 +196,7 @@
     int dma;
     struct enet_statistics stats;
     char old_lance;
+    char lock;
     int pad0, pad1;		/* Used for alignment */
 };
 
@@ -434,6 +435,7 @@
     struct lance_private *lp = (struct lance_private *)dev->priv;
     int i;
 
+    lp->lock = 0;
     lp->cur_rx = lp->cur_tx = 0;
     lp->dirty_rx = lp->dirty_tx = 0;
 
@@ -515,8 +517,17 @@
 
     /* Block a timer-based transmit from overlapping.  This could better be
        done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-    if (set_bit(0, (void*)&dev->tbusy) != 0)
+    if (set_bit(0, (void*)&dev->tbusy) != 0) {
 	printk("%s: Transmitter access conflict.\n", dev->name);
+	return 1;
+    }
+
+    if (set_bit(0, (void*)&lp->lock) != 0) {
+        if (lance_debug > 2)
+	    printk("%s: tx queue lock!.\n", dev->name);
+	/* don't clear dev->tbusy flag. */
+        return 1;
+    }
 
     /* Fill in a Tx ring entry */
 
@@ -549,8 +560,9 @@
     } else {
     	/* We can't free the packet yet, so we inform the memory management
 	   code that we are still using it. */
-    	if(skb->free==0)
-    		skb_kept_by_device(skb);
+
+        skb_kept_by_device(skb);
+
 	lp->tx_ring[entry].base = (int)(skb->data) | 0x83000000;
     }
     lp->cur_tx++;
@@ -561,8 +573,11 @@
 
     dev->trans_start = jiffies;
 
+    cli();
+    lp->lock = 0;
     if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
 	dev->tbusy=0;
+    sti();
 
     return 0;
 }
@@ -635,10 +650,8 @@
 	    if (databuff >= (void*)(&lp->tx_bounce_buffs[TX_RING_SIZE])
 		|| databuff < (void*)(lp->tx_bounce_buffs)) {
 		struct sk_buff *skb = ((struct sk_buff *)databuff) - 1;
-		if (skb->free)
-		    kfree_skb(skb, FREE_WRITE);
-		else
-		    skb_device_release(skb,FREE_WRITE);
+		skb_device_release(skb,FREE_WRITE);
+
 		/* Warning: skb may well vanish at the point you call
 		   device_release! */
 	    }
@@ -656,7 +669,7 @@
 	if (dev->tbusy  &&  dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
 	    /* The ring is no longer full, clear tbusy. */
 	    dev->tbusy = 0;
-	    mark_bh(INET_BH);
+	    mark_bh(NET_BH);
 	}
 
 	lp->dirty_tx = dirty_tx;
@@ -685,6 +698,7 @@
 {
     struct lance_private *lp = (struct lance_private *)dev->priv;
     int entry = lp->cur_rx & RX_RING_MOD_MASK;
+    int i;
 	
     /* If we own the next entry, it's a new packet. Send it up. */
     while (lp->rx_ring[entry].base >= 0) {
@@ -701,6 +715,7 @@
 	    if (status & 0x10) lp->stats.rx_over_errors++;
 	    if (status & 0x08) lp->stats.rx_crc_errors++;
 	    if (status & 0x04) lp->stats.rx_fifo_errors++;
+	    lp->rx_ring[entry].base &= 0x03ffffff;
 	} else {
 	    /* Malloc up new buffer, compatible with net-2e. */
 	    short pkt_len = lp->rx_ring[entry].msg_length;
@@ -709,7 +724,15 @@
 	    skb = alloc_skb(pkt_len, GFP_ATOMIC);
 	    if (skb == NULL) {
 		printk("%s: Memory squeeze, deferring packet.\n", dev->name);
-		lp->stats.rx_dropped++;	/* Really, deferred. */
+		for (i=0; i < RX_RING_SIZE; i++)
+		  if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
+		    break;
+
+		if (i > RX_RING_SIZE -2) {
+		  lp->stats.rx_dropped++;
+		  lp->rx_ring[entry].base |= 0x80000000;
+		  lp->cur_rx++;
+		}
 		break;
 	    }
 	    skb->len = pkt_len;
diff -u --recursive --new-file v1.1.12/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v1.1.12/linux/drivers/net/loopback.c	Tue Apr 19 10:54:16 1994
+++ linux/drivers/net/loopback.c	Mon May 23 08:16:52 1994
@@ -11,6 +11,8 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Donald Becker, <becker@super.org>
  *
+ *		Alan Cox	:	Fixed oddments for NET3.014
+ *
  *		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
@@ -44,7 +46,6 @@
   struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
   int done;
 
-  DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb));
   if (skb == NULL || dev == NULL) return(0);
 
   cli();
@@ -90,6 +91,12 @@
     return (struct enet_statistics *)dev->priv;
 }
 
+static int loopback_open(struct device *dev)
+{
+	dev->flags|=IFF_LOOPBACK;
+	return 0;
+}
+
 /* Initialize the rest of the LOOPBACK device. */
 int
 loopback_init(struct device *dev)
@@ -107,6 +114,7 @@
   dev->type		= ARPHRD_ETHER;		/* 0x0001		*/
   dev->type_trans	= eth_type_trans;
   dev->rebuild_header	= eth_rebuild_header;
+  dev->open		= loopback_open;
 #else
   dev->hard_header_length = 0;
   dev->addr_len		= 0;
@@ -119,10 +127,12 @@
   /* New-style flags. */
   dev->flags		= IFF_LOOPBACK;
   dev->family		= AF_INET;
+#ifdef CONFIG_INET    
   dev->pa_addr		= in_aton("127.0.0.1");
   dev->pa_brdaddr	= in_aton("127.255.255.255");
   dev->pa_mask		= in_aton("255.0.0.0");
   dev->pa_alen		= sizeof(unsigned long);
+#endif  
   dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
   memset(dev->priv, 0, sizeof(struct enet_statistics));
   dev->get_stats = get_stats;
diff -u --recursive --new-file v1.1.12/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v1.1.12/linux/drivers/net/net_init.c	Tue Apr 19 10:53:44 1994
+++ linux/drivers/net/net_init.c	Mon May 23 08:16:52 1994
@@ -14,6 +14,8 @@
 	It's primary advantage is that it's able to allocate low-memory buffers.
 	A secondary advantage is that the dangerous NE*000 netcards can reserve
 	their I/O port region before the SCSI probes start.
+
+	register_netdev()/unregister_netdev() by Bjorn Ekwall <bj0rn@blox.se>
 */
 
 #include <linux/config.h>
@@ -60,7 +62,9 @@
 #if defined(CONFIG_LANCE)			/* Note this is _not_ CONFIG_AT1500. */
 	mem_start = lance_init(mem_start, mem_end);
 #endif
-
+#if defined(CONFIG_PI)
+	mem_start = pi_init(mem_start, mem_end);
+#endif	
 	return mem_start;
 }
 
@@ -161,6 +165,73 @@
 	dev->pa_alen	= sizeof(unsigned long);
 }
 
+int register_netdev(struct device *dev)
+{
+	struct device *d = dev_base;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	if (dev && dev->init) 
+	{
+		if (dev->init(dev) != 0)
+		{
+			restore_flags(flags);
+			return -EIO;
+		}
+		
+		if (dev->name  &&  dev->name[0] == '\0')
+			sprintf(dev->name, "eth%d", next_ethdev_number++);
+
+		/* Add device to end of chain */
+		if (dev_base) 
+		{
+			while (d->next)
+				d = d->next;
+			d->next = dev;
+		}
+		else
+			dev_base = dev;
+		dev->next = NULL;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+void unregister_netdev(struct device *dev)
+{
+	struct device *d = dev_base;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	printk("unregister_netdev: device ");
+	if (dev) {
+		if (dev->start)
+			printk("'%s' busy", dev->name);
+		else {
+			if (dev_base == dev)
+				dev_base = dev->next;
+			else {
+				while (d && (d->next != dev))
+					d = d->next;
+
+				if (d && (d->next == dev)) {
+					d->next = dev->next;
+					printk("'%s' unlinked", dev->name);
+				}
+				else
+					printk("'%s' not found", dev->name);
+			}
+		}
+	}
+	else
+		printk("was NULL");
+	printk("\n");
+	restore_flags(flags);
+}
 
 
 /*
diff -u --recursive --new-file v1.1.12/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.1.12/linux/drivers/net/plip.c	Tue Apr 19 10:53:45 1994
+++ linux/drivers/net/plip.c	Mon May 23 08:16:52 1994
@@ -282,7 +282,7 @@
     if (skb->free)
 	kfree_skb (skb, FREE_WRITE);
     dev->tbusy = 0;
-    mark_bh (INET_BH);
+    mark_bh (NET_BH);
     return 0/*ret_val*/;
 }
 
diff -u --recursive --new-file v1.1.12/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.1.12/linux/drivers/net/ppp.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/ppp.c	Mon May 23 08:16:53 1994
@@ -0,0 +1,1998 @@
+/*
+   PPP for Linux
+*/
+
+/*
+   Sources:
+
+   slip.c
+
+   RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of
+   Multi-protocol Datagrams over Point-to-Point Links
+
+   RFC1332: IPCP
+
+   ppp-2.0
+
+   Flags for this module (any combination is acceptable for testing.):
+
+   NET02D	      -	Define if using Net-2-Debugged in kernels earler
+   			than v1.1.4.
+
+   NEW_TTY_DRIVERS    -	Define if using new Ted Ts'o's alpha TTY drivers
+   			from tsx-11.mit.edu. From Ted Ts'o.
+
+   OPTIMIZE_FLAG_TIME -	Number of jiffies to force sending of leading flag
+			character. This is normally set to ((HZ * 3) / 2).
+			This is 1.5 seconds. If not defined then the leading
+			flag is always sent.  
+*/
+
+/* #define NET02D				-* */
+#define NEW_TTY_DRIVERS				/* */
+#define OPTIMIZE_FLAG_TIME  ((HZ * 3)/2)	/* */
+
+#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/tty.h>
+#include <linux/errno.h>
+#include <linux/sched.h>   /* to get the struct task_struct */
+#include <linux/string.h>  /* used in new tty drivers */
+#include <linux/signal.h>  /* used in new tty drivers */
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+
+#ifdef NET02D				/* v1.1.4 net code and earlier */
+#include <dev.h>
+#include <skbuff.h>
+#include <inet.h>
+#define	skb_queue_head_init(buf)	*(buf) = NULL
+#else					/* v1.1.5 and later */
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#endif
+
+#include <linux/ppp.h>
+
+#include <ip.h>
+#include <tcp.h>
+
+#include "slhc.h"
+
+#include <linux/if_arp.h>
+#ifndef ARPHRD_PPP
+#define ARPHRD_PPP 0
+#endif
+
+#define PRINTK(p) printk p ;
+#define ASSERT(p) if (!p) PRINTK ((KERN_CRIT "assertion failed: " # p))
+#define PRINTKN(n,p) {if (ppp_debug >= n) PRINTK (p)}
+#define CHECK_PPP(a)  if (!ppp->inuse) { PRINTK ((ppp_warning, __LINE__)) return a;}
+#define CHECK_PPP_VOID()  if (!ppp->inuse) { PRINTK ((ppp_warning, __LINE__)) return;}
+
+#define in_xmap(ppp,c)	(ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
+#define in_rmap(ppp,c)	((((unsigned int) (unsigned char) (c)) < 0x20) && \
+			ppp->recv_async_map & (1 << (c)))
+
+#define bset(p,b)	((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
+
+int ppp_debug = 2;
+int ppp_debug_netpackets = 0;
+
+/* Define this string only once for all macro envocations */
+static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
+
+int ppp_init(struct device *);
+static void ppp_init_ctrl_blk(struct ppp *);
+static int ppp_dev_open(struct device *);
+static int ppp_dev_close(struct device *);
+static void ppp_kick_tty(struct ppp *);
+
+#ifdef NEW_TTY_DRIVERS
+#define ppp_find(tty) ((struct ppp *) tty->disc_data)
+#else
+static void ppp_output_done(void *);
+static void ppp_unesc(struct ppp *ppp, unsigned char *c, int n);
+static struct ppp *ppp_find(struct tty_struct *);
+#endif
+
+static void ppp_doframe(struct ppp *);
+static int ppp_do_ip(struct ppp *, unsigned short, unsigned char *, int);
+static int ppp_us_queue(struct ppp *, unsigned short, unsigned char *, int);
+static int ppp_xmit(struct sk_buff *, struct device *);
+static unsigned short ppp_type_trans(struct sk_buff *, struct device *);
+
+#ifdef NET02D
+static int ppp_header(unsigned char *buff, struct device *dev,
+		      unsigned short type, unsigned long daddr,
+		      unsigned long saddr, unsigned len);
+static int ppp_rebuild_header(void *buff, struct device *dev);
+static void ppp_add_arp(unsigned long addr, struct sk_buff *skb,
+			struct device *dev);
+#else
+static int ppp_header(unsigned char *, struct device *, unsigned short,
+		      void *, void *, unsigned, struct sk_buff *);
+static int ppp_rebuild_header(void *, struct device *, unsigned long,
+			      struct sk_buff *);
+#endif
+
+static struct enet_statistics *ppp_get_stats (struct device *);
+static struct ppp *ppp_alloc(void);
+static int ppp_lock(struct ppp *);
+static void ppp_unlock(struct ppp *);
+static void ppp_add_fcs(struct ppp *);
+static int ppp_check_fcs(struct ppp *);
+static void ppp_print_buffer(const char *,char *,int,int);
+
+static int ppp_read(struct tty_struct *, struct file *, unsigned char *,
+		    unsigned int);
+static int ppp_write(struct tty_struct *, struct file *, unsigned char *,
+		     unsigned int);
+static int ppp_ioctl(struct tty_struct *, struct file *, unsigned int,
+		     unsigned long);
+static int ppp_select(struct tty_struct *tty, struct inode * inode,
+		      struct file * filp, int sel_type, select_table * wait);
+static int ppp_open(struct tty_struct *);
+static void ppp_close(struct tty_struct *);
+
+#ifdef NEW_TTY_DRIVERS
+static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
+			    char *fp, int count);
+static void ppp_write_wakeup(struct tty_struct *tty);
+#else
+static void ppp_tty_input_ready(struct tty_struct *);
+#endif
+
+/* FCS table from RFC1331 */
+
+static unsigned short fcstab[256] = {
+  0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+  0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+  0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+  0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+  0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+  0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+  0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+  0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+  0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+  0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+  0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+  0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+  0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+  0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+  0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+  0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+  0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+  0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+  0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+  0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+  0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+  0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+  0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+  0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+  0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+  0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+  0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+  0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+  0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+  0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+  0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+  0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+  };
+
+struct tty_ldisc ppp_ldisc;
+
+static struct ppp ppp_ctrl[PPP_NRUNIT];
+
+/*************************************************************
+ * INITIALIZATION
+ *************************************************************/
+
+static int first_time = 1;
+
+/* called at boot time for each ppp device */
+
+int
+ppp_init(struct device *dev)
+{
+  struct ppp *ppp;
+  int i;
+
+  ppp = &ppp_ctrl[dev->base_addr];
+
+  if (first_time) {
+    first_time = 0;
+
+    printk (KERN_INFO "PPP: version %s (%d channels)"
+#ifdef NET02D
+	   " NET02D"
+#endif
+#ifdef NEW_TTY_DRIVERS
+	   " NEW_TTY_DRIVERS"
+#endif
+#ifdef OPTIMIZE_FLAG_TIME
+	   " OPTIMIZE_FLAGS"
+#endif
+	   "\n", PPP_VERSION, PPP_NRUNIT);
+
+    printk (KERN_INFO
+	   "TCP compression code copyright 1989 Regents of the "
+	   "University of California\n");
+
+    (void) memset(&ppp_ldisc, 0, sizeof(ppp_ldisc));
+    ppp_ldisc.open    = ppp_open;
+    ppp_ldisc.close   = ppp_close;
+    ppp_ldisc.read    = ppp_read;
+    ppp_ldisc.write   = ppp_write;
+    ppp_ldisc.ioctl   = ppp_ioctl;
+    ppp_ldisc.select  = ppp_select;
+
+#ifdef NEW_TTY_DRIVERS
+    ppp_ldisc.magic       = TTY_LDISC_MAGIC;
+    ppp_ldisc.receive_buf = ppp_receive_buf;
+    ppp_ldisc.write_wakeup = ppp_write_wakeup;
+#else
+    ppp_ldisc.handler     = ppp_tty_input_ready;
+#endif
+
+    if ((i = tty_register_ldisc(N_PPP, &ppp_ldisc)) == 0)
+      printk(KERN_INFO "PPP line discipline registered.\n");
+    else
+      printk(KERN_ERR "error registering line discipline: %d\n", i);
+  }
+
+  /* initialize PPP control block */
+  ppp_init_ctrl_blk (ppp);
+  ppp->inuse = 0;
+  ppp->line  = dev->base_addr;
+  ppp->tty   = NULL;
+  ppp->dev   = dev;
+
+  /* clear statistics */
+  memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
+
+  /* device INFO */
+  dev->mtu             = PPP_MTU;
+  dev->hard_start_xmit = ppp_xmit;
+  dev->open            = ppp_dev_open;
+  dev->stop            = ppp_dev_close;
+  dev->get_stats       = ppp_get_stats;
+  dev->hard_header     = ppp_header;
+  dev->type_trans      = ppp_type_trans;
+  dev->rebuild_header  = ppp_rebuild_header;
+  dev->hard_header_len = 0;
+  dev->addr_len        = 0;
+  dev->type            = ARPHRD_PPP;
+
+#ifdef NET02D
+  dev->add_arp         = ppp_add_arp;
+  dev->queue_xmit      = dev_queue_xmit;
+#endif
+
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+    skb_queue_head_init(&dev->buffs[i]);  /* = NULL if NET02D */
+
+  /* New-style flags */
+  dev->flags      = IFF_POINTOPOINT;
+  dev->family     = AF_INET;
+  dev->pa_addr    = 0;
+  dev->pa_brdaddr = 0;
+  dev->pa_mask    = 0;
+  dev->pa_alen    = sizeof(unsigned long);
+
+  return 0;
+}
+
+static void
+ppp_init_ctrl_blk(struct ppp *ppp)
+{
+  ppp->magic		= PPP_MAGIC;
+  ppp->sending		= 0;
+  ppp->toss		= 0xFE;
+  ppp->escape		= 0;
+
+  ppp->flags		= 0;
+  ppp->mtu		= PPP_MTU;
+  ppp->mru		= PPP_MRU;
+  ppp->fcs		= 0;
+
+  memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
+  ppp->xmit_async_map[0] = 0xffffffff;
+  ppp->xmit_async_map[3] = 0x60000000;
+  ppp->recv_async_map	 = 0x00000000;
+
+  ppp->slcomp		= NULL;
+  ppp->rbuff		= NULL;
+  ppp->xbuff		= NULL;
+  ppp->cbuff		= NULL;
+
+  ppp->rhead		= NULL;
+  ppp->rend		= NULL;
+  ppp->rcount		= 0;
+  ppp->xhead		= NULL;
+  ppp->xtail		= NULL;
+
+  ppp->us_rbuff		= NULL;
+  ppp->us_rbuff_end	= NULL;
+  ppp->us_rbuff_head	= NULL;
+  ppp->us_rbuff_tail	= NULL;
+  ppp->read_wait	= NULL;
+  ppp->write_wait	= NULL;
+  ppp->us_rbuff_lock	= 0;
+  ppp->inp_sig		= 0;
+  ppp->inp_sig_pid	= 0;
+
+#ifdef OPTIMIZE_FLAG_TIME /* ensure flag will always be sent first time */
+  ppp->last_xmit	= jiffies - OPTIMIZE_FLAG_TIME;
+#else
+  ppp->last_xmit	= 0;
+#endif
+
+  /* clear statistics */
+  memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
+
+  /* Reset the demand dial information */
+  ppp->ddinfo.ip_sjiffies  =
+  ppp->ddinfo.ip_rjiffies  =
+  ppp->ddinfo.nip_sjiffies =
+  ppp->ddinfo.nip_rjiffies = jiffies;
+}
+
+/*
+ * MTU has been changed by the IP layer. Unfortunately we are not told
+ * about this, but we spot it ourselves and fix things up. We could be
+ * in an upcall from the tty driver, or in an ip packet queue.
+ */
+   
+static void
+ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
+{
+  struct device *dev;
+  unsigned char *new_rbuff, *new_xbuff, *new_cbuff;
+  unsigned char *old_rbuff, *old_xbuff, *old_cbuff;
+  int mtu, mru;
+/*
+ *  Allocate the buffer from the kernel for the data
+ */
+  dev = ppp->dev;
+  mru = new_mru;
+  mtu = new_mtu;
+
+  /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
+  if (mru < PPP_MRU)
+    mru = PPP_MRU;
+
+  mtu = (mtu * 2) + 20;
+  mru = (mru * 2) + 20;
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
+	      dev->name, new_mtu, new_mru));
+	
+  new_xbuff = (unsigned char *) kmalloc(mtu + 4, GFP_KERNEL);
+  new_rbuff = (unsigned char *) kmalloc(mru + 4, GFP_KERNEL);
+  new_cbuff = (unsigned char *) kmalloc(mru + 4, GFP_KERNEL);
+/*
+ *  If the buffers failed to allocate then complain.
+ */
+  if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL)
+    {
+      PRINTKN (2,(KERN_ERR "ppp: failed to allocate new buffers\n"));
+/*
+ *  Release new buffer pointers if the updates were not performed
+ */
+      if (new_rbuff != NULL)
+	kfree (new_rbuff);
+
+      if (new_xbuff != NULL)
+	kfree (new_xbuff);
+
+      if (new_cbuff != NULL)
+	kfree (new_cbuff);
+    }
+/*
+ *  Update the pointers to the new buffer structures.
+ */
+  else
+    {
+      cli();
+      old_xbuff       = ppp->xbuff;
+      old_rbuff       = ppp->rbuff;
+      old_cbuff       = ppp->cbuff;
+
+      ppp->xbuff      = new_xbuff;
+      ppp->rbuff      = new_rbuff;
+      ppp->cbuff      = new_cbuff;
+
+      dev->mem_start  = (unsigned long) new_xbuff;
+      dev->mem_end    = (unsigned long) (dev->mem_start + mtu);
+
+      dev->rmem_start = (unsigned long) new_rbuff;
+      ppp->rend       = (unsigned char *)
+      dev->rmem_end   = (unsigned long) (dev->rmem_start + mru);
+
+      ppp->rhead      = new_rbuff;
+/*
+ *  Update the parameters for the new buffer sizes
+ */
+      ppp->toss		= 0xFE;
+      ppp->escape	= 0;
+      ppp->sending	= 0;
+      ppp->rcount	= 0;
+
+      ppp->mru		= new_mru;
+
+      ppp->mtu		=
+      dev->mtu		= new_mtu;
+
+      sti();
+/*
+ *  Release old buffer pointers
+ */
+      if (old_rbuff != NULL)
+	kfree (old_rbuff);
+
+      if (old_xbuff != NULL)
+	kfree (old_xbuff);
+
+      if (old_cbuff != NULL)
+	kfree (old_cbuff);
+    }
+}
+
+/* called when we abandon the PPP line discipline */
+
+static void
+ppp_release(struct ppp *ppp)
+{
+#ifdef NEW_TTY_DRIVERS
+  if (ppp->tty != NULL && ppp->tty->disc_data == ppp)
+    ppp->tty->disc_data = NULL; /* Break the tty->ppp link */
+#endif
+
+  if (ppp->dev) {
+    ppp->dev->flags &= ~IFF_UP; /* down the device */
+    ppp->dev->flags |= IFF_POINTOPOINT;
+  }
+
+  kfree (ppp->xbuff);
+  kfree (ppp->cbuff);
+  kfree (ppp->rbuff);
+  kfree (ppp->us_rbuff);
+
+  ppp->xbuff    =
+  ppp->cbuff    =
+  ppp->rbuff    =
+  ppp->us_rbuff = NULL;
+
+  if (ppp->slcomp) {
+    slhc_free(ppp->slcomp);
+    ppp->slcomp = NULL;
+  }
+
+  ppp->inuse = 0;
+  ppp->tty   = NULL;
+}
+
+static void
+ppp_close(struct tty_struct *tty)
+{
+  struct ppp *ppp = ppp_find(tty);
+
+  if (ppp == NULL || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,(KERN_WARNING "ppp: trying to close unopened tty!\n"));
+  } else {
+    CHECK_PPP_VOID();
+    ppp_release (ppp);
+
+    PRINTKN (2,(KERN_INFO "ppp: channel %s closing.\n", ppp->dev->name));
+  }
+}
+
+/* called when PPP line discipline is selected on a tty */
+static int
+ppp_open(struct tty_struct *tty)
+{
+  struct ppp *ppp = ppp_find(tty);
+
+  if (ppp) {
+    PRINTKN (1,(KERN_ERR "ppp_open: gack! tty already associated to %s!\n",
+		ppp->magic == PPP_MAGIC ? ppp->dev->name : "unknown"));
+    return -EEXIST;
+  }
+
+  ppp = ppp_alloc();
+  if (ppp == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp_open: couldn't allocate ppp channel\n"));
+    return -ENFILE;
+  }
+
+  /* make sure the channel is actually open */
+  ppp_init_ctrl_blk (ppp);
+
+  ppp->tty = tty;
+
+#ifdef NEW_TTY_DRIVERS
+  tty->disc_data = ppp;
+  if (tty->driver.flush_buffer)
+    tty->driver.flush_buffer(tty);
+  if (tty->ldisc.flush_buffer)
+    tty->ldisc.flush_buffer(tty);
+#else
+  tty_read_flush (tty);
+  tty_write_flush (tty);
+#endif
+
+  if ((ppp->slcomp = slhc_init(16, 16)) == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
+    ppp_release (ppp);
+    return -ENOMEM;
+  }
+
+  /* Define the buffers for operation */
+  ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
+  if (ppp->rbuff == NULL) {
+    ppp_release (ppp);
+    return -ENOMEM;
+  }
+
+  /* Allocate a user-level receive buffer */
+  ppp->us_rbuff = kmalloc (RBUFSIZE, GFP_KERNEL);
+  if (ppp->us_rbuff == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: no space for user receive buffer\n"));
+    ppp_release (ppp);
+    return -ENOMEM;
+  }
+
+  ppp->us_rbuff_head =
+  ppp->us_rbuff_tail = ppp->us_rbuff;
+  ppp->us_rbuff_end  = ppp->us_rbuff + RBUFSIZE;
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name));
+
+#ifdef NEW_TTY_DRIVERS
+  return (0);
+#else
+  return (ppp->line);
+#endif
+}
+
+/* called when ppp interface goes "up".  here this just means we start
+   passing IP packets */
+static int
+ppp_dev_open(struct device *dev)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+
+  /* reset POINTOPOINT every time, since dev_close zaps it! */
+  dev->flags |= IFF_POINTOPOINT;
+
+  if (ppp->tty == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n",
+		dev->name));
+    return -ENXIO;
+  }
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s going up for IP packets!\n",
+	      dev->name));
+
+  CHECK_PPP(-ENXIO);
+  return 0;
+}
+
+static int
+ppp_dev_close(struct device *dev)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+
+  if (ppp->tty == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go down!\n",
+		dev->name));
+    return -ENXIO;
+  }
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n",
+	      dev->name));
+  CHECK_PPP(-ENXIO);
+  return 0;
+}
+
+/*************************************************************
+ * TTY OUTPUT
+ *    The following function delivers a fully-formed PPP
+ *    frame in ppp->xbuff to the TTY for output.
+ *************************************************************/
+
+#ifdef NEW_TTY_DRIVERS
+static inline void
+#else
+static void
+#endif
+ppp_output_done (void *ppp)
+{
+  /* unlock the transmitter queue */
+  ppp_unlock ((struct ppp *) ppp);
+
+  /* If the device is still up then enable the transmitter of the
+     next frame. */
+  if (((struct ppp *) ppp)->dev->flags & IFF_UP)
+    dev_tint (((struct ppp *) ppp)->dev);
+
+  /* enable any blocked process pending transmission */
+  wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
+}
+
+#ifndef NEW_TTY_DRIVERS
+static void
+ppp_kick_tty (struct ppp *ppp)
+{
+  register int count = ppp->xhead - ppp->xbuff;
+  register int answer;
+
+  ppp->stats.sbytes += count;
+
+  answer = tty_write_data (ppp->tty,
+			   ppp->xbuff,
+			   count,
+			   ppp_output_done,
+			   (void *) ppp);
+
+  if (answer == 0)
+    ppp_output_done (ppp);   /* Should not happen */
+  else
+    if (answer < 0) {
+      ppp->stats.serrors++;
+      ppp_output_done (ppp); /* unlock the transmitter */
+    }
+}
+
+#else
+
+static void
+ppp_kick_tty (struct ppp *ppp)
+{
+	register int count, actual;
+	
+	count = ppp->xhead - ppp->xbuff;
+	
+	actual = ppp->tty->driver.write(ppp->tty, 0, ppp->xbuff, count);
+	ppp->stats.sbytes += actual;
+	if (actual == count) {
+		ppp_output_done(ppp);
+	} else {
+		ppp->xtail = ppp->xbuff + actual;
+		ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	}
+}
+
+static void ppp_write_wakeup(struct tty_struct *tty)
+{
+	register int count, actual;
+	struct ppp *ppp = ppp_find(tty);
+
+	if (!ppp || ppp->magic != PPP_MAGIC) {
+		PRINTKN (1,
+			 (KERN_ERR "PPP: write_wakeup called but couldn't "
+			  "find PPP struct.\n"));
+		return;
+	}
+
+	if (!ppp->xtail || (ppp->flags & SC_XMIT_BUSY))
+		return;
+
+	cli();
+	if (ppp->flags & SC_XMIT_BUSY)
+		return;
+	ppp->flags |= SC_XMIT_BUSY;
+	sti();
+	
+	count = ppp->xhead - ppp->xtail;
+	
+	actual = tty->driver.write(tty, 0, ppp->xtail, count);
+	ppp->stats.sbytes += actual;
+	if (actual == count) {
+		ppp->xtail = 0;
+		tty->flags &= ~TTY_DO_WRITE_WAKEUP;
+
+		ppp_output_done(ppp);
+	} else {
+		ppp->xtail += actual;
+	}
+	ppp->flags &= ~SC_XMIT_BUSY;
+}
+#endif
+
+/*************************************************************
+ * TTY INPUT
+ *    The following functions handle input that arrives from
+ *    the TTY.  It recognizes PPP frames and either hands them
+ *    to the network layer or queues them for delivery to a
+ *    user process reading this TTY.
+ *************************************************************/
+
+/* stuff a single character into the receive buffer */
+
+inline void
+ppp_enqueue(struct ppp *ppp, unsigned char c)
+{
+  unsigned long flags;
+
+  save_flags(flags);
+  cli();
+  if (ppp->rhead < ppp->rend) {
+    *ppp->rhead = c;
+    ppp->rhead++;
+    ppp->rcount++;
+  } else
+    ppp->stats.roverrun++;
+  restore_flags(flags);
+}
+
+#ifdef CHECK_CHARACTERS
+static unsigned paritytab[8] = {
+    0x96696996, 0x69969669, 0x69969669, 0x96696996,
+    0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+#endif
+
+#ifndef NEW_TTY_DRIVERS
+static void
+ppp_dump_inqueue(struct tty_struct *tty)
+{
+  int  head = tty->read_q.head,
+       tail = tty->read_q.tail,
+       i, count;
+  char buffer[8];
+
+  PRINTK ((KERN_DEBUG "INQUEUE: head %d tail %d imode %x:\n", head, tail, 
+	   (unsigned int) tty->termios->c_iflag))
+
+  i     = tail;
+  count = 0;
+
+  while (i != head) {
+    buffer [count] = tty->read_q.buf[i];
+    if (++count == 8) {
+      ppp_print_buffer (NULL, buffer, 8, KERNEL_DS);
+      count = 0;
+    }
+    i = (i + 1) & (TTY_BUF_SIZE - 1);
+  }
+  ppp_print_buffer (NULL, buffer, count, KERNEL_DS);
+}
+
+/* called by lower levels of TTY driver when data becomes available.
+   all incoming data comes through this function. */
+
+void ppp_tty_input_ready(struct tty_struct *tty)
+{
+  struct ppp *ppp = ppp_find(tty);
+  int n, error;
+  unsigned char buff[128];
+
+/*  PRINTK( (KERN_DEBUG "PPP: handler called.\n") ) */
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,
+	     (KERN_ERR "PPP: handler called but couldn't find PPP struct.\n"));
+    return;
+  }
+
+  CHECK_PPP_VOID();
+
+  /* ZZZ */
+  if (ppp_debug >= 5)
+    ppp_dump_inqueue(ppp->tty);
+
+  do {
+    n = tty_read_raw_data(tty, buff, 128);
+    if ( n == 0 )		/* nothing there */
+      break;
+
+    if (ppp_debug >= 5)
+      ppp_print_buffer ("receive buffer", buff, n > 0 ? n : -n, KERNEL_DS);
+
+    if ( n < 0 ) {
+      /* Last character is error flag.
+	 Process the previous characters, then set toss flag. */
+      n = (-n) - 1;
+      error = buff[n];
+    } else error = 0;
+    ppp->stats.rbytes += n;
+    ppp_unesc(ppp,buff,n);
+    if (error)
+      ppp->toss = error;
+  } while (1);
+}
+
+/* recover frame by undoing PPP escape mechanism;
+   copies N chars of input data from C into PPP->rbuff
+   calls ppp_doframe to dispose of any frames it finds
+*/
+
+static void
+ppp_unesc(struct ppp *ppp, unsigned char *c, int n)
+{
+  int i;
+
+  for (i = 0; i < n; i++, c++) {
+    PRINTKN (6,(KERN_DEBUG "(%x)", (unsigned int) *c));
+
+#ifdef CHECK_CHARACTERS
+    if (*c & 0x80)
+	sc->sc_flags |= SC_RCV_B7_1;
+    else
+	sc->sc_flags |= SC_RCV_B7_0;
+
+    if (paritytab[*c >> 5] & (1 << (*c & 0x1F)))
+	sc->sc_flags |= SC_RCV_ODDP;
+    else
+	sc->sc_flags |= SC_RCV_EVNP;
+#endif
+
+    switch (*c) {
+    case PPP_ESC:		/* PPP_ESC: invert 0x20 in next character */
+      ppp->escape = PPP_TRANS;
+      break;
+
+    case PPP_FLAG:		/* PPP_FLAG: end of frame */
+      if (ppp->escape)		/* PPP_ESC just before PPP_FLAG is illegal */
+	ppp->toss = 0xFF;
+
+      if ((ppp->toss & 0x80) == 0)
+	ppp_doframe(ppp);	/* pass frame on to next layers */
+
+      ppp->rcount = 0;
+      ppp->rhead  = ppp->rbuff;
+      ppp->escape = 0;
+      ppp->toss   = 0;
+      break;
+
+    default:			/* regular character */
+      if (!in_rmap (ppp, *c)) {
+	if (ppp->toss == 0)
+	  ppp_enqueue (ppp, *c ^ ppp->escape);
+	ppp->escape = 0;
+      }
+      break;
+    }
+  }
+}
+
+#else
+static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
+			    char *fp, int count)
+{
+  register struct ppp *ppp = ppp_find (tty);
+  unsigned char c;
+ 
+/*  PRINTK( ("PPP: handler called.\n") ); */
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,("PPP: handler called but couldn't find "
+		"PPP struct.\n"));
+    return;
+  }
+
+  CHECK_PPP_VOID();
+ 
+  if (ppp_debug >= 5) {
+    ppp_print_buffer ("receive buffer", cp, count, KERNEL_DS);
+  }
+ 
+  while (count-- > 0) {
+    c = *cp++;
+
+    if (fp) {
+      if (*fp && ppp->toss == 0)
+	ppp->toss = *fp;
+      fp++;
+    }
+
+#ifdef CHECK_CHARACTERS
+    if (c & 0x80)
+	sc->sc_flags |= SC_RCV_B7_1;
+    else
+	sc->sc_flags |= SC_RCV_B7_0;
+
+    if (paritytab[c >> 5] & (1 << (c & 0x1F)))
+	sc->sc_flags |= SC_RCV_ODDP;
+    else
+	sc->sc_flags |= SC_RCV_EVNP;
+#endif
+
+    switch (c) {
+    case PPP_ESC:		/* PPP_ESC: invert 0x20 in next character */
+      ppp->escape = PPP_TRANS;
+      break;
+
+    case PPP_FLAG:		/* PPP_FLAG: end of frame */
+      if (ppp->escape)		/* PPP_ESC just before PPP_FLAG is "cancel"*/
+	ppp->toss = 0xFF;
+
+      if ((ppp->toss & 0x80) == 0)
+	ppp_doframe(ppp);	/* pass frame on to next layers */
+
+      ppp->rcount = 0;
+      ppp->rhead  = ppp->rbuff;
+      ppp->escape = 0;
+      ppp->toss   = 0;
+      break;
+
+    default:			/* regular character */
+      if (!in_rmap (ppp, c)) {
+	if (ppp->toss == 0)
+	  ppp_enqueue (ppp, c ^ ppp->escape);
+	ppp->escape = 0;
+      }
+    }
+  }
+}
+#endif
+
+/* on entry, a received frame is in ppp->rbuff
+   check it and dispose as appropriate */
+static void
+ppp_doframe(struct ppp *ppp)
+{
+  u_char *c = ppp->rbuff;
+  u_short proto;
+  int count = ppp->rcount;
+
+  /* forget it if we've already noticed an error */
+  if (ppp->toss) {
+    PRINTKN (1, (KERN_WARNING "ppp_toss: tossing frame, reason = %d\n",
+		 ppp->toss));
+    ppp->stats.rerrors++;
+    return;
+  }
+
+  /* do this before printing buffer to avoid generating copious output */
+  if (count == 0)
+    return;
+
+  if (ppp_debug >= 3)
+    ppp_print_buffer ("receive frame", c, count, KERNEL_DS);
+
+  if (count < 4) {
+    PRINTKN (1,(KERN_WARNING "ppp: got runt ppp frame, %d chars\n", count));
+    ppp->stats.runts++;
+    return;
+  }
+
+  /* check PPP error detection field */
+  if (!ppp_check_fcs(ppp)) {
+    PRINTKN (1,(KERN_WARNING "ppp: frame with bad fcs\n"));
+    ppp->stats.rerrors++;
+    return;
+  }
+
+  count -= 2;			/* ignore last two characters */
+
+  /* now we have a good frame */
+  /* figure out the protocol field */
+  if ((c[0] == PPP_ADDRESS) && (c[1] == PPP_CONTROL)) {
+    c = c + 2;			/* ADDR/CTRL not compressed, so skip */
+    count -= 2;
+  }
+
+  proto = (u_short) *c++;		/* PROTO compressed */
+  if (proto & 1) {
+    count--;
+  } else {
+    proto = (proto << 8) | (u_short) *c++; /* PROTO uncompressed */
+    count -= 2;
+  }
+
+  /* Send the frame to the network if the ppp device is up */
+  if ((ppp->dev->flags & IFF_UP) && ppp_do_ip(ppp, proto, c, count)) {
+    ppp->ddinfo.ip_rjiffies = jiffies;
+    return;
+  }
+
+  /* If we got here, it has to go to a user process doing a read,
+     so queue it.
+
+     User process expects to get whole frame (for some reason), so
+     use count+2 so as to include FCS field. */
+
+  if (ppp_us_queue (ppp, proto, c, count+2)) {
+    ppp->ddinfo.nip_rjiffies = jiffies;
+    ppp->stats.rothers++;
+    return;
+  }
+
+  /* couldn't cope. */
+  PRINTKN (1,(KERN_WARNING
+	      "ppp: dropping packet on the floor: nobody could take it.\n"));
+  ppp->stats.tossed++;
+}
+
+/* Examine packet at C, attempt to pass up to net layer. 
+   PROTO is the protocol field from the PPP frame.
+   Return 1 if could handle it, 0 otherwise.  */
+
+static int
+ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
+	  int count)
+{
+  int flags, done;
+
+  PRINTKN (4,(KERN_DEBUG "ppp_do_ip: proto %x len %d first byte %x\n",
+	      (int) proto, count, c[0]));
+
+  if (ppp_debug_netpackets) {
+    PRINTK (("KERN_DEBUG %s <-- proto %x len %d\n", ppp->dev->name,
+	     (int) proto, count));
+  }
+    
+  if (proto == PROTO_IP) {
+    ppp->stats.runcomp++;
+    goto sendit;
+  }
+
+  if ((proto == PROTO_VJCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
+    /* get space for uncompressing the header */
+    done = 0;
+    save_flags (flags);
+    cli();
+    if ((ppp->rhead + 80) < ppp->rend) {
+      ppp->rhead += 80;
+      ppp->rcount += 80;
+      done = 1;
+    }
+    restore_flags(flags);
+
+    if (! done)	{
+      PRINTKN (1,(KERN_NOTICE
+		  "ppp: no space to decompress VJ compressed TCP header.\n"));
+      ppp->stats.roverrun++;
+      return 1;
+    }
+
+    count = slhc_uncompress(ppp->slcomp, c, count);
+    if (count <= 0) {
+      ppp->stats.rerrors++;
+      PRINTKN (1,(KERN_NOTICE "ppp: error in VJ decompression\n"));
+      return 1;
+    }
+    ppp->stats.rcomp++;
+    goto sendit;
+  }
+  
+  if ((proto == PROTO_VJUNCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
+    if (slhc_remember(ppp->slcomp, c, count) <= 0) {
+      ppp->stats.rerrors++;
+      PRINTKN (1,(KERN_NOTICE "ppp: error in VJ memorizing\n"));
+      return 1;
+    }
+    ppp->stats.runcomp++;
+    goto sendit;
+  }
+
+  /* not ours */
+  return 0;
+
+ sendit:
+  if (ppp_debug_netpackets) {
+    struct iphdr *iph = (struct iphdr *) c;
+    PRINTK ((KERN_INFO "%s <--    src %lx dst %lx len %d\n", ppp->dev->name, 
+	     iph->saddr, iph->daddr, count))
+  }
+
+  /* receive the frame through the network software */
+  while ((dev_rint(c, count, 0, ppp->dev) & ~1) != 0)
+    ;
+
+  return 1;
+}
+
+/* stuff packet at BUF, length LEN, into the us_rbuff buffer
+   prepend PROTO information */
+
+#define PUTC(c,label) *ppp->us_rbuff_head++ = c; \
+                if (ppp->us_rbuff_head == ppp->us_rbuff_end) \
+                     ppp->us_rbuff_head = ppp->us_rbuff; \
+                if (ppp->us_rbuff_head == ppp->us_rbuff_tail) \
+                     goto label;
+#define GETC(c) c = *ppp->us_rbuff_tail++; \
+                if (ppp->us_rbuff_tail == ppp->us_rbuff_end) \
+                     ppp->us_rbuff_tail = ppp->us_rbuff;
+
+static int
+ppp_us_queue(struct ppp *ppp, unsigned short proto, 
+	     unsigned char *buf, int len)
+{
+  int totlen;
+  unsigned char *saved_head;
+
+  totlen = len+2;		/* including protocol */
+
+  if (set_bit(1, &ppp->us_rbuff_lock)) {
+    PRINTKN (1, (KERN_NOTICE "ppp_us_queue: can't get lock\n"));
+    return 0;
+  }
+  saved_head = ppp->us_rbuff_head;
+
+  PUTC((totlen & 0xff00) >> 8, failure);
+  PUTC(totlen & 0x00ff, failure);
+  PUTC((proto & 0xff00) >> 8, failure);
+  PUTC(proto & 0x00ff, failure);
+
+  while (len-- > 0) {
+    PUTC(*buf++, failure);
+  }
+
+  PRINTKN (3, (KERN_INFO "ppp: successfully queued %d bytes\n", totlen));
+  clear_bit(1, &ppp->us_rbuff_lock);
+  wake_up_interruptible (&ppp->read_wait);
+
+#ifdef NEW_TTY_DRIVERS
+  kill_fasync(ppp->tty->fasync, SIGIO);
+#endif
+
+  if (ppp->inp_sig && ppp->inp_sig_pid)
+    if (kill_proc (ppp->inp_sig_pid, ppp->inp_sig, 1) != 0) {
+      /* process is gone */
+      PRINTKN (2,(KERN_NOTICE
+		  "ppp: process that requested notification is gone\n"));
+      ppp->inp_sig = 0;
+      ppp->inp_sig_pid = 0;
+    }
+  return 1;
+
+ failure:
+  ppp->us_rbuff_head = saved_head;
+  clear_bit(1, &ppp->us_rbuff_lock);
+
+  PRINTKN (1, (KERN_NOTICE "ppp_us_queue: ran out of buffer space.\n"));
+
+  return 0;
+}
+
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ *    The following functions form support user programs
+ *    which read and write data on a TTY with the PPP line
+ *    discipline.  Reading is done from a circular queue,
+ *    filled by the lower TTY levels.
+ *************************************************************/
+
+/* read a PPP frame from the us_rbuff circular buffer, 
+   waiting if necessary
+*/
+
+static int
+ppp_read(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
+{
+  struct ppp *ppp = ppp_find(tty);
+  unsigned char c;
+  int len, i;
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,(KERN_ERR "ppp_read: cannnot find ppp channel\n"));
+    return -EIO;
+  }
+
+  CHECK_PPP(-ENXIO);
+
+  PRINTKN (4,(KERN_DEBUG "ppp_read: called %x num %u\n",
+	      (unsigned int) buf,
+	      nr));
+
+  do {
+    /* try to acquire read lock */
+    if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
+      /* got lock */
+      if (ppp->us_rbuff_head == ppp->us_rbuff_tail) {
+	/* no data */
+	PRINTKN (4,(KERN_DEBUG "ppp_read: no data\n"));
+	clear_bit(0, &ppp->us_rbuff_lock);
+        if (ppp->inp_sig) {
+	  PRINTKN (4,(KERN_DEBUG "ppp_read: EWOULDBLOCK\n"));
+	  return -EWOULDBLOCK;
+        } else goto wait;
+      }
+
+      /* reset the time of the last read operation */
+      ppp->ddinfo.nip_rjiffies = jiffies;
+
+      GETC (c); len = c << 8; GETC (c); len += c;
+
+      PRINTKN (4,(KERN_DEBUG "ppp_read: len = %d\n", len));
+
+      if (len + 2 > nr) {
+	/* frame too big; can't copy it, but do update us_rbuff_head */
+	PRINTKN (1,(KERN_DEBUG
+		    "ppp: read of %u bytes too small for %d frame\n",
+		    nr, len+2));
+	ppp->us_rbuff_head += len;
+	if (ppp->us_rbuff_head > ppp->us_rbuff_end)
+	  ppp->us_rbuff_head += - (ppp->us_rbuff_end - ppp->us_rbuff);
+	clear_bit(0, &ppp->us_rbuff_lock);
+	wake_up_interruptible (&ppp->read_wait);
+	ppp->stats.rgiants++;
+	return -EOVERFLOW;		/* ZZZ; HACK! */
+      } else {
+	/* have the space: copy the packet, faking the first two bytes */
+	put_fs_byte (PPP_ADDRESS, buf++);
+	put_fs_byte (PPP_CONTROL, buf++);
+	i = len;
+	while (i-- > 0) {
+	  GETC (c);
+	  put_fs_byte (c, buf++);
+	}
+      }
+
+      clear_bit(0, &ppp->us_rbuff_lock);
+      PRINTKN (3,(KERN_DEBUG "ppp_read: passing %d bytes up\n", len + 2));
+      ppp->stats.rothers++;
+      return len + 2;
+    }
+
+    /* need to wait */
+  wait:
+    current->timeout = 0;
+    PRINTKN (3,(KERN_DEBUG "ppp_read: sleeping\n"));
+    interruptible_sleep_on (&ppp->read_wait);
+    if (current->signal & ~current->blocked)
+      return -EINTR;
+  } while (1);
+}
+
+/* stuff a character into the transmit buffer, using PPP's way of escaping
+   special characters.
+   also, update ppp->fcs to take account of new character */
+static inline void
+ppp_stuff_char(struct ppp *ppp, unsigned char c)
+{
+  int curpt = ppp->xhead - ppp->xbuff;
+  if ((curpt < 0) || (curpt > 3000)) {
+    PRINTK ((KERN_DEBUG "ppp_stuff_char: %x %x %d\n",
+	     (unsigned int) ppp->xbuff, (unsigned int) ppp->xhead, curpt))
+  }
+  if (in_xmap (ppp, c)) {
+    *ppp->xhead++ = PPP_ESC;
+    *ppp->xhead++ = c ^ PPP_TRANS;
+  } else
+    *ppp->xhead++ = c;
+  ppp->fcs = (ppp->fcs >> 8) ^ fcstab[(ppp->fcs ^ c) & 0xff];
+}
+
+/* write a frame with NR chars from BUF to TTY
+   we have to put the FCS field on ourselves
+*/
+
+static int
+ppp_write(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
+{
+  struct ppp *ppp = ppp_find(tty);
+  int i;
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,(KERN_ERR "ppp_write: cannot find ppp unit\n"));
+    return -EIO;
+  }
+
+  CHECK_PPP(-ENXIO);
+  
+  if (ppp->mtu != ppp->dev->mtu)	/* Someone has been ifconfigging */
+    ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
+
+  if (nr > ppp->mtu) {
+    PRINTKN (1,(KERN_WARNING
+		"ppp_write: truncating user packet from %u to mtu %d\n",
+		nr, ppp->mtu));
+    nr = ppp->mtu;
+  }
+
+  if (ppp_debug >= 3)
+    ppp_print_buffer ("write frame", buf, nr, USER_DS);
+
+  /* lock this PPP unit so we will be the only writer;
+     sleep if necessary */
+  while ((ppp->sending == 1) || !ppp_lock(ppp)) {
+    current->timeout = 0;
+    PRINTKN (3,(KERN_DEBUG "ppp_write: sleeping\n"));
+    interruptible_sleep_on(&ppp->write_wait);
+    if (current->signal & ~current->blocked)
+      return -EINTR;
+  }
+
+  /* OK, locked.  Stuff the given bytes into the buffer. */
+
+  PRINTKN(4,(KERN_DEBUG "ppp_write: acquired write lock\n"));
+  ppp->xhead = ppp->xbuff;
+
+#ifdef OPTIMIZE_FLAG_TIME
+  if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
+    *ppp->xhead++ = PPP_FLAG;
+  ppp->last_xmit = jiffies;
+#else      
+  *ppp->xhead++ = PPP_FLAG;
+#endif
+
+  ppp->fcs = PPP_FCS_INIT;
+  i = nr;
+  while (i-- > 0)
+    ppp_stuff_char(ppp,get_fs_byte(buf++));
+
+  ppp_add_fcs(ppp);		/* concatenate FCS at end */
+
+  *ppp->xhead++ = PPP_FLAG;
+  
+  /* reset the time of the last write operation */
+  ppp->ddinfo.nip_sjiffies = jiffies;
+
+  if (ppp_debug >= 6)
+    ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
+  else {
+    PRINTKN (4,(KERN_DEBUG
+		"ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
+  }
+
+  /* packet is ready-to-go */
+  ++ppp->stats.sothers;
+  ppp_kick_tty(ppp);
+
+  return((int)nr);
+}
+ 
+static int
+ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
+	  unsigned long l)
+{
+  struct ppp *ppp = ppp_find(tty);
+  register int temp_i = 0;
+  int error;
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTK ((KERN_ERR "ppp_ioctl: can't find PPP block from tty!\n"))
+    return -EBADF;
+  }
+
+  CHECK_PPP(-ENXIO);
+
+  /* This must be root user */
+  if (!suser())
+    return -EPERM;
+
+  switch (i) {
+  case PPPIOCSMRU:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %x\n", temp_i));
+      temp_i = (int) get_fs_long (l);
+      if (ppp->mru != temp_i)
+	ppp_changedmtu (ppp, ppp->mtu, temp_i);
+    }
+    break;
+
+  case PPPIOCGFLAGS:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      temp_i = (ppp->flags & SC_MASK);
+#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
+      temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | SC_RCV_ODDP | SC_RCV_EVNP;
+#endif
+      put_fs_long ((long) temp_i, l);
+      PRINTKN (3,(KERN_DEBUG "ppp_ioctl: get flags: addr %lx flags %x\n",
+		  l,
+		  temp_i));
+    }
+    break;
+
+  case PPPIOCSFLAGS:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      temp_i      = (int) get_fs_long (l);
+      ppp->flags ^= ((ppp->flags ^ temp_i) & SC_MASK);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set flags to %x\n", temp_i));
+    }
+    break;
+
+  case PPPIOCGASYNCMAP:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      put_fs_long (ppp->xmit_async_map[0], l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get asyncmap: addr %lx asyncmap %lx\n",
+		  l, ppp->xmit_async_map[0]));
+    }
+    break;
+
+  case PPPIOCSASYNCMAP:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
+      ppp->xmit_async_map[0] = get_fs_long (l);
+      bset (ppp->xmit_async_map, PPP_FLAG);
+      bset (ppp->xmit_async_map, PPP_ESC);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set xmit asyncmap %lx\n",
+		  ppp->xmit_async_map[0]));
+    }
+    break;
+
+  case PPPIOCRASYNCMAP:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp->recv_async_map = get_fs_long (l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set recv asyncmap %lx\n",
+		  ppp->recv_async_map));
+    }
+    break;
+
+  case PPPIOCGUNIT:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      put_fs_long (ppp->dev->base_addr, l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %d", ppp->dev->base_addr));
+    }
+    break;
+
+  case PPPIOCSINPSIG:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp->inp_sig     = (int) get_fs_long (l);
+      ppp->inp_sig_pid = current->pid;
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set input signal %d\n", ppp->inp_sig));
+    }
+    break;
+
+  case PPPIOCSDEBUG:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp_debug = (int) get_fs_long (l);
+      ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8;
+      ppp_debug &= 0xff;
+      PRINTKN (1, (KERN_INFO "ppp_ioctl: set debug level %d, netpacket %d\n", 
+		   ppp_debug, ppp_debug_netpackets));
+    }
+    break;
+
+  case PPPIOCGDEBUG:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      put_fs_long ((long) (ppp_debug | (ppp_debug_netpackets << 8)), l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get debug level %d\n", 
+		  ppp_debug | (ppp_debug_netpackets << 8)));
+    }
+    break;
+
+  case PPPIOCGSTAT:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_stats));
+    if (error == 0) {
+      memcpy_tofs ((void *) l, &ppp->stats, sizeof (struct ppp_stats));
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: read statistics\n"));
+    }
+    break;
+
+  case PPPIOCGTIME:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_ddinfo));
+    if (error == 0) {
+      struct ppp_ddinfo cur_ddinfo;
+      unsigned long cur_jiffies = jiffies;
+
+      /* change absolute times to relative times. */
+      cur_ddinfo.ip_sjiffies  = cur_jiffies - ppp->ddinfo.ip_sjiffies;
+      cur_ddinfo.ip_rjiffies  = cur_jiffies - ppp->ddinfo.ip_rjiffies;
+      cur_ddinfo.nip_sjiffies = cur_jiffies - ppp->ddinfo.nip_sjiffies;
+      cur_ddinfo.nip_rjiffies = cur_jiffies - ppp->ddinfo.nip_rjiffies;
+      
+      memcpy_tofs ((void *) l, &cur_ddinfo, sizeof (struct ppp_ddinfo));
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: read demand dial info\n"));
+    }
+    break;
+
+  case PPPIOCGXASYNCMAP:
+    error = verify_area (VERIFY_WRITE,
+			 (void *) l,
+			 sizeof (ppp->xmit_async_map));
+    if (error == 0) {
+      memcpy_tofs ((void *) l,
+		   ppp->xmit_async_map,
+		   sizeof (ppp->xmit_async_map));
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get xasyncmap: addr %lx\n", l));
+    }
+    break;
+
+  case PPPIOCSXASYNCMAP:
+    error = verify_area (VERIFY_READ, (void *) l,
+			 sizeof (ppp->xmit_async_map));
+    if (error == 0) {
+      unsigned long temp_tbl [8];
+
+      memcpy_fromfs (temp_tbl, (void *) l, sizeof (ppp->xmit_async_map));
+      temp_tbl[1]  =  0x00000000; /* must not escape 0x20 - 0x3f */
+      temp_tbl[2] &= ~0x40000000; /* must not escape 0x5e        */
+      temp_tbl[3] |=  0x60000000; /* must escape 0x7d and 0x7e   */
+
+      if ((temp_tbl[2] & temp_tbl[3]) != 0 ||
+	  (temp_tbl[4] & temp_tbl[5]) != 0 ||
+	  (temp_tbl[6] & temp_tbl[7]) != 0)
+	error = -EINVAL;
+      else {
+	memcpy (ppp->xmit_async_map, temp_tbl, sizeof (ppp->xmit_async_map));
+	PRINTKN (3,(KERN_INFO "ppp_ioctl: set xasyncmap\n"));
+      }
+    }
+    break;
+
+  case PPPIOCSMAXCID:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      temp_i = (int) get_fs_long (l) + 1;
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set maxcid to %d\n", temp_i));
+      if (ppp->slcomp != NULL)
+	slhc_free (ppp->slcomp);
+
+      ppp->slcomp = slhc_init (temp_i, temp_i);
+
+      if (ppp->slcomp == NULL) {
+	PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
+	ppp_release (ppp);
+	error = -ENOMEM;
+      }
+    }
+    break;
+
+#ifdef NEW_TTY_DRIVERS
+    /* Allow stty to read, but not set, the serial port */
+  case TCGETS:
+  case TCGETA:
+    error = n_tty_ioctl(tty, file, i, l);
+    break;
+#endif
+
+/*
+ *  All other ioctl() events will come here.
+ */
+
+  default:
+    PRINTKN (1,(KERN_ERR "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+		i,
+		l));
+#ifdef NEW_TTY_DRIVERS
+    error = -ENOIOCTLCMD;
+#else
+    error = -EINVAL;
+#endif
+    break;
+  }
+  return error;
+}
+
+static int
+ppp_select (struct tty_struct *tty, struct inode * inode,
+	    struct file * filp, int sel_type, select_table * wait)
+{
+  struct ppp *ppp = ppp_find (tty);
+  
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTK ((KERN_ERR "ppp_select: can't find PPP block from tty!\n"))
+    return -EBADF;
+  }
+  
+  /* If the PPP protocol is no longer active, return false */
+  CHECK_PPP (0);
+  
+  /* Process the request based upon the type desired */
+  switch (sel_type) {
+  case SEL_IN:
+    if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
+      /* Test for the presence of data in the queue */
+      if (ppp->us_rbuff_head != ppp->us_rbuff_tail) {
+	clear_bit (0, &ppp->us_rbuff_lock);
+	return 1;
+      }
+      clear_bit (0, &ppp->us_rbuff_lock);
+    } /* fall through */
+
+  case SEL_EX:
+    /* Is there a pending error condition? */
+    if (tty->packet && tty->link->ctrl_status)
+      return 1;
+    
+    /* closed? */
+    if (tty->flags & (1 << TTY_SLAVE_CLOSED))
+      return 1;
+    
+    /* If the tty is disconnected, then this is an exception too */
+    if (tty_hung_up_p(filp))
+      return 1;
+
+    select_wait (&ppp->read_wait, wait);
+    break;
+    
+  case SEL_OUT:
+    if (ppp_lock (ppp)) {
+      if (ppp->sending == 0) {
+	ppp_unlock (ppp);
+	return 1;
+      }
+      ppp_unlock (ppp);
+    }
+    select_wait (&ppp->write_wait, wait);
+    break;
+  }
+  return 0;
+}
+
+/*************************************************************
+ * NETWORK OUTPUT
+ *    This routine accepts requests from the network layer
+ *    and attempts to deliver the packets.
+ *    It also includes various routines we are compelled to
+ *    have to make the network layer work (arp, etc...).
+ *************************************************************/
+
+int
+ppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+  struct tty_struct *tty;
+  struct ppp *ppp;
+  unsigned char *p;
+  unsigned short proto;
+  int len;
+
+  /* just a little sanity check. */
+  if (skb == NULL) {
+    PRINTKN(3,(KERN_WARNING "ppp_xmit: null packet!\n"));
+    return 0;
+  }
+
+  /* Get pointers to the various components */
+  ppp   = &ppp_ctrl[dev->base_addr];
+  tty   = ppp->tty;
+  p     = (unsigned char *) (skb + 1);
+  len   = skb->len;
+  proto = PROTO_IP;
+
+  PRINTKN(4,(KERN_DEBUG "ppp_xmit [%s]: skb %lX busy %d\n", dev->name, 
+	     (unsigned long int) skb, ppp->sending));
+
+  CHECK_PPP(0);
+
+  if (tty == NULL) {
+    PRINTKN(1,(KERN_ERR "ppp_xmit: %s not connected to a TTY!\n", dev->name));
+    goto done;
+  }
+
+  if (!(dev->flags & IFF_UP)) {
+    PRINTKN(1,(KERN_WARNING
+	       "ppp_xmit: packet sent on interface %s, which is down for IP\n",
+	       dev->name));
+    goto done;
+  }
+
+  /* get length from IP header as per Alan Cox bugfix for slip.c */
+  if (len < sizeof(struct iphdr)) {
+    PRINTKN(0,(KERN_ERR "ppp_xmit: given runt packet, ignoring\n"));
+    return 1;
+  }
+  len = ntohs( ((struct iphdr *)(skb->data)) -> tot_len );
+
+  /* If doing demand dial then divert the first frame to pppd. */
+  if (ppp->flags & SC_IP_DOWN) {
+    if (ppp->flags & SC_IP_FLUSH == 0) {
+      if (ppp_us_queue (ppp, proto, p, len))
+	ppp->flags |= SC_IP_FLUSH;
+    }
+    goto done;
+  }
+
+  /* Attempt to acquire send lock */
+  if (ppp->sending || !ppp_lock(ppp)) {
+    PRINTKN(3,(KERN_WARNING "ppp_xmit: busy\n"));
+    ppp->stats.sbusy++;
+    return 1;
+  }
+
+  ppp->xhead = ppp->xbuff;
+
+  /* try to compress, if VJ compression mode is on */
+  if (ppp->flags & SC_COMP_TCP) {
+    /* NOTE: last 0 argument says never to compress connection ID */
+    len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p, 0);
+    if (p[0] & SL_TYPE_COMPRESSED_TCP)
+      proto = PROTO_VJCOMP;
+    else {
+      if (p[0] >= SL_TYPE_UNCOMPRESSED_TCP) {
+	proto = PROTO_VJUNCOMP;
+	p[0] = (p[0] & 0x0f) | 0x40; 
+      }
+    }
+  }
+
+  /* increment appropriate counter */
+  if (proto == PROTO_VJCOMP)
+    ++ppp->stats.scomp;
+  else
+    ++ppp->stats.suncomp;
+      
+  if (ppp_debug_netpackets) {
+    struct iphdr *iph = (struct iphdr *) (skb + 1);
+    PRINTK ((KERN_DEBUG "%s ==> proto %x len %d src %x dst %x proto %d\n",
+	    dev->name, (int) proto, (int) len, (int) iph->saddr,
+	    (int) iph->daddr, (int) iph->protocol))
+  }
+
+  /* start of frame:   FLAG  ALL_STATIONS  CONTROL  <protohi> <protolo> */
+#ifdef OPTIMIZE_FLAG_TIME
+  if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
+    *ppp->xhead++ = PPP_FLAG;
+  ppp->last_xmit = jiffies;
+#else      
+  *ppp->xhead++ = PPP_FLAG;
+#endif
+
+  ppp->fcs = PPP_FCS_INIT;
+  if (!(ppp->flags & SC_COMP_AC)) { 
+    ppp_stuff_char(ppp, PPP_ADDRESS);
+    ppp_stuff_char(ppp, PPP_CONTROL);
+  }
+
+  if (!(ppp->flags & SC_COMP_PROT) || (proto & 0xff00))
+    ppp_stuff_char(ppp, proto>>8);
+  ppp_stuff_char(ppp, proto&0xff);
+
+  /* data part */
+  while (len-- > 0)
+    ppp_stuff_char(ppp, *p++);
+
+  /* fcs and flag */
+  ppp_add_fcs(ppp);
+  *ppp->xhead++ = PPP_FLAG;
+
+  /* update the time for demand dial function */
+  ppp->ddinfo.ip_sjiffies = jiffies;
+
+  /* send it! */
+  if (ppp_debug >= 6)
+    ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
+  else {
+    PRINTKN (4,(KERN_DEBUG
+		"ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
+  }
+
+  ppp_kick_tty(ppp);
+
+ done:
+  if (skb->free) 
+    kfree_skb(skb, FREE_WRITE);
+  return 0;
+}
+  
+static unsigned short
+ppp_type_trans (struct sk_buff *skb, struct device *dev)
+{
+  return(htons(ETH_P_IP));
+}
+
+#ifdef NET02D
+static int
+ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
+	   unsigned long daddr, unsigned long saddr, unsigned len)
+{
+  return(0);
+}
+
+static int
+ppp_rebuild_header(void *buff, struct device *dev)
+{
+  return(0);
+}
+
+static void
+ppp_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
+{
+}
+
+#else
+
+static int
+ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
+	   void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
+{
+  return(0);
+}
+
+static int
+ppp_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+		   struct sk_buff *skb)
+{
+  return(0);
+}
+#endif
+
+static struct enet_statistics *
+ppp_get_stats (struct device *dev)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+  static struct enet_statistics ppp_stats;
+
+  ppp_stats.rx_packets = ppp->stats.rcomp + ppp->stats.runcomp;
+  ppp_stats.rx_errors = ppp->stats.rerrors;
+  ppp_stats.rx_dropped = ppp->stats.tossed;
+  ppp_stats.rx_fifo_errors = 0;
+  ppp_stats.rx_length_errors = ppp->stats.runts;
+  ppp_stats.rx_over_errors = ppp->stats.roverrun;
+  ppp_stats.rx_crc_errors = 0;
+  ppp_stats.rx_frame_errors = 0;
+  ppp_stats.tx_packets = ppp->stats.scomp + ppp->stats.suncomp;
+  ppp_stats.tx_errors = ppp->stats.serrors;
+  ppp_stats.tx_dropped = 0;
+  ppp_stats.tx_fifo_errors = 0;
+  ppp_stats.collisions = ppp->stats.sbusy;
+  ppp_stats.tx_carrier_errors = 0;
+  ppp_stats.tx_aborted_errors = 0;
+  ppp_stats.tx_window_errors = 0;
+  ppp_stats.tx_heartbeat_errors = 0;
+
+  PRINTKN (3, (KERN_INFO "ppp_get_stats called"));
+  return &ppp_stats;
+}
+
+/*************************************************************
+ * UTILITIES
+ *    Miscellany called by various functions above.
+ *************************************************************/
+
+#ifndef NEW_TTY_DRIVERS
+/* find a PPP channel given a TTY */
+struct ppp *
+ppp_find(struct tty_struct *tty)
+{
+  int i;
+  for (i = 0; i < PPP_NRUNIT; i++)
+    if (ppp_ctrl[i].inuse && (ppp_ctrl[i].tty == tty)) return &ppp_ctrl[i];
+
+  return NULL;
+}
+#endif
+
+/* allocate a PPP channel */
+static struct ppp *
+ppp_alloc(void)
+{
+  int i;
+  for (i = 0; i < PPP_NRUNIT; i++)
+    if (!set_bit(0, &ppp_ctrl[i].inuse)) return &ppp_ctrl[i];
+
+  return NULL;
+}
+
+/* marks a PPP interface 'busy'.  user processes will wait, if
+   they try to write, and the network code will refrain from sending
+   return nonzero if succeeded in acquiring lock
+*/
+
+static int
+ppp_lock(struct ppp *ppp)
+{
+  int flags, locked;
+  save_flags(flags);
+  cli();
+  locked = ppp->sending;
+  ppp->sending = 1;
+  if (ppp->dev->flags & IFF_UP)
+    ppp->dev->tbusy = 1;
+  restore_flags(flags);
+  return locked == 0;
+}
+
+static void
+ppp_unlock(struct ppp *ppp)
+{
+  int flags;
+  save_flags(flags);
+  cli();
+  ppp->sending = 0;
+  if (ppp->dev->flags & IFF_UP)
+    ppp->dev->tbusy = 0;
+  restore_flags(flags);
+}
+
+/* FCS support functions */
+
+static void
+ppp_add_fcs(struct ppp *ppp)
+{
+  unsigned short fcs = ppp->fcs;
+
+  fcs ^= 0xffff;
+  ppp_stuff_char(ppp, fcs & 0x00ff);
+  ppp_stuff_char(ppp, (fcs & 0xff00) >> 8);
+  ASSERT (ppp->fcs == PPP_FCS_GOOD);
+  PRINTKN (4,(KERN_DEBUG "ppp_add_fcs: fcs is %lx\n",
+	      (long) (unsigned long) fcs));
+}
+
+static int
+ppp_check_fcs(struct ppp *ppp)
+{
+  unsigned short fcs = PPP_FCS_INIT, msgfcs;
+  unsigned char *c = ppp->rbuff;
+  int i;
+
+  for (i = 0; i < ppp->rcount - 2; i++, c++)
+    fcs = (fcs >> 8) ^ fcstab[(fcs ^ *c) & 0xff];
+
+  fcs ^= 0xffff;
+  msgfcs = (c[1] << 8) + c[0];
+  PRINTKN (4,(KERN_INFO "ppp_check_fcs: got %lx want %lx\n",
+	      (unsigned long) msgfcs, (unsigned long) fcs));
+  return fcs == msgfcs;
+}
+
+static char hex[] = "0123456789ABCDEF";
+
+inline void ppp_print_hex (register char *out, char *in, int count);
+inline void ppp_print_hex (register char *out, char *in, int count)
+{
+  register unsigned char next_ch;
+
+  while (count-- > 0) {
+    next_ch = (unsigned char) get_fs_byte (in);
+
+    *out++  = hex[(next_ch >> 4) & 0x0F];
+    *out++  = hex[next_ch        & 0x0F];
+    ++out;
+    ++in;
+  }
+}
+
+inline void ppp_print_char (register char *out, char *in, int count);
+inline void ppp_print_char (register char *out, char *in, int count)
+{
+  register unsigned char next_ch;
+
+  while (count-- > 0) {
+    next_ch = (unsigned char) get_fs_byte (in);
+
+    if (next_ch < 0x20 || next_ch > 0x7e)
+      *out++ = '.';
+    else {
+      *out++ = next_ch;
+      if (next_ch == '%')	/* printk/syslogd has a bug !! */
+	*out++ = '%';
+    }
+    ++in;
+  }
+  *out = '\0';
+}
+
+static void ppp_print_buffer(const char *name, char *buf, int count, int seg)
+{
+  char line [44];
+  int  old_fs = get_fs();
+
+  set_fs (seg);
+
+  if (name != (char *) NULL)
+    PRINTK ((KERN_DEBUG "ppp: %s, count = %d\n", name, count));
+
+  while (count > 8) {
+    memset         (line, ' ', sizeof (line));
+    ppp_print_hex  (line, buf, 8);
+    ppp_print_char (&line[8 * 3], buf, 8);
+    PRINTK ((KERN_DEBUG "%s\n", line));
+    count -= 8;
+    buf   += 8;
+  }
+
+  if (count > 0) {
+    memset         (line, ' ', sizeof (line));
+    ppp_print_hex  (line, buf, count);
+    ppp_print_char (&line[8 * 3], buf, count);
+    PRINTK ((KERN_DEBUG "%s\n", line));
+  }
+
+  set_fs (old_fs);
+}
diff -u --recursive --new-file v1.1.12/linux/drivers/net/skeleton.c linux/drivers/net/skeleton.c
--- v1.1.12/linux/drivers/net/skeleton.c	Tue Apr 19 10:53:45 1994
+++ linux/drivers/net/skeleton.c	Mon May 23 08:16:53 1994
@@ -330,7 +330,7 @@
 		if (status /*& TX_INTR*/) {
 			lp->stats.tx_packets++;
 			dev->tbusy = 0;
-			mark_bh(INET_BH);	/* Inform upper layers. */
+			mark_bh(NET_BH);	/* Inform upper layers. */
 		}
 		if (status /*& COUNTERS_INTR*/) {
 			/* Increment the appropriate 'localstats' field. */
@@ -387,7 +387,7 @@
 	} while (--boguscount);
 
 	/* If any worth-while packets have been received, dev_rint()
-	   has done a mark_bh(INET_BH) for us and will work on them
+	   has done a mark_bh(NET_BH) for us and will work on them
 	   when we get to the bottom-half routine. */
 	return;
 }
diff -u --recursive --new-file v1.1.12/linux/drivers/net/slhc.c linux/drivers/net/slhc.c
--- v1.1.12/linux/drivers/net/slhc.c	Wed Apr 27 09:46:06 1994
+++ linux/drivers/net/slhc.c	Mon May 23 08:16:53 1994
@@ -69,8 +69,6 @@
 #include <linux/mm.h>
 #include "slhc.h"
 
-#define DPRINT(x)
-
 int last_retran;
 
 static unsigned char *encode(unsigned char *cp, unsigned short n);
@@ -236,8 +234,6 @@
 	/* Bail if this packet isn't TCP, or is an IP fragment */
 	if(ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x1fff) || 
 				       (ip->frag_off & 32)){
-		DPRINT(("comp: noncomp 1 %d %d %d\n", ip->protocol, 
-		      ntohs(ip->frag_off), ip->frag_off));
 		/* Send as regular IP */
 		if(ip->protocol != IPPROTO_TCP)
 			comp->sls_o_nontcp++;
@@ -255,8 +251,6 @@
 	 */
 	if(th->syn || th->fin || th->rst ||
 	    ! (th->ack)){
-		DPRINT(("comp: noncomp 2 %x %x %d %d %d %d\n", ip, th, 
-		       th->syn, th->fin, th->rst, th->ack));
 		/* TCP connection stuff; send as regular IP */
 		comp->sls_o_tcp++;
 		return isize;
@@ -300,7 +294,6 @@
 	 */
 	comp->sls_o_misses++;
 	comp->xmit_oldest = lcs->cs_this;
-	DPRINT(("comp: not found\n"));
 	goto uncompressed;
 
 found:
@@ -341,7 +334,6 @@
 	 || th->doff != cs->cs_tcp.doff
 	 || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
 	 || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4 != 0))){
-		DPRINT(("comp: incompat\n"));
 		goto uncompressed;
 	}
 	
@@ -360,7 +352,6 @@
 		 * implementation should never do this but RFC793
 		 * doesn't prohibit the change so we have to deal
 		 * with it. */
-		DPRINT(("comp: urg incompat\n"));
 		goto uncompressed;
 	}
 	if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
@@ -391,7 +382,6 @@
 		if(ip->tot_len != cs->cs_ip.tot_len && 
 		   ntohs(cs->cs_ip.tot_len) == hlen)
 			break;
-		DPRINT(("comp: retrans\n"));
 		goto uncompressed;
 		break;
 	case SPECIAL_I:
@@ -399,7 +389,6 @@
 		/* actual changes match one of our special case encodings --
 		 * send packet uncompressed.
 		 */
-		DPRINT(("comp: special\n"));
 		goto uncompressed;
 	case NEW_S|NEW_A:
 		if(deltaS == deltaA &&
@@ -450,7 +439,6 @@
 	}
 	cp = put16(cp,(short)deltaA);	/* Write TCP checksum */
 /* deltaS is now the size of the change section of the compressed header */
-	DPRINT(("comp: %x %x %x %d %d\n", icp, cp, new_seq, hlen, deltaS));
 	memcpy(cp,new_seq,deltaS);	/* Write list of deltas */
 	memcpy(cp+deltaS,icp+hlen,isize-hlen);
 	comp->sls_o_compressed++;
@@ -493,7 +481,6 @@
 	comp->sls_i_compressed++;
 	if(isize < 3){
 		comp->sls_i_error++;
-		DPRINT(("uncomp: runt\n"));
 		return 0;
 	}
 	changes = *cp++;
@@ -513,7 +500,6 @@
 		 * explicit state index, we have to toss the packet. */
 		if(comp->flags & SLF_TOSS){
 			comp->sls_i_tossed++;
-			DPRINT(("uncomp: toss\n"));
 			return 0;
 		}
 	}
@@ -522,7 +508,6 @@
 	ip = &cs->cs_ip;
 
 	if((x = pull16(&cp)) == -1) {	/* Read the TCP checksum */
-		DPRINT(("uncomp: bad tcp chk\n"));
 		goto bad;
         }
 	thp->check = htons(x);
@@ -555,7 +540,6 @@
 		if(changes & NEW_U){
 			thp->urg = 1;
 			if((x = decode(&cp)) == -1) {
-				DPRINT(("uncomp: bad U\n"));
 				goto bad;
 			}
 			thp->urg_ptr = htons(x);
@@ -563,21 +547,18 @@
 			thp->urg = 0;
 		if(changes & NEW_W){
 			if((x = decode(&cp)) == -1) {
-				DPRINT(("uncomp: bad W\n"));
 				goto bad;
 			}	
 			thp->window = htons( ntohs(thp->window) + x);
 		}
 		if(changes & NEW_A){
 			if((x = decode(&cp)) == -1) {
-				DPRINT(("uncomp: bad A\n"));
 				goto bad;
 			}
 			thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
 		}
 		if(changes & NEW_S){
 			if((x = decode(&cp)) == -1) {
-				DPRINT(("uncomp: bad S\n"));
 				goto bad;
 			}
 			thp->seq = htonl( ntohl(thp->seq) + x);
@@ -586,7 +567,6 @@
 	}
 	if(changes & NEW_I){
 		if((x = decode(&cp)) == -1) {
-			DPRINT(("uncomp: bad I\n"));
 			goto bad;
 		}
 		ip->id = htons (ntohs (ip->id) + x);
@@ -606,8 +586,6 @@
 	ip->tot_len = htons(len);
 	ip->check = 0;
 
-	DPRINT(("uncomp: %d %d %d %d\n", cp - icp, hdrlen, isize, len));
-
 	memmove(icp + hdrlen, cp, len - hdrlen);
 
 	cp = icp;
@@ -629,7 +607,6 @@
 	  cp += ((thp->doff) - 5) * 4;
 	}
 
-if (inet_debug == DBG_SLIP) printk("\runcomp: change %x len %d\n", changes, len);
 	return len;
 bad:
 	comp->sls_i_error++;
diff -u --recursive --new-file v1.1.12/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.1.12/linux/drivers/net/slip.c	Mon May 23 12:14:22 1994
+++ linux/drivers/net/slip.c	Mon May 23 08:16:53 1994
@@ -64,83 +64,18 @@
 #include "slhc.h"
 #endif
 
-#define	SLIP_VERSION	"0.7.5"
+#define	SLIP_VERSION	"0.7.5-NET3.014-NEWTTY"
 
-/* Define some IP layer stuff.  Not all systems have it. */
-#ifdef SL_DUMP
-#   define	IP_VERSION	4	/* version# of our IP software	*/
-#   define	IPF_F_OFFSET	0x1fff	/* Offset field			*/
-#   define	IPF_DF		0x4000	/* Don't fragment flag		*/
-#   define	IPF_MF		0x2000	/* More Fragments flag		*/
-#   define	IP_OF_COPIED	0x80	/* Copied-on-fragmentation flag	*/
-#   define	IP_OF_CLASS	0x60	/* Option class			*/
-#   define	IP_OF_NUMBER	0x1f	/* Option number		*/
-#endif
-
 
 static struct slip	sl_ctrl[SL_NRUNIT];
 static struct tty_ldisc	sl_ldisc;
 static int		already = 0;
 
-
-/* Dump the contents of an IP datagram. */
-static void
-ip_dump(unsigned char *ptr, int len)
-{
-#ifdef SL_DUMP
-  struct iphdr *ip;
-  struct tcphdr *th;
-  int dlen, doff;
-
-  if (inet_debug != DBG_SLIP) return;
-
-  ip = (struct iphdr *) ptr;
-  th = (struct tcphdr *) (ptr + ip->ihl * 4);
-  printk("\r%s -> %s seq %lx ack %lx len %d\n",
-	 in_ntoa(ip->saddr), in_ntoa(ip->daddr), 
-	 ntohl(th->seq), ntohl(th->ack_seq), ntohs(ip->tot_len));
-  return;
-
-  printk("\r*****\n");
-  printk("%p %d\n", ptr, len);
-  ip = (struct iphdr *) ptr;
-  dlen = ntohs(ip->tot_len);
-  doff = ((ntohs(ip->frag_off) & IPF_F_OFFSET) << 3);
-
-
-  printk("SLIP: %s->", in_ntoa(ip->saddr));
-  printk("%s\n", in_ntoa(ip->daddr));
-  printk(" len %u ihl %u ver %u ttl %u prot %u",
-	dlen, ip->ihl, ip->version, ip->ttl, ip->protocol);
-
-  if (ip->tos != 0) printk(" tos %u", ip->tos);
-  if (doff != 0 || (ntohs(ip->frag_off) & IPF_MF))
-	printk(" id %u offs %u", ntohs(ip->id), doff);
-
-  if (ntohs(ip->frag_off) & IPF_DF) printk(" DF");
-  if (ntohs(ip->frag_off) & IPF_MF) printk(" MF");
-  printk("\n*****\n");
-#endif
-}
-
-#if 0
-void clh_dump(unsigned char *cp, int len)
-{
-  if (len > 60)
-    len = 60;
-  printk("%d:", len);
-  while (len > 0) {
-    printk(" %x", *cp++);
-    len--;
-  }
-  printk("\n\n");
-}
-#endif
-
 /* Initialize a SLIP control block for use. */
 static void
 sl_initialize(struct slip *sl, struct device *dev)
 {
+  sl->magic		= SLIP_MAGIC;
   sl->inuse		= 0;
   sl->sending		= 0;
   sl->escape		= 0;
@@ -172,25 +107,10 @@
   dev->mem_end		= (unsigned long) NULL;
   dev->mem_start	= (unsigned long) NULL;
   dev->type		= ARPHRD_SLIP + sl->mode;
+  if(dev->type == 260)	/* KISS */
+  	dev->type=ARPHRD_AX25;
 }
 
-
-/* Find a SLIP channel from its `tty' link. */
-static struct slip *
-sl_find(struct tty_struct *tty)
-{
-  struct slip *sl;
-  int i;
-
-  if (tty == NULL) return(NULL);
-  for (i = 0; i < SL_NRUNIT; i++) {
-	sl = &sl_ctrl[i];
-	if (sl->tty == tty) return(sl);
-  }
-  return(NULL);
-}
-
-
 /* Find a free SLIP channel, and link in this `tty' line. */
 static inline struct slip *
 sl_alloc(void)
@@ -251,8 +171,6 @@
 	if (l < (576 * 2))
 	  l = 576 * 2;
 	
-	DPRINTF((DBG_SLIP,"SLIP: mtu changed!\n"));
-	
 	tb= (unsigned char *) kmalloc(l + 4, GFP_ATOMIC);
 	rb= (unsigned char *) kmalloc(l + 4, GFP_ATOMIC);
 	cb= (unsigned char *) kmalloc(l + 4, GFP_ATOMIC);
@@ -417,13 +335,9 @@
     }
   }
 
-  DPRINTF((DBG_SLIP, "<< \"%s\" recv:\r\n", sl->dev->name));
-  ip_dump(sl->rbuff, sl->rcount);
 #endif
   /* Bump the datagram to the upper layers... */
   do {
-	DPRINTF((DBG_SLIP, "SLIP: packet is %d at 0x%X\n",
-					sl->rcount, sl->rbuff));
 	/* clh_dump(sl->rbuff, count); */
 	done = dev_rint(sl->rbuff, count, 0, sl->dev);
 	if (done == 0 || done == 1) break;
@@ -432,28 +346,13 @@
   sl->rpacket++;
 }
 
-
-/* TTY finished sending a datagram, so clean up. */
-static void
-sl_next(struct slip *sl)
-{
-  DPRINTF((DBG_SLIP, "SLIP: sl_next(0x%X) called!\n", sl));
-  sl_unlock(sl);
-  dev_tint(sl->dev);
-}
-
-
 /* Encapsulate one IP datagram and stuff into a TTY queue. */
 static void
 sl_encaps(struct slip *sl, unsigned char *icp, int len)
 {
-  unsigned char *bp, *p;
-  int count;
+  unsigned char *p;
+  int actual, count;
 
-  DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", icp, len));
-  DPRINTF((DBG_SLIP, ">> \"%s\" sent:\r\n", sl->dev->name));
-  
-  ip_dump(icp, len);
   
   if(sl->mtu != sl->dev->mtu)	/* Someone has been ifconfigging */
   	sl_changedmtu(sl);
@@ -474,17 +373,57 @@
   else
   	count=slip_esc(p, (unsigned char *)sl->xbuff,len);
   sl->spacket++;
-  bp = sl->xbuff;
 
   /* Tell TTY to send it on its way. */
-  DPRINTF((DBG_SLIP, "SLIP: kicking TTY for %d bytes at 0x%X\n", count, bp));
-  if (tty_write_data(sl->tty, (char *) bp, count,
-	     (void (*)(void *))sl_next, (void *) sl) == 0) {
-	DPRINTF((DBG_SLIP, "SLIP: TTY already done with %d bytes!\n", count));
-	sl_next(sl);
+  actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
+  if (actual == count) {
+	  sl_unlock(sl);
+	  dev_tint(sl->dev);
+  } else {
+	  sl->xhead = sl->xbuff + count;
+	  sl->xtail = sl->xbuff + actual;
+	  sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
   }
 }
 
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void slip_write_wakeup(struct tty_struct *tty)
+{
+	register int count, answer;
+	struct slip *sl = (struct slip *) tty->disc_data;
+
+	/* First make sure we're connected. */
+	if (!sl || sl->magic != SLIP_MAGIC) {
+		return;
+	}
+
+	if (!sl->xtail || (sl->flags & SLF_XMIT_BUSY))
+		return;
+
+	cli();
+	if (sl->flags & SLF_XMIT_BUSY)
+		return;
+	sl->flags |= SLF_XMIT_BUSY;
+	sti();
+	
+	count = sl->xhead - sl->xtail;
+
+	answer = tty->driver.write(tty, 0, sl->xtail, count);
+	if (answer == count) {
+		sl->xtail = 0;
+		tty->flags &= ~TTY_DO_WRITE_WAKEUP;
+
+		sl_unlock(sl);
+		dev_tint(sl->dev);
+	} else {
+		sl->xtail += answer;
+	}
+	sl->flags &= ~SLF_XMIT_BUSY;
+}
+
 /*static void sl_hex_dump(unsigned char *x,int l)
 {
 	int n=0;
@@ -512,8 +451,6 @@
   /* Find the correct SLIP channel to use. */
   sl = &sl_ctrl[dev->base_addr];
   tty = sl->tty;
-  DPRINTF((DBG_SLIP, "SLIP: sl_xmit(\"%s\") skb=0x%X busy=%d\n",
-				dev->name, skb, sl->sending));
 
   /*
    * If we are busy already- too bad.  We ought to be able
@@ -521,7 +458,6 @@
    * frame buffer.  Oh well...
    */
   if (sl->sending) {
-	DPRINTF((DBG_SLIP, "SLIP: sl_xmit: BUSY\r\n"));
 	sl->sbusy++;
 	return(1);
   }
@@ -545,7 +481,7 @@
 	sl_lock(sl);
 	
 	size=skb->len;
-	
+#if 0	
 	if(!(sl->mode&SL_MODE_AX25))
 	{
 		if(size<sizeof(struct iphdr))
@@ -559,6 +495,7 @@
 		/*	sl_hex_dump(skb->data,skb->len);*/
 		}
 	}
+#endif	
 	sl_encaps(sl, skb->data, size);
 	if (skb->free) 
 		kfree_skb(skb, FREE_WRITE);
@@ -586,10 +523,12 @@
 	  void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
 {
 #ifdef CONFIG_AX25
+#ifdef CONFIG_INET
   struct slip *sl=&sl_ctrl[dev->base_addr];
   if((sl->mode&SL_MODE_AX25) && type!=htons(ETH_P_AX25))
   	return ax25_encapsulate(buff,dev,type,daddr,saddr,len,skb);
 #endif  
+#endif
 
   return(0);
 }
@@ -601,10 +540,12 @@
 		struct sk_buff *skb)
 {
 #ifdef CONFIG_AX25
+#ifdef CONFIG_INET
   struct slip *sl=&sl_ctrl[dev->base_addr];
   
   if(sl->mode&SL_MODE_AX25)
   	return ax25_rebuild_header(buff,dev,raddr, skb);
+#endif  	
 #endif  
   return(0);
 }
@@ -620,7 +561,6 @@
 
   sl = &sl_ctrl[dev->base_addr];
   if (sl->tty == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: channel %d not connected!\n", sl->line));
 	return(-ENXIO);
   }
   sl->dev = dev;
@@ -644,7 +584,6 @@
 
   p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
   if (p == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP XMIT buffer!\n"));
 	return(-ENOMEM);
   }
   
@@ -654,7 +593,7 @@
 
   p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
   if (p == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP RECV buffer!\n"));
+	kfree_s((void *)sl->dev->mem_start,l+4);
 	return(-ENOMEM);
   }
   sl->dev->rmem_start	= (unsigned long) p;
@@ -672,25 +611,22 @@
   p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
   if (p == NULL) {
   	kfree((unsigned char *)sl->dev->mem_start);
-	DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP COMPRESS buffer!\n"));
 	return(-ENOMEM);
   }
   sl->cbuff		= p;
-
+#ifdef CONFIG_INET
   sl->slcomp = slhc_init(16, 16);
   if (sl->slcomp == NULL) {
   	kfree((unsigned char *)sl->dev->mem_start);
   	kfree((unsigned char *)sl->dev->rmem_start);
   	kfree(sl->cbuff);
-	DPRINTF((DBG_SLIP, "SLIP: no memory for SLCOMP!\n"));
 	return(-ENOMEM);
   }
-
+#endif
   dev->flags|=IFF_UP;
   /* Needed because address '0' is special */
   if(dev->pa_addr==0)
   	dev->pa_addr=ntohl(0xC0000001);
-  DPRINTF((DBG_SLIP, "SLIP: channel %d opened.\n", sl->line));
   return(0);
 }
 
@@ -703,20 +639,20 @@
 
   sl = &sl_ctrl[dev->base_addr];
   if (sl->tty == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: channel %d not connected!\n", sl->line));
 	return(-EBUSY);
   }
+  sl->tty->disc_data = 0;
   sl_free(sl);
 
   /* Free all SLIP frame buffers. */
   kfree(sl->rbuff);
   kfree(sl->xbuff);
   kfree(sl->cbuff);
+#ifdef CONFIG_INET  
   slhc_free(sl->slcomp);
-
+#endif
   sl_initialize(sl, dev);
 
-  DPRINTF((DBG_SLIP, "SLIP: channel %d closed.\n", sl->line));
   return(0);
 }
 
@@ -727,40 +663,35 @@
  * a block of SLIP data has been received, which can now be decapsulated
  * and sent on to some IP layer for further processing.
  */
-static void
-slip_recv(struct tty_struct *tty)
+static void slip_receive_buf(struct tty_struct *tty, unsigned char *cp,
+			       char *fp, int count)
 {
-  unsigned char buff[128];
-  unsigned char *p;
-  struct slip *sl;
-  int count, error=0;
+	struct slip *sl = (struct slip *) tty->disc_data;
   
-  DPRINTF((DBG_SLIP, "SLIP: slip_recv(%d) called\n", tty->line));
-  if ((sl = sl_find(tty)) == NULL) return;	/* not connected */
-
-  if(sl->mtu!=sl->dev->mtu)	/* Argh! mtu change time! - costs us the packet part received at the change */
-  	sl_changedmtu(sl);
+	if (!sl || sl->magic != SLIP_MAGIC)
+		return;
+  
+	/*
+	 * Argh! mtu change time! - costs us the packet part received
+	 * at the change
+	 */
+	if(sl->mtu!=sl->dev->mtu)
+		sl_changedmtu(sl);
   	
-  /* Suck the bytes out of the TTY queues. */
-  do {
-	count = tty_read_raw_data(tty, buff, 128);
-	if (count <= 0)
-	{
-		count= - count;
-		if(count)
-			error=1;
-		break;
+	/* Read the characters out of the buffer */
+	while (count--) {
+		if (*fp++) {
+			sl->flags |= SLF_ERROR;
+			cp++;
+			continue;
+		}
+		if (sl->mode & SL_MODE_SLIP6)
+			slip_unesc6(sl,*cp++);
+		else
+			slip_unesc(sl,*cp++);
 	}
-	p = buff;
-	if(sl->mode & SL_MODE_SLIP6)
-		slip_unesc6(sl,buff,count,error);
-	else
-		slip_unesc(sl,buff,count,error);
-  } while(1);
-  
 }
 
-
 /*
  * Open the high-level part of the SLIP channel.  
  * This function is called by the TTY module when the
@@ -771,29 +702,26 @@
 static int
 slip_open(struct tty_struct *tty)
 {
-  struct slip *sl;
+  struct slip *sl = (struct slip *) tty->disc_data;
 
   /* First make sure we're not already connected. */
-  if ((sl = sl_find(tty)) != NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: TTY %d already connected to %s !\n",
-					tty->line, sl->dev->name));
+  if (sl && sl->magic == SLIP_MAGIC) {
 	return(-EEXIST);
   }
 
   /* OK.  Find a free SLIP channel to use. */
   if ((sl = sl_alloc()) == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: TTY %d not connected: all channels in use!\n",
-						tty->line));
 	return(-ENFILE);
   }
   sl->tty = tty;
-  tty_read_flush(tty);
-  tty_write_flush(tty);
+  tty->disc_data = sl;
+  if (tty->driver.flush_buffer)
+	  tty->driver.flush_buffer(tty);
+  if (tty->ldisc.flush_buffer)
+	  tty->ldisc.flush_buffer(tty);
 
   /* Perform the low-level SLIP initialization. */
   (void) sl_open(sl->dev);
-  DPRINTF((DBG_SLIP, "SLIP: TTY %d connected to %s.\n",
-				tty->line, sl->dev->name));
 
   /* Done.  We have linked the TTY line to a channel. */
   return(sl->line);
@@ -819,7 +747,7 @@
     stats.tx_packets = sl->spacket;
     stats.tx_dropped = sl->sbusy;
     stats.rx_errors = sl->errors;
-
+#ifdef CONFIG_INET
     comp = sl->slcomp;
     if (comp) {
       stats.rx_fifo_errors = comp->sls_i_compressed;
@@ -827,7 +755,7 @@
       stats.tx_fifo_errors = comp->sls_o_compressed;
       stats.collisions = comp->sls_o_misses;
     }
-
+#endif
     return (&stats);
 }
 
@@ -840,20 +768,17 @@
 static void
 slip_close(struct tty_struct *tty)
 {
-  struct slip *sl;
+  struct slip *sl = (struct slip *) tty->disc_data;
 
   /* First make sure we're connected. */
-  if ((sl = sl_find(tty)) == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: TTY %d not connected !\n", tty->line));
-	return;
+  if (!sl || sl->magic != SLIP_MAGIC) {
+  	return;
   }
 
   (void) dev_close(sl->dev);
-  DPRINTF((DBG_SLIP, "SLIP: TTY %d disconnected from %s.\n",
-					tty->line, sl->dev->name));
 }
 
- 
+
  /************************************************************************
   *			STANDARD SLIP ENCAPSULATION			*
   ************************************************************************
@@ -898,44 +823,39 @@
  }
  
  void
- slip_unesc(struct slip *sl, unsigned char *s, int count, int error)
+ slip_unesc(struct slip *sl, unsigned char s)
  {
-     int i;
- 
-     for (i = 0; i < count; ++i, ++s) {
- 	switch(*s) {
- 	case ESC:
- 	    sl->flags |= SLF_ESCAPE;
- 	    break;
- 	case ESC_ESC:
- 	    if (sl->flags & SLF_ESCAPE)
- 	    	sl_enqueue(sl, ESC);
- 	    else
- 	        sl_enqueue(sl, *s);
-	    sl->flags &= ~SLF_ESCAPE;
- 	    break;
-	case ESC_END:
- 	    if (sl->flags & SLF_ESCAPE)
-	    	sl_enqueue(sl, END);
-	    else
- 	        sl_enqueue(sl, *s);
-	    sl->flags &= ~SLF_ESCAPE;
-	    break;
-	case END:
- 	    if (sl->rcount > 2) 
- 	    	sl_bump(sl);
- 	    sl_dequeue(sl, sl->rcount);
- 	    sl->rcount = 0;
- 	    sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
- 	    break;
- 	default:
- 	    sl_enqueue(sl, *s);
- 	    sl->flags &= ~SLF_ESCAPE;
- 	}
+     switch(s) {
+     case ESC:
+	     sl->flags |= SLF_ESCAPE;
+	     break;
+     case ESC_ESC:
+	     if (sl->flags & SLF_ESCAPE)
+		     sl_enqueue(sl, ESC);
+	     else
+		     sl_enqueue(sl, s);
+	     sl->flags &= ~SLF_ESCAPE;
+	     break;
+     case ESC_END:
+	     if (sl->flags & SLF_ESCAPE)
+		     sl_enqueue(sl, END);
+	     else
+		     sl_enqueue(sl, s);
+	     sl->flags &= ~SLF_ESCAPE;
+	     break;
+     case END:
+	     if (sl->rcount > 2) 
+		     sl_bump(sl);
+	     sl_dequeue(sl, sl->rcount);
+	     sl->rcount = 0;
+	     sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
+	     break;
+     default:
+	     sl_enqueue(sl, s);
+	     sl->flags &= ~SLF_ESCAPE;
      }
-     if (error)
-     	sl->flags |= SLF_ERROR;
  }
+
  
  /************************************************************************
   *			 6 BIT SLIP ENCAPSULATION			*
@@ -985,44 +905,39 @@
  }
  
  void
- slip_unesc6(struct slip *sl, unsigned char *s, int count, int error)
+ slip_unesc6(struct slip *sl, unsigned char s)
  {
-     int i;
      unsigned char c;
  
-     for (i = 0; i < count; ++i, ++s) {
-     	if (*s == 0x70) {
- 	    if (sl->rcount > 8) {	/* XXX must be 2 for compressed slip */
+     if (s == 0x70) {
+	     if (sl->rcount > 8) {	/* XXX must be 2 for compressed slip */
  #ifdef NOTDEF
- 	        printk("rbuff %02x %02x %02x %02x\n",
- 	            sl->rbuff[0],
- 	            sl->rbuff[1],
- 	            sl->rbuff[2],
- 	            sl->rbuff[3]
- 	        );
+		     printk("rbuff %02x %02x %02x %02x\n",
+			    sl->rbuff[0],
+			    sl->rbuff[1],
+			    sl->rbuff[2],
+			    sl->rbuff[3]
+			    );
  #endif
- 	    	sl_bump(sl);
- 	    }
+		     sl_bump(sl);
+	     }
  	    sl_dequeue(sl, sl->rcount);
  	    sl->rcount = 0;
  	    sl->flags &= ~(SLF_ESCAPE | SLF_ERROR); /* SLF_ESCAPE not used */
  	    sl->xbits = 0;
- 	} else if (*s >= 0x30 && *s < 0x70) {
- 	    sl->xdata = (sl->xdata << 6) | ((*s - 0x30) & 0x3F);
- 	    sl->xbits += 6;
- 	    if (sl->xbits >= 8) {
- 	    	sl->xbits -= 8;
- 	    	c = (unsigned char)(sl->xdata >> sl->xbits);
- 		sl_enqueue(sl, c);
- 	    }
- 
+ 	} else if (s >= 0x30 && s < 0x70) {
+		sl->xdata = (sl->xdata << 6) | ((s - 0x30) & 0x3F);
+		sl->xbits += 6;
+		if (sl->xbits >= 8) {
+			sl->xbits -= 8;
+			c = (unsigned char)(sl->xdata >> sl->xbits);
+			sl_enqueue(sl, c);
+		}
  	}
-     }
-     if (error)
-     	sl->flags |= SLF_ERROR;
  }
 
 
+
 #ifdef CONFIG_AX25
 
 int sl_set_mac_address(struct device *dev, void *addr)
@@ -1046,16 +961,14 @@
 static int
 slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
 {
-  struct slip *sl;
+  struct slip *sl = (struct slip *) tty->disc_data;
   int err;
 
   /* First make sure we're connected. */
-  if ((sl = sl_find(tty)) == NULL) {
-	DPRINTF((DBG_SLIP, "SLIP: ioctl: TTY %d not connected !\n", tty->line));
+  if (!sl || sl->magic != SLIP_MAGIC) {
 	return(-EINVAL);
   }
 
-  DPRINTF((DBG_SLIP, "SLIP: ioctl(%d, 0x%X, 0x%X)\n", tty->line, cmd, arg));
   switch(cmd) {
 	case SIOCGIFNAME:
 		err=verify_area(VERIFY_WRITE, arg, 16);
@@ -1083,15 +996,21 @@
 		}
 #endif		
 		sl->dev->type=ARPHRD_SLIP+sl->mode;
+		if(sl->dev->type==260)
+			sl->dev->type=ARPHRD_AX25;
 		return(0);
 	case SIOCSIFHWADDR:
 #ifdef CONFIG_AX25	
 		return sl_set_mac_address(sl->dev,arg);
 #endif
+	/* Allow stty to read, but not set, the serial port */
+	case TCGETS:
+	case TCGETA:
+		return n_tty_ioctl(tty, file, cmd, (unsigned long) arg);
+		
 	default:
-		return(-EINVAL);
+		return -ENOIOCTLCMD;
   }
-  return(-EINVAL);
 }
 
 
@@ -1118,6 +1037,8 @@
 	printk("AX25: KISS encapsulation enabled\n");
 #endif	
 	/* Fill in our LDISC request block. */
+	memset(&sl_ldisc, 0, sizeof(sl_ldisc));
+	sl_ldisc.magic	= TTY_LDISC_MAGIC;
 	sl_ldisc.flags	= 0;
 	sl_ldisc.open	= slip_open;
 	sl_ldisc.close	= slip_close;
@@ -1126,7 +1047,8 @@
 	sl_ldisc.ioctl	= (int (*)(struct tty_struct *, struct file *,
 				   unsigned int, unsigned long)) slip_ioctl;
 	sl_ldisc.select = NULL;
-	sl_ldisc.handler = slip_recv;
+	sl_ldisc.receive_buf = slip_receive_buf;
+	sl_ldisc.write_wakeup = slip_write_wakeup;
 	if ((i = tty_register_ldisc(N_SLIP, &sl_ldisc)) != 0)
 		printk("ERROR: %d\n", i);
   }
diff -u --recursive --new-file v1.1.12/linux/drivers/net/slip.h linux/drivers/net/slip.h
--- v1.1.12/linux/drivers/net/slip.h	Sat Jan  8 14:35:09 1994
+++ linux/drivers/net/slip.h	Mon May 23 07:55:14 1994
@@ -27,6 +27,7 @@
 
 
 struct slip {
+  int			magic;
   /* Bitmapped flag fields. */
   char			inuse;		/* are we allocated?		*/
   char			sending;	/* "channel busy" indicator	*/
@@ -48,6 +49,8 @@
   unsigned char		*rhead;		/* RECV buffer pointer (head)	*/
   unsigned char		*rend;		/* RECV buffer pointer (end)	*/
   int			rcount;		/* SLIP receive counter		*/
+  unsigned char		*xhead;		/* XMIT buffer pointer (head)	*/
+  unsigned char		*xtail;		/* XMIT buffer pointer (tail)	*/
 
   /* SLIP interface statistics. */
   unsigned long		rpacket;	/* inbound frame counter	*/
@@ -62,6 +65,7 @@
 #define SLF_ERROR	4
 #define SLF_COMP	16
 #define SLF_EXPN	32
+#define SLF_XMIT_BUSY	64
   unsigned char		mode;		/* SLIP mode			*/
 #define SL_MODE_SLIP	0
 #define SL_MODE_CSLIP	1
@@ -72,12 +76,12 @@
   int			xdata,xbits;	/* 6 bit slip controls 		*/
 };
 
+#define SLIP_MAGIC 0x5302
 
 extern int	slip_init(struct device *dev);
 extern int	slip_esc(unsigned char *s, unsigned char *d, int len);
 extern int	slip_esc6(unsigned char *s, unsigned char *d, int len);
-extern void	slip_unesc(struct slip *sl, unsigned char *s, int count, int error);
-extern void 	slip_unesc6(struct slip *sl, unsigned char *s, int count, int error);
-
+extern void	slip_unesc(struct slip *sl, unsigned char s);
+extern void 	slip_unesc6(struct slip *sl, unsigned char s);
 
 #endif	/* _LINUX_SLIP.H */
diff -u --recursive --new-file v1.1.12/linux/fs/fcntl.c linux/fs/fcntl.c
--- v1.1.12/linux/fs/fcntl.c	Sat May  7 14:54:06 1994
+++ linux/fs/fcntl.c	Mon May 23 07:55:11 1994
@@ -83,8 +83,15 @@
 		case F_GETFL:
 			return filp->f_flags;
 		case F_SETFL:
-			filp->f_flags &= ~(O_APPEND | O_NONBLOCK);
-			filp->f_flags |= arg & (O_APPEND | O_NONBLOCK);
+			if ((arg & FASYNC) && !(filp->f_flags & FASYNC) &&
+			    filp->f_op->fasync)
+				filp->f_op->fasync(filp->f_inode, filp, 1);
+			if (!(arg & FASYNC) && (filp->f_flags & FASYNC) &&
+			    filp->f_op->fasync)
+				filp->f_op->fasync(filp->f_inode, filp, 0);
+			filp->f_flags &= ~(O_APPEND | O_NONBLOCK | FASYNC);
+			filp->f_flags |= arg & (O_APPEND | O_NONBLOCK |
+						FASYNC);
 			return 0;
 		case F_GETLK:
 			return fcntl_getlk(fd, (struct flock *) arg);
@@ -92,6 +99,20 @@
 			return fcntl_setlk(fd, cmd, (struct flock *) arg);
 		case F_SETLKW:
 			return fcntl_setlk(fd, cmd, (struct flock *) arg);
+		case F_GETOWN:
+			/*
+			 * XXX If f_owner is a process group, the
+			 * negative return value will get converted
+			 * into an error.  Oops.  If we keep the the
+			 * current syscall conventions, the only way
+			 * to fix this will be in libc.
+			 */
+			return filp->f_owner;
+		case F_SETOWN:
+			filp->f_owner = arg; /* XXX security implications? */
+			if (S_ISSOCK (filp->f_inode->i_mode))
+				sock_fcntl (filp, F_SETOWN, arg);
+			return 0;
 		default:
 			/* sockets need a few special fcntls. */
 			if (S_ISSOCK (filp->f_inode->i_mode))
@@ -99,5 +120,21 @@
 			     return (sock_fcntl (filp, cmd, arg));
 			  }
 			return -EINVAL;
+	}
+}
+
+void kill_fasync(struct fasync_struct *fa, int sig)
+{
+	while (fa) {
+		if (fa->magic != FASYNC_MAGIC) {
+			printk("kill_fasync: bad magic number in "
+			       "fasync_struct!\n");
+			return;
+		}
+		if (fa->fa_file->f_owner > 0)
+			kill_proc(fa->fa_file->f_owner, sig, 1);
+		else
+			kill_pg(-fa->fa_file->f_owner, sig, 1);
+		fa = fa->fa_next;
 	}
 }
diff -u --recursive --new-file v1.1.12/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v1.1.12/linux/fs/nfs/proc.c	Mon Feb 14 12:48:36 1994
+++ linux/fs/nfs/proc.c	Mon May 23 08:17:42 1994
@@ -4,6 +4,16 @@
  *  Copyright (C) 1992, 1993, 1994  Rick Sladkey
  *
  *  OS-independent nfs remote procedure call functions
+ *
+ *  Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
+ *  so at last we can have decent(ish) throughput off a 
+ *  Sun server.
+ *
+ *  FixMe: We ought to define a sensible small max size for
+ *  things like getattr that are tiny packets and use the
+ *  old get_free_page stuff with it.
+ *
+ *  Feel free to fix it and mail me the diffs if it worries you.
  */
 
 /*
@@ -20,6 +30,7 @@
 #include <linux/param.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/malloc.h>
 #include <linux/nfs_fs.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
@@ -48,15 +59,18 @@
 /*
  * Our memory allocation and release functions.
  */
+ 
+#define NFS_SLACK_SPACE		1024	/* Total overkill */ 
 
-static inline int *nfs_rpc_alloc(void)
+static inline int *nfs_rpc_alloc(int size)
 {
-	return (int *) __get_free_page(GFP_KERNEL);
+	size+=NFS_SLACK_SPACE;		/* Allow for the NFS crap as well as buffer */
+	return (int *)kmalloc(size,GFP_KERNEL);
 }
 
 static inline void nfs_rpc_free(int *p)
 {
-	free_page((long) p);
+	kfree((void *)p);
 }
 
 /*
@@ -195,7 +209,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  getattr\n");
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->rsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid);
@@ -229,7 +243,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  setattr\n");
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid);
@@ -268,7 +282,7 @@
 	if (!strcmp(name, "xyzzy"))
 		proc_debug = 1 - proc_debug;
 #endif
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->rsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid);
@@ -304,7 +318,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  readlink\n");
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->rsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_READLINK, ruid);
@@ -343,7 +357,7 @@
 	int len = 0; /* = 0 is for gcc */
 
 	PRINTK("NFS call  read %d @ %d\n", count, offset);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->rsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_READ, ruid);
@@ -385,7 +399,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  write %d @ %d\n", count, offset);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid);
@@ -424,7 +438,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  create %s\n", name);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid);
@@ -460,7 +474,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  remove %s\n", name);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid);
@@ -495,7 +509,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  rename %s -> %s\n", old_name, new_name);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid);
@@ -531,7 +545,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  link %s\n", name);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_LINK, ruid);
@@ -566,7 +580,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  symlink %s -> %s\n", name, path);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid);
@@ -603,7 +617,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  mkdir %s\n", name);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid);
@@ -639,7 +653,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  rmdir %s\n", name);
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->wsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid);
@@ -677,7 +691,7 @@
 
 	PRINTK("NFS call  readdir %d @ %d\n", count, cookie);
 	size = server->rsize;
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->rsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid);
@@ -727,7 +741,7 @@
 	int ruid = 0;
 
 	PRINTK("NFS call  statfs\n");
-	if (!(p0 = nfs_rpc_alloc()))
+	if (!(p0 = nfs_rpc_alloc(server->rsize)))
 		return -EIO;
 retry:
 	p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid);
diff -u --recursive --new-file v1.1.12/linux/fs/open.c linux/fs/open.c
--- v1.1.12/linux/fs/open.c	Sat May  7 14:54:09 1994
+++ linux/fs/open.c	Mon May 23 07:55:11 1994
@@ -481,14 +481,10 @@
  */
 asmlinkage int sys_vhangup(void)
 {
-	struct tty_struct *tty;
-
 	if (!suser())
 		return -EPERM;
-	/* See if there is a controlling tty. */
-	if (current->tty < 0)
-		return 0;
-	tty = TTY_TABLE(MINOR(current->tty));
-	tty_vhangup(tty);
+	/* If there is a controlling tty, hang it up */
+	if (current->tty)
+		tty_vhangup(current->tty);
 	return 0;
 }
diff -u --recursive --new-file v1.1.12/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.1.12/linux/fs/proc/array.c	Sat May  7 14:54:10 1994
+++ linux/fs/proc/array.c	Mon May 23 07:55:11 1994
@@ -306,9 +306,8 @@
 		default: sigcatch |= bit;
 		} bit <<= 1;
 	}
-	tty_pgrp = (*p)->tty;
-	if (tty_pgrp > 0 && tty_table[tty_pgrp])
-		tty_pgrp = tty_table[tty_pgrp]->pgrp;
+	if ((*p)->tty)
+		tty_pgrp = (*p)->tty->pgrp;
 	else
 		tty_pgrp = -1;
 	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
@@ -320,7 +319,7 @@
 		(*p)->p_pptr->pid,
 		(*p)->pgrp,
 		(*p)->session,
-		(*p)->tty,
+	        (*p)->tty ? (*p)->tty->device : 0,
 		tty_pgrp,
 		(*p)->flags,
 		(*p)->mm->min_flt,
diff -u --recursive --new-file v1.1.12/linux/fs/proc/net.c linux/fs/proc/net.c
--- v1.1.12/linux/fs/proc/net.c	Mon May 23 12:14:24 1994
+++ linux/fs/proc/net.c	Mon May 23 08:17:42 1994
@@ -52,7 +52,17 @@
 extern int ipx_get_info(char *, char **, off_t, int);
 extern int ipx_rt_get_info(char *, char **, off_t, int);
 #endif /* CONFIG_IPX */
+#ifdef CONFIG_AX25
+extern int ax25_get_info(char *, char **, off_t, int);
+extern int ax25_rt_get_info(char *, char **, off_t, int);
+#ifdef CONFIG_NETROM
+extern int nr_get_info(char *, char **, off_t, int);
+extern int nr_nodes_get_info(char *, char **, off_t, int);
+extern int nr_neigh_get_info(char *, char **, off_t, int);
+#endif /* CONFIG_NETROM */
+#endif /* CONFIG_AX25 */
 
+
 static struct file_operations proc_net_operations = {
 	NULL,			/* lseek - default */
 	proc_readnet,		/* read - bad */
@@ -97,15 +107,24 @@
 	{ 131,3,"dev" },
 	{ 132,3,"raw" },
 	{ 133,3,"tcp" },
-	{ 134,3,"udp" },
+	{ 134,3,"udp" }
 #ifdef CONFIG_INET_RARP
-	{ 135,4,"rarp"}
+	,{ 135,4,"rarp"}
 #endif
 #endif	/* CONFIG_INET */
 #ifdef CONFIG_IPX
 	,{ 136,9,"ipx_route" },
 	{ 137,3,"ipx" }
 #endif /* CONFIG_IPX */
+#ifdef CONFIG_AX25
+	,{ 138,10,"ax25_route" },
+	{ 139,4,"ax25" }
+#ifdef CONFIG_NETROM
+	,{ 140,8,"nr_nodes" },
+	{ 141,8,"nr_neigh" },
+	{ 142,2,"nr" }
+#endif /* CONFIG_NETROM */
+#endif /* CONFIG_AX25 */
 };
 
 #define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])))
@@ -216,9 +235,11 @@
 			case 134:
 				length = udp_get_info(page,&start,file->f_pos,thistime);
 				break;
+#ifdef CONFIG_INET_RARP				
 			case 135:
 				length = rarp_get_info(page,&start,file->f_pos,thistime);
 				break;
+#endif /* CONFIG_INET_RARP */				
 #endif /* CONFIG_INET */
 #ifdef CONFIG_IPX
 			case 136:
@@ -228,6 +249,26 @@
 				length = ipx_get_info(page,&start,file->f_pos,thistime);
 				break;
 #endif /* CONFIG_IPX */
+#ifdef CONFIG_AX25
+			case 138:
+				length = ax25_rt_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 139:
+				length = ax25_get_info(page,&start,file->f_pos,thistime);
+				break;
+#ifdef CONFIG_NETROM
+			case 140:
+				length = nr_nodes_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 141:
+				length = nr_neigh_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 142:
+				length = nr_get_info(page,&start,file->f_pos,thistime);
+				break;
+#endif /* CONFIG_NETROM */
+#endif /* CONFIG_AX25 */
+
 			default:
 				free_page((unsigned long) page);
 				return -EBADF;
diff -u --recursive --new-file v1.1.12/linux/include/linux/ddi.h linux/include/linux/ddi.h
--- v1.1.12/linux/include/linux/ddi.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/ddi.h	Mon May 23 08:17:42 1994
@@ -1,79 +0,0 @@
-/*
- * ddi.h	Define the structure for linking in I/O drivers into the
- *		operating system kernel.  This method is currently only
- *		used by NET layer drivers, but it will be expanded into
- *		a link methos for ALL kernel-resident device drivers.
- *
- * Version:	@(#)ddi.h	1.0.2	04/22/93
- *
- * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- */
-#ifndef _LINUX_DDI_H
-#define _LINUX_DDI_H
-
-
-/* DDI control block flags. */
-#define DDI_FREADY	0x10000000	/* device is initialized	*/
-#define DDI_FPRESENT	0x20000000	/* device hardware is present	*/
-#define DDI_FBLKDEV	0x00000001	/* device has a BLK spec. file	*/
-#define DDI_FCHRDEV	0x00000002	/* device has a CHR spec. file	*/
-
-/* Various constants. */
-#define DDI_MAXNAME	16		/* length of a DDI ID string	*/
-
-
-/* This structure is used to set up a DDI driver. */
-struct ddconf {
-  int		ioaddr;			/* main I/O (port) address	*/
-  int		ioaux;			/* auxiliary I/O (HD, AST)	*/
-  int		irq;			/* IRQ channel			*/
-  int		dma;			/* DMA channel to use		*/
-  unsigned long	memsize;		/* size of onboard memory	*/
-  unsigned long	memaddr;		/* base address of memory	*/
-};
-
-
-/* The DDI device control block. */
-struct ddi_device {
-  char		*title;			/* title of the driver		*/
-  char		name[DDI_MAXNAME];	/* unit name of the I/O driver	*/
-  short int	unit;			/* unit number of this driver	*/
-  short int	nunits;			/* number of units in driver	*/
-  int		(*init)(struct ddi_device *);	/* initialization func		*/
-  int		(*handler)(int, ...);	/* command handler		*/
-  short	int	major;			/* driver major dev number	*/
-  short	int	minor;			/* driver minor dev number	*/
-  unsigned long	flags;			/* various flags		*/
-  struct ddconf config;			/* driver HW setup		*/
-};
-
-
-/* This structure is used to set up networking protocols. */
-struct ddi_proto {
-  char		*name;			/* protocol name		*/
-  void		(*init)(struct ddi_proto *);	/* initialization func	*/
-};
-
-
-/* This structure is used to link a STREAMS interface. */
-struct iflink {
-  char		id[DDI_MAXNAME];	/* DDI ID string		*/
-  char		stream[DDI_MAXNAME];	/* STREAMS interface name	*/
-  int		family;			/* address (protocol) family	*/
-  unsigned int	flags;			/* any flags needed (unused)	*/
-};
-
-
-/* DDI control requests. */
-#define DDIOCSDBG	0x9000		/* set DDI debug level		*/
-#define DDIOCGNAME	0x9001		/* get DDI ID name		*/
-#define DDIOCGCONF	0x9002		/* get DDI HW config		*/
-#define DDIOCSCONF	0x9003		/* set DDI HW config		*/
-
-
-/* DDI global functions. */
-extern void			ddi_init(void);
-extern struct ddi_device	*ddi_map(const char *id);
-
-
-#endif	/* _LINUX_DDI_H */
diff -u --recursive --new-file v1.1.12/linux/include/linux/errno.h linux/include/linux/errno.h
--- v1.1.12/linux/include/linux/errno.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/errno.h	Mon May 23 07:55:10 1994
@@ -128,5 +128,6 @@
 #define ERESTARTSYS	512
 #define ERESTARTNOINTR	513
 #define ERESTARTNOHAND	514	/* restart if no handler.. */
+#define ENOIOCTLCMD	515	/* No ioctl command */
 
 #endif
diff -u --recursive --new-file v1.1.12/linux/include/linux/fcntl.h linux/include/linux/fcntl.h
--- v1.1.12/linux/include/linux/fcntl.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/fcntl.h	Mon May 23 07:55:11 1994
@@ -14,6 +14,7 @@
 #define O_NONBLOCK	 04000
 #define O_NDELAY	O_NONBLOCK
 #define O_SYNC		010000
+#define FASYNC		020000	/* fcntl, for BSD compatibility */
 
 #define F_DUPFD		0	/* dup */
 #define F_GETFD		1	/* get f_flags */
diff -u --recursive --new-file v1.1.12/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.1.12/linux/include/linux/fs.h	Mon Apr 25 10:04:34 1994
+++ linux/include/linux/fs.h	Mon May 23 07:55:11 1994
@@ -49,6 +49,7 @@
 #define MAJOR(a) (int)((unsigned short)(a) >> 8)
 #define MINOR(a) (int)((unsigned short)(a) & 0xFF)
 #define MKDEV(a,b) ((int)((((a) & 0xff) << 8) | ((b) & 0xff)))
+#define NODEV MKDEV(0,0)
 
 #ifndef NULL
 #define NULL ((void *) 0)
@@ -211,14 +212,15 @@
 
 struct file {
 	mode_t f_mode;
-	dev_t f_rdev;			/* needed for /dev/tty */
 	off_t f_pos;
 	unsigned short f_flags;
 	unsigned short f_count;
 	off_t f_reada;
 	struct file *f_next, *f_prev;
+	int f_owner;		/* pid or -pgrp where SIGIO should be sent */
 	struct inode * f_inode;
 	struct file_operations * f_op;
+	void *private_data;	/* needed for tty driver, and maybe others */
 };
 
 struct file_lock {
@@ -232,6 +234,14 @@
 	off_t fl_end;
 };
 
+struct fasync_struct {
+	int    magic;
+	struct fasync_struct	*fa_next; /* singly linked list */
+	struct file 		*fa_file;
+};
+
+#define FASYNC_MAGIC 0x4601
+
 #include <linux/minix_fs_sb.h>
 #include <linux/ext_fs_sb.h>
 #include <linux/ext2_fs_sb.h>
@@ -281,6 +291,7 @@
 	int (*open) (struct inode *, struct file *);
 	void (*release) (struct inode *, struct file *);
 	int (*fsync) (struct inode *, struct file *);
+	int (*fasync) (struct inode *, struct file *, int);
 };
 
 struct inode_operations {
@@ -326,6 +337,8 @@
 
 asmlinkage int sys_open(const char *, int, int);
 asmlinkage int sys_close(unsigned int);		/* yes, it's really unsigned */
+
+extern void kill_fasync(struct fasync_struct *fa, int sig);
 
 extern int getname(const char * filename, char **result);
 extern void putname(char * name);
diff -u --recursive --new-file v1.1.12/linux/include/linux/if.h linux/include/linux/if.h
--- v1.1.12/linux/include/linux/if.h	Tue Apr 19 10:53:48 1994
+++ linux/include/linux/if.h	Mon May 23 08:17:43 1994
@@ -31,11 +31,12 @@
 #define	IFF_NOTRAILERS	0x20		/* avoid use of trailers	*/
 #define	IFF_RUNNING	0x40		/* resources allocated		*/
 #define	IFF_NOARP	0x80		/* no ARP protocol		*/
-
-/* These are not yet used: */
 #define	IFF_PROMISC	0x100		/* recve all packets		*/
+/* These are not yet used: */
 #define	IFF_ALLMULTI	0x200		/* recve all multicast packets	*/
 
+#define IFF_MASTER	0x400		/* master of a load balancer 	*/
+#define IFF_SLAVE	0x800		/* slave of a load balancer	*/
 
 /*
  * The ifaddr structure contains information about one address
@@ -57,6 +58,26 @@
 #define	ifa_dstaddr	ifa_ifu.ifu_dstaddr	/* other end of link	*/
 
 /*
+ *	Device mapping structure. I'd just gone off and designed a 
+ *	beautiful scheme using only loadable modules with arguments
+ *	for driver options and along come the PCMICA people 8)
+ *
+ *	Ah well. The get() side of this is good for WDSETUP, and it'll
+ *	be handy for debugging things. The set side is fine for now and
+ *	being very small might be worth keeping for clean configuration.
+ */
+
+struct ifmap {
+	unsigned long mem_start;
+	unsigned long mem_end;
+	unsigned short base_addr; 
+	unsigned char irq;
+	unsigned char dma;
+	unsigned char port;
+	/* 3 bytes spare */
+};
+
+/*
  * Interface request structure used for socket
  * ioctl's.  All interface ioctl's must have parameter
  * definitions which begin with ifr_name.  The
@@ -80,6 +101,8 @@
 		short	ifru_flags;
 		int	ifru_metric;
 		int	ifru_mtu;
+		struct  ifmap ifru_map;
+		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
 		caddr_t	ifru_data;
 	} ifr_ifru;
 };
@@ -94,6 +117,8 @@
 #define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
 #define	ifr_metric	ifr_ifru.ifru_metric	/* metric		*/
 #define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
+#define ifr_map		ifr_ifru.ifru_map	/* device map		*/
+#define ifr_slave	ifr_ifru.ifru_slave	/* slave device		*/
 #define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
 
 /*
diff -u --recursive --new-file v1.1.12/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.1.12/linux/include/linux/if_arp.h	Mon May 23 12:14:25 1994
+++ linux/include/linux/if_arp.h	Mon May 23 08:17:43 1994
@@ -35,7 +35,7 @@
 #define ARPHRD_CSLIP	257
 #define ARPHRD_SLIP6	258
 #define ARPHRD_CSLIP6	259
-#define ARPHRD_KISS	260
+#define ARPHRD_RSRVD	260		/* Notional KISS type 		*/
 #define ARPHRD_ADAPT	264
 
 /* ARP protocol opcodes. */
diff -u --recursive --new-file v1.1.12/linux/include/linux/if_ether.h linux/include/linux/if_ether.h
--- v1.1.12/linux/include/linux/if_ether.h	Fri Feb 18 11:20:46 1994
+++ linux/include/linux/if_ether.h	Mon May 23 08:17:43 1994
@@ -40,6 +40,8 @@
 #define ETH_P_802_3	0x0001		/* Dummy type for 802.3 frames  */
 #define ETH_P_AX25	0x0002		/* Dummy protocol id for AX.25  */
 #define ETH_P_ALL	0x0003		/* Every packet (be careful!!!) */
+#define ETH_P_802_2	0x0004		/* 802.2 frames 		*/
+#define ETH_P_SNAP	0x0005		/* 802.2 SNAP frames  		*/
 
 /* This is an Ethernet frame header. */
 struct ethhdr {
diff -u --recursive --new-file v1.1.12/linux/include/linux/inet.h linux/include/linux/inet.h
--- v1.1.12/linux/include/linux/inet.h	Tue Apr 19 10:53:49 1994
+++ linux/include/linux/inet.h	Mon May 23 08:17:43 1994
@@ -42,49 +42,19 @@
 #ifndef _LINUX_INET_H
 #define _LINUX_INET_H
 
-
-#include <linux/ddi.h>
-
-
-#undef	INET_DEBUG
-#ifdef	INET_DEBUG
-#  define	DPRINTF(x)	dprintf x 
-#else
-#   define	DPRINTF(x)	do ; while (0)
+#ifdef __i386__
+#define NET16(x)	((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00))
 #endif
-
-/* Debug levels. One per module. */
-#define DBG_OFF		0			/* no debugging		*/
-#define DBG_INET	1			/* sock.c		*/
-#define DBG_RT		2			/* route.c		*/
-#define DBG_DEV		3			/* dev.c		*/
-#define DBG_ETH		4			/* eth.c		*/
-#define DBG_PROTO	5			/* protocol.c		*/
-#define DBG_TMR		6			/* timer.c		*/
-#define DBG_PKT		7			/* packet.c		*/
-#define DBG_RAW		8			/* raw.c		*/
-
-#define DBG_LOOPB	10			/* loopback.c		*/
-#define DBG_SLIP	11			/* slip.c		*/
-
-#define DBG_ARP		20			/* arp.c		*/
-#define DBG_IP		21			/* ip.c			*/
-#define DBG_ICMP	22			/* icmp.c		*/
-#define DBG_TCP		23			/* tcp.c		*/
-#define DBG_UDP		24			/* udp.c		*/
+#ifdef __mc680x0__
+#define NET16(x)	(x)
+#endif
 
 #ifdef __KERNEL__
 
-extern int		inet_debug;
-
 
-extern void		inet_proto_init(struct ddi_proto *pro);
+extern void		inet_proto_init(struct net_proto *pro);
 extern char		*in_ntoa(unsigned long in);
 extern unsigned long	in_aton(char *str);
-
-extern void		dprintf(int level, char *fmt, ...);
-
-extern int		dbg_ioctl(void *arg, int level);
 
 #endif
 #endif	/* _LINUX_INET_H */
diff -u --recursive --new-file v1.1.12/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v1.1.12/linux/include/linux/interrupt.h	Mon Mar  7 14:34:14 1994
+++ linux/include/linux/interrupt.h	Mon May 23 08:17:43 1994
@@ -12,13 +12,14 @@
 extern struct bh_struct bh_base[32];
 
 /* Who gets which entry in bh_base.  Things which will occur most often
-   should come first. */
+   should come first - in which case NET should be up the top with SERIAL/TQUEUE! */
+   
 enum {
 	TIMER_BH = 0,
 	CONSOLE_BH,
+	TQUEUE_BH,
 	SERIAL_BH,
-	TTY_BH,
-	INET_BH,
+	NET_BH,
 	KEYBOARD_BH
 };
 
diff -u --recursive --new-file v1.1.12/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v1.1.12/linux/include/linux/ipx.h	Tue Apr 19 10:53:49 1994
+++ linux/include/linux/ipx.h	Mon May 23 08:17:43 1994
@@ -1,9 +1,13 @@
+#ifndef _IPX_H_
+#define _IPX_H_
+
 struct sockaddr_ipx
 {
 	short sipx_family;
 	unsigned long  sipx_network;
 	unsigned char sipx_node[6];
 	short sipx_port;
+	unsigned char	sipx_type;
 };
 
 struct ipx_route_def
@@ -14,10 +18,13 @@
 	unsigned char ipx_router_node[6];
 	unsigned char ipx_device[16];
 	unsigned short ipx_flags;
+#define IPX_RT_SNAP		8
+#define IPX_RT_8022		4
 #define IPX_RT_BLUEBOOK		2
 #define IPX_RT_ROUTED		1
 };
 
 #define IPX_MTU		576
 
+#endif
 
diff -u --recursive --new-file v1.1.12/linux/include/linux/keyboard.h linux/include/linux/keyboard.h
--- v1.1.12/linux/include/linux/keyboard.h	Thu Mar  3 08:05:22 1994
+++ linux/include/linux/keyboard.h	Mon May 23 07:55:11 1994
@@ -84,6 +84,7 @@
 #define K_BOOT		K(KT_SPEC,12)
 #define K_CAPSON	K(KT_SPEC,13)
 #define K_COMPOSE       K(KT_SPEC,14)
+#define K_SAK		K(KT_SPEC,15)
 
 #define K_P0		K(KT_PAD,0)
 #define K_P1		K(KT_PAD,1)
diff -u --recursive --new-file v1.1.12/linux/include/linux/net.h linux/include/linux/net.h
--- v1.1.12/linux/include/linux/net.h	Fri Mar 18 10:46:45 1994
+++ linux/include/linux/net.h	Mon May 23 08:17:43 1994
@@ -56,7 +56,7 @@
 
 
 /*
- * Internel representation of a socket. not all the fields are used by
+ * Internal representation of a socket. not all the fields are used by
  * all configurations:
  *
  *		server			client
@@ -122,6 +122,10 @@
 			 unsigned long arg);	
 };
 
+struct net_proto {
+	char *name;		/* Protocol name */
+	void (*init_func)(struct net_proto *);	/* Bootstrap */
+};
 
 extern int	sock_awaitconn(struct socket *mysock, struct socket *servsock);
 extern int	sock_register(int family, struct proto_ops *ops);
diff -u --recursive --new-file v1.1.12/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.1.12/linux/include/linux/netdevice.h	Mon May 23 12:14:25 1994
+++ linux/include/linux/netdevice.h	Mon May 23 08:17:43 1994
@@ -12,6 +12,7 @@
  *		Corey Minyard <wf-rch!minyard@relay.EU.net>
  *		Donald J. Becker, <becker@super.org>
  *		Alan Cox, <A.Cox@swansea.ac.uk>
+ *		Bjorn Ekwall. <bj0rn@blox.se>
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -53,7 +54,7 @@
    */
   char			  *name;
 
-  /* I/O specific fields.  */
+  /* I/O specific fields - FIXME: Merge these and struct ifmap into one */
   unsigned long		  rmem_end;		/* shmem "recv" end	*/
   unsigned long		  rmem_start;		/* shmem "recv" start	*/
   unsigned long		  mem_end;		/* sahared mem end	*/
@@ -105,6 +106,12 @@
   unsigned long		  pa_dstaddr;	/* protocol P-P other side addr	*/
   unsigned long		  pa_mask;	/* protocol netmask		*/
   unsigned short	  pa_alen;	/* protocol address length	*/
+  
+  /* For load balancing driver pair support */
+  
+  unsigned long		   pkt_queue;	/* Packets queued */
+  struct device		  *slave;	/* Slave device */
+  
 
   /* Pointer to the interface buffers. */
   struct sk_buff_head	  buffs[DEV_NUMBUFFS];
@@ -132,6 +139,9 @@
   int			  (*set_mac_address)(struct device *dev, void *addr);
 #define HAVE_PRIVATE_IOCTL
   int			  (*do_ioctl)(struct device *dev, struct ifreq *ifr);
+#define HAVE_SET_CONFIG
+  int			  (*set_config)(struct device *dev, struct ifmap *map);
+  
 };
 
 
@@ -175,8 +185,8 @@
 extern int		dev_rint(unsigned char *buff, long len, int flags,
 				 struct device * dev);
 extern void		dev_transmit(void);
-extern int		in_inet_bh(void);
-extern void		inet_bh(void *tmp);
+extern int		in_net_bh(void);
+extern void		net_bh(void *tmp);
 extern void		dev_tint(struct device *dev);
 extern int		dev_get_info(char *buffer, char **start, off_t offset, int length);
 extern int		dev_ioctl(unsigned int cmd, void *);
@@ -186,6 +196,9 @@
 /* This function lives elsewhere (drivers/net/net_init.c but is related) */
 
 extern void		ether_setup(struct device *dev);
+/* Support for loadable net-drivers */
+extern int		register_netdev(struct device *dev);
+extern void		unregister_netdev(struct device *dev);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.1.12/linux/include/linux/ppp.h linux/include/linux/ppp.h
--- v1.1.12/linux/include/linux/ppp.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/ppp.h	Mon May 23 07:55:15 1994
@@ -0,0 +1,233 @@
+#ifndef _LINUX_PPP_H
+#define _LINUX_PPP_H
+
+/* definitions for kernel PPP module
+   Michael Callahan <callahan@maths.ox.ac.uk>
+   Nov. 4 1993 */
+
+/* how many PPP units? */
+#define PPP_NRUNIT     4
+
+#define PPP_VERSION  "0.2.7"
+
+/* line discipline number */
+#define N_PPP	       3
+
+/* Magic value for the ppp structure */
+#define PPP_MAGIC 0x5002
+
+#define	PPPIOCGFLAGS	 0x5490	/* get configuration flags */
+#define	PPPIOCSFLAGS	 0x5491	/* set configuration flags */
+#define	PPPIOCGASYNCMAP	 0x5492	/* get async map */
+#define	PPPIOCSASYNCMAP	 0x5493	/* set async map */
+#define	PPPIOCGUNIT	 0x5494	/* get ppp unit number */
+#define PPPIOCSINPSIG	 0x5495	/* set input ready signal */
+#define PPPIOCSDEBUG	 0x5497	/* set debug level */
+#define PPPIOCGDEBUG	 0x5498	/* get debug level */
+#define PPPIOCGSTAT	 0x5499	/* read PPP statistic information */
+#define PPPIOCGTIME	 0x549A	/* read time delta information */
+#define	PPPIOCGXASYNCMAP 0x549B	/* get async table */
+#define	PPPIOCSXASYNCMAP 0x549C	/* set async table */
+#define PPPIOCSMRU	 0x549D	/* set receive unit size for PPP */
+#define PPPIOCRASYNCMAP	 0x549E	/* set receive async map */
+#define PPPIOCSMAXCID    0x549F /* set the maximum compression slot id */
+
+/* special characters in the framing protocol */
+#define	PPP_ALLSTATIONS	0xff	/* All-Stations broadcast address */
+#define	PPP_UI		0x03	/* Unnumbered Information */
+#define PPP_FLAG	0x7E	/* frame delimiter -- marks frame boundaries */
+#define PPP_ADDRESS	0xFF	/* first character of frame   <--  (may be   */
+#define PPP_CONTROL	0x03	/* second character of frame  <-- compressed)*/
+#define	PPP_TRANS	0x20	/* Asynchronous transparency modifier */
+#define PPP_ESC		0x7d	/* escape charecter -- next character is
+				   data, and the PPP_TRANS bit should be
+				   toggled. PPP_ESC PPP_FLAG is illegal */
+
+/* protocol numbers */
+#define PROTO_IP       0x0021
+#define PROTO_VJCOMP   0x002d
+#define PROTO_VJUNCOMP 0x002f
+
+/* FCS support */
+#define PPP_FCS_INIT   0xffff
+#define PPP_FCS_GOOD   0xf0b8
+
+/* initial MTU */
+#define PPP_MTU	       1500
+
+/* initial MRU */
+#define PPP_MRU	       PPP_MTU
+
+/* flags */
+#define SC_COMP_PROT	0x00000001	/* protocol compression (output) */
+#define SC_COMP_AC	0x00000002	/* header compression (output) */
+#define	SC_COMP_TCP	0x00000004	/* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID	0x00000008	/* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC	0x00000010	/* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP	0x00000020	/* reject TCP (VJ) comp. on input */
+#define SC_ENABLE_IP	0x00000100	/* IP packets may be exchanged */
+#define SC_IP_DOWN	0x00000200	/* give ip frames to pppd */
+#define SC_IP_FLUSH	0x00000400	/* "next time" flag for IP_DOWN */
+#define SC_DEBUG	0x00010000	/* enable debug messages */
+#define SC_LOG_INPKT	0x00020000	/* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent */
+#define SC_LOG_RAWIN	0x00080000	/* log all chars received */
+#define SC_LOG_FLUSH	0x00100000	/* log all chars flushed */
+
+/* Flag bits to determine state of input characters */
+#define SC_RCV_B7_0	0x01000000	/* have rcvd char with bit 7 = 0 */
+#define SC_RCV_B7_1	0x02000000	/* have rcvd char with bit 7 = 0 */
+#define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
+#define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
+
+#define	SC_MASK		0x0fffffff	/* bits that user can change */
+
+/* flag for doing transmitter lockout */
+#define SC_XMIT_BUSY	0x10000000	/* ppp_write_wakeup is active */
+
+/*
+ * This is the format of the data buffer of a LQP packet. The packet data
+ * is sent/received to the peer.
+ */
+
+struct ppp_lqp_packet_hdr {
+  unsigned long		LastOutLQRs;	/* Copied from PeerOutLQRs	 */
+  unsigned long		LastOutPackets; /* Copied from PeerOutPackets	 */
+  unsigned long		LastOutOctets;	/* Copied from PeerOutOctets	 */
+  unsigned long		PeerInLQRs;	/* Copied from SavedInLQRs	 */
+  unsigned long		PeerInPackets;	/* Copied from SavedInPackets	 */
+  unsigned long		PeerInDiscards; /* Copied from SavedInDiscards	 */
+  unsigned long		PeerInErrors;	/* Copied from SavedInErrors	 */
+  unsigned long		PeerInOctets;	/* Copeid from SavedInOctets	 */
+  unsigned long		PeerOutLQRs;	/* Copied from OutLQRs, plus 1	 */
+  unsigned long		PeerOutPackets; /* Current ifOutUniPackets, + 1	 */
+  unsigned long		PeerOutOctets;	/* Current ifOutOctets + LQR	 */
+  };
+
+/*
+ * This data is not sent to the remote. It is updated by the driver when
+ * a packet is received.
+ */
+
+struct ppp_lqp_packet_trailer {
+  unsigned long		SaveInLQRs;	/* Current InLQRs on receiption	 */
+  unsigned long		SaveInPackets;	/* Current ifInUniPackets	 */
+  unsigned long		SaveInDiscards; /* Current ifInDiscards		 */
+  unsigned long		SaveInErrors;	/* Current ifInErrors		 */
+  unsigned long		SaveInOctets;	/* Current ifInOctects		 */
+};
+
+/*
+ * PPP LQP packet. The packet is changed by the driver immediately prior
+ * to transmission and updated upon receiption with the current values.
+ * So, it must be known to the driver as well as the pppd software.
+ */
+
+struct ppp_lpq_packet {
+  unsigned long			magic;	/* current magic value		 */
+  struct ppp_lqp_packet_hdr	hdr;	/* Header fields for structure	 */
+  struct ppp_lqp_packet_trailer tail;	/* Trailer fields (not sent)	 */
+};
+
+/*
+ * PPP interface statistics. (used by LQP / pppstats)
+ */
+
+struct ppp_stats {
+  unsigned long		rbytes;		/* bytes received		 */
+  unsigned long		rcomp;		/* compressed packets received	 */
+  unsigned long		runcomp;	/* uncompressed packets received */
+  unsigned long		rothers;	/* non-ip frames received	 */
+  unsigned long		rerrors;	/* received errors		 */
+  unsigned long		roverrun;	/* "buffer overrun" counter	 */
+  unsigned long		tossed;		/* packets discarded		 */
+  unsigned long		runts;		/* frames too short to process	 */
+  unsigned long		rgiants;	/* frames too large to process	 */
+  unsigned long		sbytes;		/* bytes sent			 */
+  unsigned long		scomp;		/* compressed packets sent	 */
+  unsigned long		suncomp;	/* uncompressed packets sent	 */
+  unsigned long		sothers;	/* non-ip frames sent		 */
+  unsigned long		serrors;	/* transmitter errors		 */
+  unsigned long		sbusy;		/* "transmitter busy" counter	 */
+};
+
+/*
+ * Demand dial fields
+ */
+
+struct ppp_ddinfo {
+  unsigned long		ip_sjiffies;	/* time when last IP frame sent */
+  unsigned long		ip_rjiffies;	/* time when last IP frame recvd*/
+  unsigned long		nip_sjiffies;	/* time when last NON-IP sent	*/
+  unsigned long		nip_rjiffies;	/* time when last NON-IP recvd	*/
+};
+
+#ifdef __KERNEL__
+
+struct ppp {
+  int			magic;		/* magic value for structure	*/
+
+  /* Bitmapped flag fields. */
+  char			inuse;		/* are we allocated?		*/
+  char			sending;	/* "channel busy" indicator	*/
+  char			escape;		/* 0x20 if prev char was PPP_ESC*/
+  char			toss;		/* toss this frame		*/
+
+  unsigned int		flags;		/* miscellany			*/
+
+  unsigned long		xmit_async_map[8]; /* 1 bit means that given control 
+					   character is quoted on output*/
+
+  unsigned long		recv_async_map; /* 1 bit means that given control 
+					   character is ignored on input*/
+  int			mtu;		/* maximum xmit frame size	*/
+  int			mru;		/* maximum receive frame size	*/
+  unsigned short	fcs;		/* FCS field of current frame	*/
+
+  /* Various fields. */
+  int			line;		/* PPP channel number		*/
+  struct tty_struct	*tty;		/* ptr to TTY structure		*/
+  struct device		*dev;		/* easy for intr handling	*/
+  struct slcompress	*slcomp;	/* for header compression	*/
+  unsigned long		last_xmit;	/* time of last transmission	*/
+
+  /* These are pointers to the malloc()ed frame buffers.
+     These buffers are used while processing a packet.	If a packet
+     has to hang around for the user process to read it, it lingers in
+     the user buffers below. */
+  unsigned char		*rbuff;		/* receiver buffer		*/
+  unsigned char		*xbuff;		/* transmitter buffer		*/
+  unsigned char		*cbuff;		/* compression buffer		*/
+
+  /* These are the various pointers into the buffers. */
+  unsigned char		*rhead;		/* RECV buffer pointer (head)	*/
+  unsigned char		*rend;		/* RECV buffer pointer (end)	*/
+  int			rcount;		/* PPP receive counter		*/
+  unsigned char		*xhead;		/* XMIT buffer pointer (head)	*/
+  unsigned char 	*xtail;		/* XMIT buffer pointer (end) 	*/
+
+  /* Structures for interfacing with the user process. */
+#define RBUFSIZE 4000
+  unsigned char		*us_rbuff;	/* circular incoming packet buf.*/
+  unsigned char		*us_rbuff_end;	/* end of allocated space	*/
+  unsigned char		*us_rbuff_head; /* head of waiting packets	*/
+  unsigned char		*us_rbuff_tail; /* tail of waiting packets	*/
+  unsigned char		us_rbuff_lock;	/* lock: bit 0 head bit 1 tail	*/
+  int			inp_sig;	/* input ready signal for pgrp	*/
+  int			inp_sig_pid;	/* process to get notified	*/
+
+  /* items to support the select() function */
+  struct wait_queue	*write_wait;	/* queue for reading processes	*/
+  struct wait_queue	*read_wait;	/* queue for writing processes	*/
+
+  /* PPP interface statistics. */
+  struct ppp_stats	stats;		/* statistic information	*/
+
+  /* PPP demand dial information. */
+  struct ppp_ddinfo	ddinfo;		/* demand dial information	*/
+};
+
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_PPP_H */
+
+
diff -u --recursive --new-file v1.1.12/linux/include/linux/route.h linux/include/linux/route.h
--- v1.1.12/linux/include/linux/route.h	Sun Feb 13 17:16:51 1994
+++ linux/include/linux/route.h	Mon May 23 08:17:44 1994
@@ -44,14 +44,15 @@
 	struct ifnet	*rt_ifp;
 	short		rt_metric;	/* +1 for binary compatibility!	*/
 	char		*rt_dev;	/* forcing the device at add	*/
+	unsigned long	rt_mtu;		/* per route MTU/Window */
 };
 
 
-#define	RTF_UP		0x0001		/* route useable		*/
-#define	RTF_GATEWAY	0x0002		/* destination is a gateway	*/
-#define	RTF_HOST	0x0004		/* host entry (net otherwise)	*/
-#define RTF_REINSTATE	0x0008		/* re-instate route after tmout	*/
-#define	RTF_DYNAMIC	0x0010		/* created dyn. (by redirect)	*/
-#define	RTF_MODIFIED	0x0020		/* modified dyn. (by redirect)	*/
-
+#define	RTF_UP		0x0001		/* route useable		  */
+#define	RTF_GATEWAY	0x0002		/* destination is a gateway	  */
+#define	RTF_HOST	0x0004		/* host entry (net otherwise)	  */
+#define RTF_REINSTATE	0x0008		/* re-instate route after tmout	  */
+#define	RTF_DYNAMIC	0x0010		/* created dyn. (by redirect)	  */
+#define	RTF_MODIFIED	0x0020		/* modified dyn. (by redirect)	  */
+#define RTF_MTU		0x0040		/* specific MSS for this route	  */
 #endif	/* _LINUX_ROUTE_H */
diff -u --recursive --new-file v1.1.12/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.1.12/linux/include/linux/sched.h	Sat May  7 14:54:12 1994
+++ linux/include/linux/sched.h	Mon May 23 07:55:11 1994
@@ -284,7 +284,7 @@
 	unsigned long v86flags, v86mask, v86mode;
 /* file system info */
 	int link_count;
-	int tty;		/* -1 if no tty, so it must be signed */
+	struct tty_struct *tty; /* NULL if no tty */
 	struct inode * executable;
 	struct shm_desc *shm;
 	struct sem_undo *semun;
@@ -337,7 +337,7 @@
 /* math */	0, \
 /* comm */	"swapper", \
 /* vm86_info */	NULL, 0, 0, 0, 0, \
-/* fs info */	0,-1,NULL, \
+/* fs info */	0,NULL,NULL, \
 /* ipc */	NULL, NULL, \
 /* ldt */	NULL, \
 /* tss */	INIT_TSS, \
diff -u --recursive --new-file v1.1.12/linux/include/linux/serial.h linux/include/linux/serial.h
--- v1.1.12/linux/include/linux/serial.h	Thu Dec 23 11:17:30 1993
+++ linux/include/linux/serial.h	Mon May 23 07:55:11 1994
@@ -7,7 +7,70 @@
  * Public License (GPL)
  */
 
+#ifndef _LINUX_SERIAL_H
+#define _LINUX_SERIAL_H
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;
+	int	reserved[5];
+};
+
 /*
+ * These are the supported serial types.
+ */
+#define PORT_UNKNOWN	0
+#define PORT_8250	1
+#define PORT_16450	2
+#define PORT_16550	3
+#define PORT_16550A	4
+#define PORT_MAX	4
+
+/*
+ * Definitions for async_struct (and serial_struct) flags field
+ */
+#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
+				   on the callout port */
+#define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ASYNC_SPD_MASK	0x0030
+#define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
+
+#define ASYNC_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
+#define ASYNC_SPD_CUST	0x0030  /* Use user-specified divisor */
+
+#define ASYNC_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define ASYNC_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ASYNC_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ASYNC_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define ASYNC_FLAGS	0x0FFF	/* Possible legal async flags */
+#define ASYNC_USR_MASK 0x0430	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ASYNC_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define ASYNC_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define ASYNC_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define ASYNC_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define ASYNC_CLOSING		0x08000000 /* Serial port is closing */
+#define ASYNC_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define ASYNC_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+
+#ifdef __KERNEL__
+/*
  * This is our internal structure for each serial port's state.
  * 
  * Many fields are paralleled by the structure used by the serial_struct
@@ -15,10 +78,9 @@
  *
  * For definitions of the flags field, see tty.h
  */
-#ifndef _LINUX_SERIAL_H
-#define _LINUX_SERIAL_H
 
 struct async_struct {
+	int			magic;
 	int			baud_base;
 	int			port;
 	int			irq;
@@ -27,135 +89,47 @@
 	int			type; 		/* UART type */
 	struct tty_struct 	*tty;
 	int			read_status_mask;
+	int			ignore_status_mask;
 	int			timeout;
 	int			xmit_fifo_size;
 	int			custom_divisor;
 	int			x_char;	/* xon/xoff characater */
 	int			close_delay;
 	int			IER; 	/* Interrupt Enable Register */
+	int			MCR; 	/* Modem control register */
+	int			MCR_noint; /* MCR with interrupts off */
 	int			event;
 	int			line;
 	int			count;	    /* # of fd on device */
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
 	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
 	struct termios		normal_termios;
 	struct termios		callout_termios;
 	struct wait_queue	*open_wait;
 	struct wait_queue	*close_wait;
-	struct wait_queue	*xmit_wait;
 	struct async_struct	*next_port; /* For the linked list */
 	struct async_struct	*prev_port;
 };
 
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_READ_PROCESS	0
-#define RS_EVENT_WRITE_WAKEUP	1
-#define RS_EVENT_HANGUP		2
-#define RS_EVENT_BREAK		3
-#define RS_EVENT_OPEN_WAKEUP	4
-
-/*
- * These are the UART port assignments, expressed as offsets from the base
- * register.  These assignments should hold for any serial port based on
- * a 8250, 16450, or 16550(A).
- */
-#define UART_RX		0	/* In:  Receive buffer (DLAB=0) */
-#define UART_TX		0	/* Out: Transmit buffer (DLAB=0) */
-#define UART_DLL	0	/* Out: Devisor Latch Low (DLAB=1) */
-#define UART_DLM	1	/* Out: Devisor Latch High (DLAB=1) */
-#define UART_IER	1	/* Out: Interrupt Enable Register */
-#define UART_IIR	2	/* In:  Interrupt ID Register */
-#define UART_FCR	2	/* Out: FIFO Control Register */
-#define UART_LCR	3	/* Out: Line Control Register */
-#define UART_MCR	4	/* Out: Modem Control Register */
-#define UART_LSR	5	/* In:  Line Status Register */
-#define UART_MSR	6	/* In:  Modem Status Register */
-#define UART_SCR	7	/* I/O: Scratch Register */
-
-/*
- * These are the definitions for the FIFO Control Register
- */
-#define UART_FCR_ENABLE_FIFO	0x01 /* Enable the FIFO */
-#define UART_FCR_CLEAR_RCVR	0x02 /* Clear the RCVR FIFO */
-#define UART_FCR_CLEAR_XMIT	0x04 /* Clear the XMIT FIFO */
-#define UART_FCR_DMA_SELECT	0x08 /* For DMA applications */
-#define UART_FCR_TRIGGER_MASK	0xC0 /* Mask for the FIFO trigger range */
-#define UART_FCR_TRIGGER_1	0x00 /* Mask for trigger set at 1 */
-#define UART_FCR_TRIGGER_4	0x40 /* Mask for trigger set at 4 */
-#define UART_FCR_TRIGGER_8	0x80 /* Mask for trigger set at 8 */
-#define UART_FCR_TRIGGER_14	0xC0 /* Mask for trigger set at 14 */
-
-/*
- * These are the definitions for the Line Control Register
- * 
- * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting 
- * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
- */
-#define UART_LCR_DLAB	0x80	/* Devisor latch access bit */
-#define UART_LCR_SBC	0x40	/* Set break control */
-#define UART_LCR_SPAR	0x20	/* Stick parity (?) */
-#define UART_LCR_EPAR	0x10	/* Even paraity select */
-#define UART_LCR_PARITY	0x08	/* Parity Enable */
-#define UART_LCR_STOP	0x04	/* Stop bits: 0=1 stop bit, 1= 2 stop bits */
-#define UART_LCR_WLEN5  0x00	/* Wordlength: 5 bits */
-#define UART_LCR_WLEN6  0x01	/* Wordlength: 6 bits */
-#define UART_LCR_WLEN7  0x02	/* Wordlength: 7 bits */
-#define UART_LCR_WLEN8  0x03	/* Wordlength: 8 bits */
-
-/*
- * These are the definitions for the Line Status Register
- */
-#define UART_LSR_TEMT	0x40	/* Transmitter empty */
-#define UART_LSR_THRE	0x20	/* Transmit-hold-register empty */
-#define UART_LSR_BI	0x10	/* Break interrupt indicator */
-#define UART_LSR_FE	0x08	/* Frame error indicator */
-#define UART_LSR_PE	0x04	/* Parity error indicator */
-#define UART_LSR_OE	0x02	/* Overrun error indicator */
-#define UART_LSR_DR	0x01	/* Receiver data ready */
+#define SERIAL_MAGIC 0x5301
 
 /*
- * These are the definitions for the Interrupt Indentification Register
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
  */
-#define UART_IIR_NO_INT	0x01	/* No interrupts pending */
-#define UART_IIR_ID	0x06	/* Mask for the interrupt ID */
+#define SERIAL_XMIT_SIZE 4096
 
-#define UART_IIR_MSI	0x00	/* Modem status interrupt */
-#define UART_IIR_THRI	0x02	/* Transmitter holding register empty */
-#define UART_IIR_RDI	0x04	/* Receiver data interrupt */
-#define UART_IIR_RLSI	0x06	/* Receiver line status interrupt */
-
-/*
- * These are the definitions for the Interrupt Enable Register
- */
-#define UART_IER_MSI	0x08	/* Enable Modem status interrupt */
-#define UART_IER_RLSI	0x04	/* Enable receiver line status interrupt */
-#define UART_IER_THRI	0x02	/* Enable Transmitter holding register int. */
-#define UART_IER_RDI	0x01	/* Enable receiver data interrupt */
-
-/*
- * These are the definitions for the Modem Control Register
- */
-#define UART_MCR_LOOP	0x10	/* Enable loopback test mode */
-#define UART_MCR_OUT2	0x08	/* Out2 complement */
-#define UART_MCR_OUT1	0x04	/* Out1 complement */
-#define UART_MCR_RTS	0x02	/* RTS complement */
-#define UART_MCR_DTR	0x01	/* DTR complement */
-
 /*
- * These are the definitions for the Modem Status Register
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
  */
-#define UART_MSR_DCD	0x80	/* Data Carrier Detect */
-#define UART_MSR_RI	0x40	/* Ring Indicator */
-#define UART_MSR_DSR	0x20	/* Data Set Ready */
-#define UART_MSR_CTS	0x10	/* Clear to Send */
-#define UART_MSR_DDCD	0x08	/* Delta DCD */
-#define UART_MSR_TERI	0x04	/* Trailing edge ring indicator */
-#define UART_MSR_DDSR	0x02	/* Delta DSR */
-#define UART_MSR_DCTS	0x01	/* Delta CTS */
-#define UART_MSR_ANY_DELTA 0x0F	/* Any of the delta bits! */
+#define RS_EVENT_WRITE_WAKEUP	0
+#define RS_EVENT_HANGUP		1
 
+#endif /* __KERNEL__ */
 #endif /* _LINUX_SERIAL_H */
diff -u --recursive --new-file v1.1.12/linux/include/linux/serial_reg.h linux/include/linux/serial_reg.h
--- v1.1.12/linux/include/linux/serial_reg.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/serial_reg.h	Mon May 23 07:55:15 1994
@@ -0,0 +1,113 @@
+/*
+ * include/linux/serial.h
+ *
+ * Copyright (C) 1992, 1994 by Theodore Ts'o.
+ * 
+ * Redistribution of this file is permitted under the terms of the GNU 
+ * Public License (GPL)
+ * 
+ * These are the UART port assignments, expressed as offsets from the base
+ * register.  These assignments should hold for any serial port based on
+ * a 8250, 16450, or 16550(A).
+ */
+
+#ifndef _LINUX_SERIAL_REG_H
+#define _LINUX_SERIAL_REG_H
+
+#define UART_RX		0	/* In:  Receive buffer (DLAB=0) */
+#define UART_TX		0	/* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL	0	/* Out: Devisor Latch Low (DLAB=1) */
+#define UART_DLM	1	/* Out: Devisor Latch High (DLAB=1) */
+#define UART_IER	1	/* Out: Interrupt Enable Register */
+#define UART_IIR	2	/* In:  Interrupt ID Register */
+#define UART_FCR	2	/* Out: FIFO Control Register */
+#define UART_LCR	3	/* Out: Line Control Register */
+#define UART_MCR	4	/* Out: Modem Control Register */
+#define UART_LSR	5	/* In:  Line Status Register */
+#define UART_MSR	6	/* In:  Modem Status Register */
+#define UART_SCR	7	/* I/O: Scratch Register */
+
+/*
+ * These are the definitions for the FIFO Control Register
+ */
+#define UART_FCR_ENABLE_FIFO	0x01 /* Enable the FIFO */
+#define UART_FCR_CLEAR_RCVR	0x02 /* Clear the RCVR FIFO */
+#define UART_FCR_CLEAR_XMIT	0x04 /* Clear the XMIT FIFO */
+#define UART_FCR_DMA_SELECT	0x08 /* For DMA applications */
+#define UART_FCR_TRIGGER_MASK	0xC0 /* Mask for the FIFO trigger range */
+#define UART_FCR_TRIGGER_1	0x00 /* Mask for trigger set at 1 */
+#define UART_FCR_TRIGGER_4	0x40 /* Mask for trigger set at 4 */
+#define UART_FCR_TRIGGER_8	0x80 /* Mask for trigger set at 8 */
+#define UART_FCR_TRIGGER_14	0xC0 /* Mask for trigger set at 14 */
+
+/*
+ * These are the definitions for the Line Control Register
+ * 
+ * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting 
+ * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
+ */
+#define UART_LCR_DLAB	0x80	/* Devisor latch access bit */
+#define UART_LCR_SBC	0x40	/* Set break control */
+#define UART_LCR_SPAR	0x20	/* Stick parity (?) */
+#define UART_LCR_EPAR	0x10	/* Even paraity select */
+#define UART_LCR_PARITY	0x08	/* Parity Enable */
+#define UART_LCR_STOP	0x04	/* Stop bits: 0=1 stop bit, 1= 2 stop bits */
+#define UART_LCR_WLEN5  0x00	/* Wordlength: 5 bits */
+#define UART_LCR_WLEN6  0x01	/* Wordlength: 6 bits */
+#define UART_LCR_WLEN7  0x02	/* Wordlength: 7 bits */
+#define UART_LCR_WLEN8  0x03	/* Wordlength: 8 bits */
+
+/*
+ * These are the definitions for the Line Status Register
+ */
+#define UART_LSR_TEMT	0x40	/* Transmitter empty */
+#define UART_LSR_THRE	0x20	/* Transmit-hold-register empty */
+#define UART_LSR_BI	0x10	/* Break interrupt indicator */
+#define UART_LSR_FE	0x08	/* Frame error indicator */
+#define UART_LSR_PE	0x04	/* Parity error indicator */
+#define UART_LSR_OE	0x02	/* Overrun error indicator */
+#define UART_LSR_DR	0x01	/* Receiver data ready */
+
+/*
+ * These are the definitions for the Interrupt Indentification Register
+ */
+#define UART_IIR_NO_INT	0x01	/* No interrupts pending */
+#define UART_IIR_ID	0x06	/* Mask for the interrupt ID */
+
+#define UART_IIR_MSI	0x00	/* Modem status interrupt */
+#define UART_IIR_THRI	0x02	/* Transmitter holding register empty */
+#define UART_IIR_RDI	0x04	/* Receiver data interrupt */
+#define UART_IIR_RLSI	0x06	/* Receiver line status interrupt */
+
+/*
+ * These are the definitions for the Interrupt Enable Register
+ */
+#define UART_IER_MSI	0x08	/* Enable Modem status interrupt */
+#define UART_IER_RLSI	0x04	/* Enable receiver line status interrupt */
+#define UART_IER_THRI	0x02	/* Enable Transmitter holding register int. */
+#define UART_IER_RDI	0x01	/* Enable receiver data interrupt */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP	0x10	/* Enable loopback test mode */
+#define UART_MCR_OUT2	0x08	/* Out2 complement */
+#define UART_MCR_OUT1	0x04	/* Out1 complement */
+#define UART_MCR_RTS	0x02	/* RTS complement */
+#define UART_MCR_DTR	0x01	/* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD	0x80	/* Data Carrier Detect */
+#define UART_MSR_RI	0x40	/* Ring Indicator */
+#define UART_MSR_DSR	0x20	/* Data Set Ready */
+#define UART_MSR_CTS	0x10	/* Clear to Send */
+#define UART_MSR_DDCD	0x08	/* Delta DCD */
+#define UART_MSR_TERI	0x04	/* Trailing edge ring indicator */
+#define UART_MSR_DDSR	0x02	/* Delta DSR */
+#define UART_MSR_DCTS	0x01	/* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F	/* Any of the delta bits! */
+
+#endif /* _LINUX_SERIAL_REG_H */
+
diff -u --recursive --new-file v1.1.12/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.1.12/linux/include/linux/skbuff.h	Tue Apr 19 22:20:34 1994
+++ linux/include/linux/skbuff.h	Mon May 23 08:17:44 1994
@@ -70,6 +70,9 @@
 				arp;
   unsigned char			tries,lock,localroute;
   unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) */
+#ifdef CONFIG_SLAVE_BALANCING
+  unsigned short		in_dev_queue;
+#endif  
   unsigned long			padding[0];
   unsigned char			data[0];
 };
diff -u --recursive --new-file v1.1.12/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.1.12/linux/include/linux/sockios.h	Mon May 23 12:14:25 1994
+++ linux/include/linux/sockios.h	Mon May 23 08:17:44 1994
@@ -57,6 +57,8 @@
 #define SIOCGIFENCAP	0x8925		/* get/set slip encapsulation   */
 #define SIOCSIFENCAP	0x8926		
 #define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
+#define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
+#define SIOCSIFSLAVE	0x8930
 
 /* Routing table calls (oldrtent - don't use) */
 #define SIOCADDRTOLD	0x8940		/* add routing table entry	*/
@@ -71,6 +73,11 @@
 #define SIOCDRARP	0x8960		/* delete RARP table entry	*/
 #define SIOCGRARP	0x8961		/* get RARP table entry		*/
 #define SIOCSRARP	0x8962		/* set RARP table entry		*/
+
+/* Driver configuration calls */
+
+#define SIOCGIFMAP	0x8970		/* Get device parameters	*/
+#define SIOCSIFMAP	0x8971		/* Set device parameters	*/
 
 /* Device private ioctl calls */
 
diff -u --recursive --new-file v1.1.12/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v1.1.12/linux/include/linux/sysv_fs.h	Wed Jul 21 22:41:47 1993
+++ linux/include/linux/sysv_fs.h	Mon May 23 10:04:06 1994
@@ -316,6 +316,8 @@
 #define SYSV_DIRSIZE	sizeof(struct sysv_dir_entry)	/* size of every directory entry */
 
 
+#ifdef __KERNEL__
+
 /* Operations */
 /* ========== */
 
@@ -432,6 +434,8 @@
 extern struct inode_operations sysv_file_inode_operations_with_bmap;
 extern struct inode_operations sysv_dir_inode_operations;
 extern struct inode_operations sysv_symlink_inode_operations;
+
+#endif /* __KERNEL__ */
 
 #endif
 
diff -u --recursive --new-file v1.1.12/linux/include/linux/tqueue.h linux/include/linux/tqueue.h
--- v1.1.12/linux/include/linux/tqueue.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/tqueue.h	Mon May 23 07:55:15 1994
@@ -0,0 +1,179 @@
+/*
+ * tqueue.h --- task queue handling for Linux.
+ *
+ * Mostly based on a proposed bottom-half replacement code written by
+ * Kai Petzke, wpp@marie.physik.tu-berlin.de.
+ *
+ * Modified for use in the Linux kernel by Theodore Ts'o,
+ * tytso@mit.edu.  Any bugs are my fault, not Kai's.
+ *
+ * The original comment follows below.
+ */
+
+#ifndef _LINUX_TQUEUE_H
+#define _LINUX_TQUEUE_H
+
+#ifdef INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#define _INLINE_ extern __inline__
+#endif
+
+/*
+ * New proposed "bottom half" handlers:
+ * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de
+ *
+ * Advantages:
+ * - Bottom halfs are implemented as a linked list.  You can have as many
+ *   of them, as you want.
+ * - No more scanning of a bit field is required upon call of a bottom half.
+ * - Support for chained bottom half lists.  The run_task_queue() function can be
+ *   used as a bottom half handler.  This is for example usefull for bottom
+ *   halfs, which want to be delayed until the next clock tick.
+ *
+ * Problems:
+ * - The queue_task_irq() inline function is only atomic with respect to itself.
+ *   Problems can occur, when queue_task_irq() is called from a normal system
+ *   call, and an interrupt comes in.  No problems occur, when queue_task_irq()
+ *   is called from an interrupt or bottom half, and interrupted, as run_task_queue()
+ *   will not be executed/continued before the last interrupt returns.  If in
+ *   doubt, use queue_task(), not queue_task_irq().
+ * - Bottom halfs are called in the reverse order that they were linked into
+ *   the list.
+ */
+
+struct tq_struct {
+	struct tq_struct *next;		/* linked list of active bh's */
+	int sync;			/* must be initialized to zero */
+	void (*routine)(void *);	/* function to call */
+	void *data;			/* argument to function */
+};
+
+typedef struct tq_struct * task_queue;
+
+#define DECLARE_TASK_QUEUE(q)  task_queue q = &tq_last
+
+extern struct tq_struct tq_last;
+extern task_queue tq_timer;
+
+#ifdef INCLUDE_INLINE_FUNCS
+struct tq_struct tq_last = {
+	&tq_last, 0, 0, 0
+};
+#endif
+
+/*
+ * To implement your own list of active bottom halfs, use the following
+ * two definitions:
+ *
+ * struct tq_struct *my_bh = &tq_last;
+ * struct tq_struct run_my_bh = {
+ *	0, 0, (void *)(void *) run_task_queue, &my_bh
+ * };
+ *
+ * To activate a bottom half on your list, use:
+ *
+ *     queue_task(tq_pointer, &my_bh);
+ *
+ * To run the bottom halfs on your list put them on the immediate list by:
+ *
+ *     queue_task(&run_my_bh, &tq_immediate);
+ *
+ * This allows you to do deferred procession.  For example, you could
+ * have a bottom half list tq_timer, which is marked active by the timer
+ * interrupt.
+ */
+
+/*
+ * queue_task_irq: put the bottom half handler "bh_pointer" on the list
+ * "bh_list".  You may call this function only from an interrupt
+ * handler or a bottom half handler.
+ */
+_INLINE_ void queue_task_irq(struct tq_struct *bh_pointer,
+			       task_queue *bh_list)
+{
+	int l1;
+
+	__asm__ __volatile__ (
+	"btsl $0,%1\n\t"	/* bottom half already marked? */
+	"jc 1f\n\t"
+	"leal %2,%3\n\t"	/* address of the "next" field of bh_struct */
+	"xchgl %3,%0\n\t"	/* link bottom half into list */
+	"movl %3,%2\n1:"	/* save the pointer to next bottom half */
+	: "=m" (*bh_list), "=m" (bh_pointer -> sync), "=m" (bh_pointer -> next),
+	  "=r" (l1) );
+}
+
+/*
+ * queue_task_irq_off: put the bottom half handler "bh_pointer" on the list
+ * "bh_list".  You may call this function only when interrupts are off.
+ */
+_INLINE_ void queue_task_irq_off(struct tq_struct *bh_pointer,
+				 task_queue *bh_list)
+{
+	int l1;
+
+	__asm__ __volatile__ (
+	"testl $1,%1\n\t"	/* bottom half already marked? */
+	"jne 1f\n\t"
+	"movl $1,%1\n\t"			      
+	"leal %2,%3\n\t"	/* address of the "next" field of bh_struct */
+	"xchgl %3,%0\n\t"	/* link bottom half into list */
+	"movl %3,%2\n1:"	/* save the pointer to next bottom half */
+	: "=m" (*bh_list), "=m" (bh_pointer -> sync), "=m" (bh_pointer -> next),
+	  "=r" (l1) );
+}
+
+
+/*
+ * queue_task: as queue_task_irq, but can be called from anywhere.
+ */
+_INLINE_ void queue_task(struct tq_struct *bh_pointer,
+			   task_queue *bh_list)
+{
+	int l1;
+
+	__asm__ __volatile__ (
+	"btsl $0,%1\n\t"
+	"jc 1f\n\t"
+	"leal %2,%3\n\t"
+	"pushfl\n\t"		/* save interrupt flag */
+	"cli\n\t"		/* turn off interrupts */
+	"xchgl %3,%0\n\t"
+	"movl %3,%2\n\t"	/* now the linking step is really atomic! */
+	"popfl\n1:"		/* restore interrupt flag */
+	: "=m" (*bh_list), "=m" (bh_pointer -> sync), "=m" (bh_pointer -> next),
+	  "=r" (l1) );
+}
+
+/*
+ * Call all "bottom halfs" on a given list.
+ */
+_INLINE_ void run_task_queue(task_queue *list)
+{
+	register struct tq_struct *save_p;
+	register struct tq_struct *p;
+	void *arg;
+	void (*f) (void *);
+
+	while(1) {
+		p = &tq_last;
+		__asm__ __volatile__("xchgl %0,%2" : "=r" (p) :
+				     "0" (p), "m" (*list) : "memory");
+		if(p == &tq_last)
+			break;
+
+		do {
+			arg    = p -> data;
+			f      = p -> routine;
+			save_p = p -> next;
+			p -> sync = 0;
+			(*f)(arg);
+			p = save_p;
+		} while(p != &tq_last);
+	}
+}
+
+#undef _INLINE_
+
+#endif /* _LINUX_TQUEUE_H */
diff -u --recursive --new-file v1.1.12/linux/include/linux/tty.h linux/include/linux/tty.h
--- v1.1.12/linux/include/linux/tty.h	Fri Feb 18 11:08:41 1994
+++ linux/include/linux/tty.h	Mon May 23 07:55:11 1994
@@ -6,10 +6,18 @@
  */
 
 #include <linux/termios.h>
+#include <linux/tqueue.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_ldisc.h>
 
 #include <asm/system.h>
 
+/*
+ * Note: don't mess with NR_PTYS until you understand the tty minor 
+ * number allocation game...
+ */
 #define NR_CONSOLES	8
+#define NR_PTYS		64
 #define NR_LDISCS	16
 
 /*
@@ -54,96 +62,31 @@
 #define __DISABLED_CHAR '\0'
 
 /*
- * See comment for the tty_struct structure before changing
- * TTY_BUF_SIZE.  Actually, there should be different sized tty_queue
- * structures for different purposes.  1024 bytes for the transmit
- * queue is way overkill.  TYT, 9/14/92
+ * This is the flip buffer used for the tty driver.  The buffer is
+ * located in the tty structure, and is used as a high speed interface
+ * between the tty driver and the tty line discpline.
  */
-#define TTY_BUF_SIZE 1024	/* Must be a power of 2 */
-
-struct tty_queue {
-	unsigned long head;
-	unsigned long tail;
-	struct wait_queue * proc_list;
-	unsigned char buf[TTY_BUF_SIZE];
-};
+#define TTY_FLIPBUF_SIZE 512
 
-struct serial_struct {
-	int	type;
-	int	line;
-	int	port;
-	int	irq;
-	int	flags;
-	int	xmit_fifo_size;
-	int	custom_divisor;
-	int	baud_base;
-	unsigned short	close_delay;
-	char	reserved_char[2];
-	int	hub6;
-	int	reserved[5];
+struct tty_flip_buffer {
+	struct tq_struct tqueue;
+	unsigned char	char_buf[2*TTY_FLIPBUF_SIZE];
+	char		flag_buf[2*TTY_FLIPBUF_SIZE];
+	char		*char_buf_ptr;
+	unsigned char	*flag_buf_ptr;
+	int		count;
+	int		buf_num;
 };
 
 /*
- * These are the supported serial types.
- */
-#define PORT_UNKNOWN	0
-#define PORT_8250	1
-#define PORT_16450	2
-#define PORT_16550	3
-#define PORT_16550A	4
-#define PORT_MAX	4
-
-/*
- * Definitions for async_struct (and serial_struct) flags field
+ * When a break, frame error, or parity error happens, these codes are
+ * stuffed into the flags buffer.
  */
-#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
-				   on the callout port */
-#define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
-#define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
-#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ASYNC_SPD_MASK	0x0030
-#define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
-#define ASYNC_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
-#define ASYNC_SPD_CUST	0x0030  /* Use user-specified divisor */
-
-#define ASYNC_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
-#define ASYNC_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ASYNC_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
-#define ASYNC_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
-
-#define ASYNC_FLAGS	0x0FFF	/* Possible legal async flags */
-#define ASYNC_USR_MASK 0x0430	/* Legal flags that non-privileged
-				 * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ASYNC_INITIALIZED	0x80000000 /* Serial port was initialized */
-#define ASYNC_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
-#define ASYNC_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
-#define ASYNC_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
-#define ASYNC_CLOSING		0x08000000 /* Serial port is closing */
-
-#define IS_A_CONSOLE(min)	(((min) & 0xC0) == 0x00)
-#define IS_A_SERIAL(min)	(((min) & 0xC0) == 0x40)
-#define IS_A_PTY(min)		((min) & 0x80)
-#define IS_A_PTY_MASTER(min)	(((min) & 0xC0) == 0x80)
-#define IS_A_PTY_SLAVE(min)	(((min) & 0xC0) == 0xC0)
-#define PTY_OTHER(min)		((min) ^ 0x40)
-
-#define SL_TO_DEV(line)		((line) | 0x40)
-#define DEV_TO_SL(min)		((min) & 0x3F)
-
-#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
-#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
-#define EMPTY(a) ((a)->head == (a)->tail)
-#define LEFT(a) (((a)->tail-(a)->head-1)&(TTY_BUF_SIZE-1))
-#define LAST(a) ((a)->buf[(TTY_BUF_SIZE-1)&((a)->head-1)])
-#define FULL(a) (!LEFT(a))
-#define CHARS(a) (((a)->head-(a)->tail)&(TTY_BUF_SIZE-1))
-
-extern void put_tty_queue(unsigned char c, struct tty_queue * queue);
-extern int get_tty_queue(struct tty_queue * queue);
+#define TTY_NORMAL	0
+#define TTY_BREAK	1
+#define TTY_FRAME	2
+#define TTY_PARITY	3
+#define TTY_OVERRUN	4
 
 #define INTR_CHAR(tty) ((tty)->termios->c_cc[VINTR])
 #define QUIT_CHAR(tty) ((tty)->termios->c_cc[VQUIT])
@@ -240,99 +183,52 @@
  * 						- TYT, 9/14/92
  */
 struct tty_struct {
-	struct termios *termios;
+	int	magic;
+	struct tty_driver driver;
+	struct tty_ldisc ldisc;
+	struct termios *termios, *termios_locked;
 	int pgrp;
 	int session;
-	unsigned char stopped:1, hw_stopped:1, packet:1, lnext:1;
-	unsigned char char_error:3;
-	unsigned char erasing:1;
-	unsigned char ctrl_status;
-	short line;
-	int disc;
+	dev_t	device;
 	int flags;
 	int count;
-	unsigned int column;
 	struct winsize winsize;
-	int  (*open)(struct tty_struct * tty, struct file * filp);
-	void (*close)(struct tty_struct * tty, struct file * filp);
-	void (*write)(struct tty_struct * tty);
-	int  (*ioctl)(struct tty_struct *tty, struct file * file,
-		    unsigned int cmd, unsigned long arg);
-	void (*throttle)(struct tty_struct * tty, int status);
-	void (*set_termios)(struct tty_struct *tty, struct termios * old);
-	void (*stop)(struct tty_struct *tty);
-	void (*start)(struct tty_struct *tty);
-	void (*hangup)(struct tty_struct *tty);
+	unsigned char stopped:1, hw_stopped:1, packet:1;
+	unsigned char ctrl_status;
+
 	struct tty_struct *link;
-	unsigned char *write_data_ptr;
-	int write_data_cnt;
-	void (*write_data_callback)(void * data);
-	void * write_data_arg;
-	int readq_flags[TTY_BUF_SIZE/32];
-	int secondary_flags[TTY_BUF_SIZE/32];
-	int canon_data;
-	unsigned long canon_head;
-	unsigned int canon_column;
-	struct tty_queue read_q;
-	struct tty_queue write_q;
-	struct tty_queue secondary;
+	struct fasync_struct *fasync;
+	struct tty_flip_buffer flip;
+	int max_flip_cnt;
+	struct wait_queue *write_wait;
+	struct wait_queue *read_wait;
 	void *disc_data;
-};
+	void *driver_data;
 
-struct tty_ldisc {
-	int	flags;
+#define N_TTY_BUF_SIZE 4096
+	
 	/*
-	 * The following routines are called from above.
+	 * The following is data for the N_TTY line discpline.  For
+	 * historical reasons, this is included in the tty structure.
 	 */
-	int	(*open)(struct tty_struct *);
-	void	(*close)(struct tty_struct *);
-	int	(*read)(struct tty_struct * tty, struct file * file,
-			unsigned char * buf, unsigned int nr);
-	int	(*write)(struct tty_struct * tty, struct file * file,
-			 unsigned char * buf, unsigned int nr);	
-	int	(*ioctl)(struct tty_struct * tty, struct file * file,
-			 unsigned int cmd, unsigned long arg);
-	int	(*select)(struct tty_struct * tty, struct inode * inode,
-			  struct file * file, int sel_type,
-			  struct select_table_struct *wait);
-	/*
-	 * The following routines are called from below.
-	 */
-	void	(*handler)(struct tty_struct *);
+	unsigned int column;
+	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
+	unsigned short minimum_to_wake;
+	unsigned overrun_time;
+	int num_overrun;
+	int process_char_map[256/32];
+	char *read_buf;
+	int read_head;
+	int read_tail;
+	int read_cnt;
+	int read_flags[N_TTY_BUF_SIZE/32];
+	int canon_data;
+	unsigned long canon_head;
+	unsigned int canon_column;
 };
 
-#define LDISC_FLAG_DEFINED	0x00000001
-
-/*
- * These are the different types of thottle status which can be sent
- * to the low-level tty driver.  The tty_io.c layer is responsible for
- * notifying the low-level tty driver of the following conditions:
- * secondary queue full, secondary queue available, and read queue
- * available.  The low-level driver must send the read queue full
- * command to itself, if it is interested in that condition.
- *
- * Note that the low-level tty driver may elect to ignore one or both
- * of these conditions; normally, however, it will use ^S/^Q or some
- * sort of hardware flow control to regulate the input to try to avoid
- * overflow.  While the low-level driver is responsible for all
- * receiving flow control, note that the ^S/^Q handling (but not
- * hardware flow control) is handled by the upper layer, in
- * copy_to_cooked.  
- */
-#define TTY_THROTTLE_SQ_FULL	1
-#define TTY_THROTTLE_SQ_AVAIL	2
-#define TTY_THROTTLE_RQ_FULL	3
-#define TTY_THROTTLE_RQ_AVAIL	4
-
-/*
- * This defines the low- and high-watermarks for the various conditions.
- * Again, the low-level driver is free to ignore any of these, and has
- * to implement RQ_THREHOLD_LW for itself if it wants it.
- */
-#define SQ_THRESHOLD_LW	16
-#define SQ_THRESHOLD_HW 768
-#define RQ_THRESHOLD_LW 16
-#define RQ_THRESHOLD_HW 768
+/* tty magic number */
+#define TTY_MAGIC		0x5401
 
 /*
  * These bits are used in the flags field of the tty structure.
@@ -342,38 +238,18 @@
  * tty->write.  Thus, you must use the inline functions set_bit() and
  * clear_bit() to make things atomic.
  */
-#define TTY_WRITE_BUSY 0
-#define TTY_READ_BUSY 1
-#define TTY_SQ_THROTTLED 2
-#define TTY_RQ_THROTTLED 3
-#define TTY_IO_ERROR 4
-#define TTY_SLAVE_CLOSED 5
-#define TTY_EXCLUSIVE 6
-
-/*
- * When a break, frame error, or parity error happens, these codes are
- * stuffed into the read queue, and the relevant bit in readq_flag bit
- * array is set.
- */
-#define TTY_BREAK	1
-#define TTY_FRAME	2
-#define TTY_PARITY	3
-#define TTY_OVERRUN	4
+#define TTY_THROTTLED 0
+#define TTY_IO_ERROR 1
+#define TTY_SLAVE_CLOSED 2
+#define TTY_EXCLUSIVE 3
+#define TTY_DEBUG 4
+#define TTY_DO_WRITE_WAKEUP 5
 
 #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
-#define TTY_READ_FLUSH(tty) tty_read_flush((tty))
 
 extern void tty_write_flush(struct tty_struct *);
-extern void tty_read_flush(struct tty_struct *);
 
-/* Number of chars that must be available in a write queue before
-   the queue is awakened. */
-#define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
-
-extern struct tty_struct *tty_table[];
-extern struct termios *tty_termios[];
-extern struct termios *termios_locked[];
-extern int tty_check_write[];
+extern struct termios tty_std_termios;
 extern struct tty_struct * redirect;
 extern struct tty_ldisc ldiscs[];
 extern int fg_console;
@@ -381,9 +257,6 @@
 extern unsigned long video_num_lines;
 extern struct wait_queue * keypress_wait;
 
-#define TTY_TABLE_IDX(nr)	((nr) ? (nr) : (fg_console+1))
-#define TTY_TABLE(nr) 		(tty_table[TTY_TABLE_IDX(nr)])
-
 /*	intr=^C		quit=^|		erase=del	kill=^U
 	eof=^D		vtime=\0	vmin=\1		sxtc=\0
 	start=^Q	stop=^S		susp=^Z		eol=\0
@@ -395,21 +268,22 @@
 extern long rs_init(long);
 extern long lp_init(long);
 extern long con_init(long);
+extern long pty_init(long);
 extern long tty_init(long);
 
-extern void flush_input(struct tty_struct * tty);
-extern void flush_output(struct tty_struct * tty);
+extern int tty_paranoia_check(struct tty_struct *tty, dev_t device,
+			      const char *routine);
+extern char *_tty_name(struct tty_struct *tty, char *buf);
+extern char *tty_name(struct tty_struct *tty);
 extern void wait_until_sent(struct tty_struct * tty, int timeout);
-extern int check_change(struct tty_struct * tty, int channel);
+extern int tty_check_change(struct tty_struct * tty);
 extern void stop_tty(struct tty_struct * tty);
 extern void start_tty(struct tty_struct * tty);
 extern int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc);
+extern int tty_register_driver(struct tty_driver *driver);
 extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
 			     int buflen);
-extern int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
-			  void (*callback)(void * data), void * callarg);
 
-extern int tty_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 extern int is_orphaned_pgrp(int pgrp);
 extern int is_ignored(int sig);
 extern int tty_signal(int sig, struct tty_struct *tty);
@@ -420,10 +294,12 @@
 extern void do_SAK(struct tty_struct *tty);
 extern void disassociate_ctty(int priv);
 
-/* tty write functions */
+/* n_tty.c */
+extern struct tty_ldisc tty_ldisc_N_TTY;
 
-extern void rs_write(struct tty_struct * tty);
-extern void con_write(struct tty_struct * tty);
+/* tty_ioctl.c */
+extern int n_tty_ioctl(struct tty_struct * tty, struct file * file,
+		       unsigned int cmd, unsigned long arg);
 
 /* serial.c */
 
diff -u --recursive --new-file v1.1.12/linux/include/linux/tty_driver.h linux/include/linux/tty_driver.h
--- v1.1.12/linux/include/linux/tty_driver.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/tty_driver.h	Mon May 23 07:55:15 1994
@@ -0,0 +1,178 @@
+#ifndef _LINUX_TTY_DRIVER_H
+#define _LINUX_TTY_DRIVER_H
+
+/*
+ * This structure defines the interface between the low-level tty
+ * driver and the tty routines.  The following routines can be
+ * defined; unless noted otherwise, they are optional, and can be
+ * filled in with a null pointer.
+ *
+ * int  (*open)(struct tty_struct * tty, struct file * filp);
+ *
+ * 	This routine is called when a particular tty device is opened.
+ * 	This routine is mandatory; if this routine is not filled in,
+ * 	the attempted open will fail with ENODEV.
+ *     
+ * void (*close)(struct tty_struct * tty, struct file * filp);
+ *
+ * 	This routine is called when a particular tty device is closed.
+ *
+ * int (*write)(struct tty_struct * tty, int from_user,
+ * 		 unsigned char *buf, int count);
+ *
+ * 	This routine is called by the kernel to write a series of
+ * 	characters to the tty device.  The characters may come from
+ * 	user space or kernel space.  This routine will return the
+ *	number of characters actually accepted for writing.  This
+ *	routine is mandatory.
+ *
+ * void (*put_char)(struct tty_struct *tty, unsigned char ch);
+ *
+ * 	This routine is called by the kernel to write a single
+ * 	character to the tty device.  If the kernel uses this routine,
+ * 	it must call the flush_chars() routine (if defined) when it is
+ * 	done stuffing characters into the driver.  If there is no room
+ * 	in the queue, the character is ignored.
+ *
+ * void (*flush_chars)(struct tty_struct *tty);
+ *
+ * 	This routine is called by the kernel after it has written a
+ * 	series of characters to the tty device using put_char().  
+ * 
+ * int  (*write_room)(struct tty_struct *tty);
+ *
+ * 	This routine returns the numbers of characters the tty driver
+ * 	will accept for queueing to be writen.  This number is subject
+ * 	to change as output buffers get emptied, or if the output flow
+ *	control is acted.
+ * 
+ * int  (*ioctl)(struct tty_struct *tty, struct file * file,
+ * 	    unsigned int cmd, unsigned long arg);
+ *
+ * 	This routine allows the tty driver to implement
+ *	device-specific iotctl's.  If the ioctl number passed in cmd
+ * 	is not recognized by the driver, it should return ENOIOCTLCMD.
+ * 
+ * void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ *
+ * 	This routine allows the tty driver to be notified when
+ * 	device's termios settings have changed.  
+ * 
+ * void (*throttle)(struct tty_struct * tty);
+ *
+ * 	This routine notifies the tty driver that input buffers for
+ * 	the line discpline are close to full, and it should somehow
+ * 	signal that no more characters should be sent to the tty.
+ * 
+ * void (*unthrottle)(struct tty_struct * tty);
+ *
+ * 	This routine notifies the tty drivers that it should signals
+ * 	that characters can now be sent to the tty without fear of
+ * 	overrunning the input buffers of the line discplines.
+ * 
+ * void (*stop)(struct tty_struct *tty);
+ *
+ * 	This routine notfies the tty driver that it should stop
+ * 	outputting characters to the tty device.  
+ * 
+ * void (*start)(struct tty_struct *tty);
+ *
+ * 	This routine notifies the tty driver that it resume sending
+ *	characters to the tty device.
+ * 
+ * void (*hangup)(struct tty_struct *tty);
+ *
+ * 	This routine notifies the tty driver that it should hangup the
+ * 	tty device.
+ * 
+ */
+
+struct tty_driver {
+	int	magic;		/* magic number for this structure */
+	char	*name;
+	int	name_base;	/* offset of printed name */
+	short	major;		/* major device number */
+	short	minor_start;	/* start of minor device number*/
+	short	num;		/* number of devices */
+	short	type;		/* type of tty driver */
+	short	subtype;	/* subtype of tty driver */
+	struct termios init_termios; /* Initial termios */
+	int	flags;		/* tty driver flags */
+	int	*refcount;	/* for loadable tty drivers */
+	struct tty_driver *other; /* only used for the PTY driver */
+
+	/*
+	 * Pointer to the tty data structures
+	 */
+	struct tty_struct **table;
+	struct termios **termios;
+	struct termios **termios_locked;
+	
+	/*
+	 * Interface routines from the upper tty layer to the tty
+	 * driver.
+	 */
+	int  (*open)(struct tty_struct * tty, struct file * filp);
+	void (*close)(struct tty_struct * tty, struct file * filp);
+	int  (*write)(struct tty_struct * tty, int from_user,
+		      unsigned char *buf, int count);
+	void (*put_char)(struct tty_struct *tty, unsigned char ch);
+	void (*flush_chars)(struct tty_struct *tty);
+	int  (*write_room)(struct tty_struct *tty);
+	int  (*chars_in_buffer)(struct tty_struct *tty);
+	int  (*ioctl)(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg);
+	void (*set_termios)(struct tty_struct *tty, struct termios * old);
+	void (*throttle)(struct tty_struct * tty);
+	void (*unthrottle)(struct tty_struct * tty);
+	void (*stop)(struct tty_struct *tty);
+	void (*start)(struct tty_struct *tty);
+	void (*hangup)(struct tty_struct *tty);
+	void (*flush_buffer)(struct tty_struct *tty);
+
+	/*
+	 * linked list pointers
+	 */
+	struct tty_driver *next;
+	struct tty_driver *prev;
+};
+
+/* tty driver magic number */
+#define TTY_DRIVER_MAGIC		0x5402
+
+/*
+ * tty driver flags
+ * 
+ * TTY_DRIVER_RESET_TERMIOS --- requests the tty layer to reset the
+ * 	termios setting when the last process has closed the device.
+ * 	Used for PTY's, in particular.
+ * 
+ * TTY_DRIVER_REAL_RAW --- if set, indicates that the driver will
+ * 	guarantee never not to set any special character handling
+ * 	flags if ((IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR ||
+ * 	!INPCK)).  That is, if there is no reason for the driver to
+ * 	send notifications of parity and break characters up to the
+ * 	line driver, it won't do so.  This allows the line driver to
+ *	optimize for this case if this flag is set.  (Note that there
+ * 	is also a promise, if the above case is true, not to signal
+ * 	overruns, either.)
+ */
+#define TTY_DRIVER_INSTALLED		0x0001
+#define TTY_DRIVER_RESET_TERMIOS	0x0002
+#define TTY_DRIVER_REAL_RAW		0x0004
+
+/* tty driver types */
+#define TTY_DRIVER_TYPE_SYSTEM		0x0001
+#define TTY_DRIVER_TYPE_CONSOLE		0x0002
+#define TTY_DRIVER_TYPE_SERIAL		0x0003
+#define TTY_DRIVER_TYPE_PTY		0x0004
+
+/* system subtypes (magic, used by tty_io.c) */
+#define SYSTEM_TYPE_TTY			0x0001
+#define SYSTEM_TYPE_CONSOLE		0x0002
+
+/* pty subtypes (magic, used by tty_io.c) */
+#define PTY_TYPE_MASTER			0x0001
+#define PTY_TYPE_SLAVE			0x0002
+
+#endif /* #ifdef _LINUX_TTY_DRIVER_H */
diff -u --recursive --new-file v1.1.12/linux/include/linux/tty_flip.h linux/include/linux/tty_flip.h
--- v1.1.12/linux/include/linux/tty_flip.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/tty_flip.h	Mon May 23 07:55:15 1994
@@ -0,0 +1,34 @@
+#ifndef _LINUX_TTY_FLIP_H
+#define _LINUX_TTY_FLIP_H
+
+#ifdef INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#define _INLINE_ extern __inline__
+#endif
+
+_INLINE_ void tty_insert_flip_char(struct tty_struct *tty,
+				   unsigned char ch, char flag)
+{
+	if (tty->flip.count++ >= TTY_FLIPBUF_SIZE)
+		return;
+	*tty->flip.flag_buf_ptr++ = flag;
+	*tty->flip.char_buf_ptr++ = ch;
+}
+
+_INLINE_ void tty_schedule_flip(struct tty_struct *tty)
+{
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+#undef _INLINE_
+
+
+#endif /* _LINUX_TTY_FLIP_H */
+
+
+
+
+
+
+
diff -u --recursive --new-file v1.1.12/linux/include/linux/tty_ldisc.h linux/include/linux/tty_ldisc.h
--- v1.1.12/linux/include/linux/tty_ldisc.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/tty_ldisc.h	Mon May 23 07:55:15 1994
@@ -0,0 +1,43 @@
+#ifndef _LINUX_TTY_LDISC_H
+#define _LINUX_TTY_LDISC_H
+
+/*
+ * Definitions for the tty line discipline
+ */
+
+struct tty_ldisc {
+	int	magic;
+	int	num;
+	int	flags;
+	/*
+	 * The following routines are called from above.
+	 */
+	int	(*open)(struct tty_struct *);
+	void	(*close)(struct tty_struct *);
+	void	(*flush_buffer)(struct tty_struct *tty);
+	int	(*chars_in_buffer)(struct tty_struct *tty);
+	int	(*read)(struct tty_struct * tty, struct file * file,
+			unsigned char * buf, unsigned int nr);
+	int	(*write)(struct tty_struct * tty, struct file * file,
+			 unsigned char * buf, unsigned int nr);	
+	int	(*ioctl)(struct tty_struct * tty, struct file * file,
+			 unsigned int cmd, unsigned long arg);
+	void	(*set_termios)(struct tty_struct *tty, struct termios * old);
+	int	(*select)(struct tty_struct * tty, struct inode * inode,
+			  struct file * file, int sel_type,
+			  struct select_table_struct *wait);
+	
+	/*
+	 * The following routines are called from below.
+	 */
+	void	(*receive_buf)(struct tty_struct *, unsigned char *cp,
+			       char *fp, int count);
+	int	(*receive_room)(struct tty_struct *);
+	void	(*write_wakeup)(struct tty_struct *);
+};
+
+#define TTY_LDISC_MAGIC	0x5403
+
+#define LDISC_FLAG_DEFINED	0x00000001
+
+#endif /* _LINUX_TTY_LDISC_H */
diff -u --recursive --new-file v1.1.12/linux/kernel/Makefile linux/kernel/Makefile
--- v1.1.12/linux/kernel/Makefile	Mon Apr 25 10:04:36 1994
+++ linux/kernel/Makefile	Mon May 23 07:55:11 1994
@@ -19,7 +19,7 @@
 OBJS  = sched.o sys_call.o traps.o irq.o dma.o fork.o \
  	panic.o printk.o vsprintf.o sys.o module.o ksyms.o exit.o \
 	signal.o mktime.o ptrace.o ioport.o itimer.o \
-	info.o ldt.o time.o vm86.o
+	info.o ldt.o time.o tqueue.o vm86.o
 
 all: kernel.o
 
diff -u --recursive --new-file v1.1.12/linux/kernel/exit.c linux/kernel/exit.c
--- v1.1.12/linux/kernel/exit.c	Sat May  7 14:54:13 1994
+++ linux/kernel/exit.c	Mon May 23 10:04:04 1994
@@ -52,6 +52,11 @@
 		return -EPERM;
 	if (!sig)
 		return 0;
+	/*
+	 * Forget it if the process is already zombie'd.
+	 */
+	if (p->state == TASK_ZOMBIE)
+		return 0;
 	if ((sig == SIGKILL) || (sig == SIGCONT)) {
 		if (p->state == TASK_STOPPED)
 			p->state = TASK_RUNNING;
diff -u --recursive --new-file v1.1.12/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.1.12/linux/kernel/ksyms.c	Sat May  7 14:54:15 1994
+++ linux/kernel/ksyms.c	Mon May 23 10:00:22 1994
@@ -14,6 +14,10 @@
 #include <linux/ptrace.h>
 #include <linux/sys.h>
 #include <linux/utsname.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_INET
+#include <linux/netdevice.h>
+#endif
   
 extern void *sys_call_table;
 
@@ -38,6 +42,18 @@
 
 extern void (* iABI_hook)(struct pt_regs * regs);
 #endif
+#ifdef CONFIG_INET
+extern int register_netdev(struct device *);
+extern void unregister_netdev(struct device *);
+extern void ether_setup(struct device *);
+extern struct sk_buff *alloc_skb(unsigned int,int);
+extern void kfree_skb(struct sk_buff *, int);
+extern void snarf_region(unsigned int, unsigned int);
+extern void netif_rx(struct sk_buff *);
+extern int dev_rint(unsigned char *, long, int, struct device *);
+extern void dev_tint(struct device *);
+extern struct device *irq2dev_map[];
+#endif
 
 struct {
 	void *addr;
@@ -81,6 +97,8 @@
 	/* interrupt handling */
 	X(request_irq),
 	X(free_irq),
+	X(bh_active),
+	X(bh_mask),
 
 	/* process management */
 	X(wake_up),
@@ -131,6 +149,20 @@
 
 	/* Miscellaneous access points */
 	X(si_meminfo),
+#endif
+
+#ifdef CONFIG_INET
+	/* support for loadable net drivers */
+	X(register_netdev),
+	X(unregister_netdev),
+	X(ether_setup),
+	X(alloc_skb),
+	X(kfree_skb),
+	X(snarf_region),
+	X(netif_rx),
+	X(dev_rint),
+	X(dev_tint),
+	X(irq2dev_map),
 #endif
 };
 
diff -u --recursive --new-file v1.1.12/linux/kernel/sched.c linux/kernel/sched.c
--- v1.1.12/linux/kernel/sched.c	Sun May  1 12:12:49 1994
+++ linux/kernel/sched.c	Mon May 23 07:55:11 1994
@@ -24,6 +24,7 @@
 #include <linux/segment.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/tqueue.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -40,6 +41,8 @@
 volatile struct timeval xtime;		/* The current time */
 int tickadj = 500/HZ;			/* microsecs */
 
+DECLARE_TASK_QUEUE(tq_timer);
+
 /*
  * phase-lock loop variables
  */
@@ -548,6 +551,11 @@
 	}
 }
 
+void tqueue_bh(void * unused)
+{
+	run_task_queue(&tq_timer);
+}
+
 /*
  * The int argument is really a (struct pt_regs *), in case the
  * interrupt wants to know from where it was called. The timer
@@ -667,6 +675,8 @@
 			mark_bh(TIMER_BH);
 		}
 	}
+	if (tq_timer != &tq_last)
+		mark_bh(TQUEUE_BH);
 	sti();
 }
 
@@ -776,6 +786,7 @@
 	struct desc_struct * p;
 
 	bh_base[TIMER_BH].routine = timer_bh;
+	bh_base[TQUEUE_BH].routine = tqueue_bh;
 	if (sizeof(struct sigaction) != 16)
 		panic("Struct sigaction MUST be 16 bytes");
 	set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
diff -u --recursive --new-file v1.1.12/linux/kernel/sys.c linux/kernel/sys.c
--- v1.1.12/linux/kernel/sys.c	Mon May 23 12:14:25 1994
+++ linux/kernel/sys.c	Mon May 23 07:55:12 1994
@@ -490,7 +490,7 @@
 		return -EPERM;
 	current->leader = 1;
 	current->session = current->pgrp = current->pid;
-	current->tty = -1;
+	current->tty = NULL;
 	return current->pgrp;
 }
 
diff -u --recursive --new-file v1.1.12/linux/kernel/tqueue.c linux/kernel/tqueue.c
--- v1.1.12/linux/kernel/tqueue.c	Thu Jan  1 02:00:00 1970
+++ linux/kernel/tqueue.c	Mon May 23 07:55:15 1994
@@ -0,0 +1,10 @@
+/*
+ * tqueue.c --- task queue handling for Linux.
+ *
+ * This routine merely draws in the static portion of the task queue
+ * inline functions.  Look in tqueue.h for the relevant functions.
+ */
+
+#define INCLUDE_INLINE_FUNCS
+
+#include <linux/tqueue.h>
diff -u --recursive --new-file v1.1.12/linux/kernel/vm86.c linux/kernel/vm86.c
--- v1.1.12/linux/kernel/vm86.c	Mon May 23 12:14:25 1994
+++ linux/kernel/vm86.c	Mon May 23 07:45:07 1994
@@ -250,7 +250,7 @@
 __asm__ __volatile__( \
 	"movb %%fs:0(%1,%0),%b2\n\t" \
 	"incw %w0" \
-	: "=r" (ptr), "=r" (base), "=r" (__res) \
+	: "=r" (ptr), "=r" (base), "=q" (__res) \
 	: "0" (ptr), "1" (base), "2" (0)); \
 __res; })
 
@@ -261,7 +261,7 @@
 	"incw %w0\n\t" \
 	"movb %%fs:0(%1,%0),%h2\n\t" \
 	"incw %w0" \
-	: "=r" (ptr), "=r" (base), "=r" (__res) \
+	: "=r" (ptr), "=r" (base), "=q" (__res) \
 	: "0" (ptr), "1" (base), "2" (0)); \
 __res; })
 
@@ -278,7 +278,7 @@
 	"movb %%fs:0(%1,%0),%h2\n\t" \
 	"incw %w0\n\t" \
 	"rorl $16,%2" \
-	: "=r" (ptr), "=r" (base), "=r" (__res) \
+	: "=r" (ptr), "=r" (base), "=q" (__res) \
 	: "0" (ptr), "1" (base)); \
 __res; })
 
diff -u --recursive --new-file v1.1.12/linux/modules/NET_MODULES linux/modules/NET_MODULES
--- v1.1.12/linux/modules/NET_MODULES	Thu Jan  1 02:00:00 1970
+++ linux/modules/NET_MODULES	Mon May 23 11:53:54 1994
@@ -0,0 +1 @@
+3c509.o de600.o
diff -u --recursive --new-file v1.1.12/linux/net/Makefile linux/net/Makefile
--- v1.1.12/linux/net/Makefile	Wed Dec  1 14:44:15 1993
+++ linux/net/Makefile	Mon May 23 08:17:45 1994
@@ -21,7 +21,7 @@
 .c.s:
 	$(CC) $(CFLAGS) -S $<
 
-OBJS	=  Space.o ddi.o socket.o
+OBJS	=  socket.o protocols.o
 
 all:		subdirs net.o
 
diff -u --recursive --new-file v1.1.12/linux/net/Space.c linux/net/Space.c
--- v1.1.12/linux/net/Space.c	Tue Apr 19 10:53:50 1994
+++ linux/net/Space.c	Mon May 23 08:17:45 1994
@@ -1,59 +0,0 @@
-/*
- * Space.c	Defines which protocol modules and I/O device drivers get
- *		linked into the LINUX kernel.  Currently, this is only used
- *		by the NET layer of LINUX, but it eventually might move to
- *		an upper directory of the system.
- *
- * Version:	@(#)Space.c	1.0.2	04/22/93
- *
- * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- *	Please see the comments in ddi.c - Alan
- * 
- */
- 
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/ddi.h>
-
-
-#define CONFIG_UNIX		YES		/* always present...	*/
-
-
-/*
- * Section A:	Networking Protocol Handlers.
- *		This section defines which networking protocols get
- *		linked into the SOCKET layer of the Linux kernel.
- *		Currently, these are AF_UNIX (always) and AF_INET.
- */
-#ifdef	CONFIG_UNIX
-#  include "unix/unix.h"
-#endif
-#ifdef	CONFIG_INET
-#  include <linux/inet.h>
-#endif
-#ifdef CONFIG_IPX
-#include "inet/ipxcall.h"
-#endif
-#ifdef CONFIG_AX25
-#include "inet/ax25call.h"
-#endif
-
-struct ddi_proto protocols[] = {
-#ifdef	CONFIG_UNIX
-  { "UNIX",	unix_proto_init	},
-#endif
-#ifdef  CONFIG_IPX
-  { "IPX",	ipx_proto_init },
-#endif
-#ifdef CONFIG_AX25  
-  { "AX.25",	ax25_proto_init },
-#endif  
-#ifdef	CONFIG_INET
-  { "INET",	inet_proto_init	},
-#endif
-  { NULL,	NULL		}
-};
-
-
diff -u --recursive --new-file v1.1.12/linux/net/inet/Makefile linux/net/inet/Makefile
--- v1.1.12/linux/net/inet/Makefile	Tue Apr 19 10:53:50 1994
+++ linux/net/inet/Makefile	Mon May 23 08:29:50 1994
@@ -15,23 +15,34 @@
 	$(CC) $(CFLAGS) -S -o $*.s $<
 
 
-OBJS	= sock.o utils.o route.o proc.o timer.o protocol.o \
-	  eth.o packet.o arp.o dev.o ip.o raw.o icmp.o tcp.o udp.o \
-	  datagram.o skbuff.o devinet.o
+OBJS	:= sock.o eth.o dev.o skbuff.o datagram.o
 
+ifdef CONFIG_INET
+
+OBJS	:= $(OBJS) utils.o route.o proc.o timer.o protocol.o packet.o \
+		   arp.o ip.o raw.o icmp.o tcp.o udp.o devinet.o af_inet.o
+
+endif
+
+ifdef CONFIG_INET_RARP
+
+OBJS	:= $(OBJS) rarp.o
+
+endif
+
 ifdef CONFIG_AX25
 
-OBJS	:= $(OBJS) ax25.o ax25_in.o ax25_out.o ax25_subr.o ax25_timer.o
+OBJS	:= $(OBJS) ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
 
 endif
 
 ifdef CONFIG_IPX
 
-OBJS	:= $(OBJS) ipx.o
+OBJS	:= $(OBJS) ipx.o pe2.o p8022.o p8023.o
 
 endif
 
-ifdef CONFIG_INET
+ifdef CONFIG_NET
 
 inet.o:		$(OBJS)
 		$(LD) -r -o inet.o $(OBJS)
diff -u --recursive --new-file v1.1.12/linux/net/inet/README linux/net/inet/README
--- v1.1.12/linux/net/inet/README	Tue Apr 19 10:53:50 1994
+++ linux/net/inet/README	Mon May 23 08:17:45 1994
@@ -1,92 +1,37 @@
+This is snapshot 014
 
-This is snapshot 010
+This fixes the following from the 1.1.12 release (see the relevant files
+for the credits to authors).
 
-Notes:
-ARP
-	As of snapshot 006, ARP should compile and work correctly
-	for any protocol that has the right build_header support.
-
-AX25	
-	This is an ALPHA release. It will not be a standard part
-	of the real release module. Please read the copyrights on
-	the AX.25 code carefully. When AX.25 is finished it will
-	be part of a seperatly available amateur radio add on. Also
-	please rememeber this is ALPHA code. It works well for a lot
-	of people but I know for a fact it is currently buggy.
-
-IPX
-	The IPX module in here is fairly complete, and certainly
-	usable for things. The IPX user code isn't yet very useful
-	(nobody has written a RIP/SAP daemon!).
-
-NetROM
-	I'm slowly doing bits of this code, but its not even fit
-	to include here.
-
-
-Status:
-
-Done:
-	Replaced ARP with Florian la Roche's ARP.
-	Replaced/improved sk_buff handlers (again from Florian)
-	Removed surplus DDI code.
-	Reformatted most modules.
-	Fixed ICMP handling bugs (ICMP error to ICMP error).
-	Fixed fragmentation bugs (both memory and mtu).
-	Moved some includes.
-	Drivers now build correctly with no IP layer.
-	Merged Linus 1.0.1 diffs and my patches 1-3.
-	Further fixups on clean driver build.
-	Loopback driver now lives where it belongs.
-	UDP verified against specification (passes).
-	IP verified against specification (two errors: Incorrect forwarding and
-		no mandatory option handling).
-	ARP verified against specification (passes: recommendation that ARP
-		rejects MAC broadcast/multicast addresses - this needs
-		driver changes doing).
-	All surplus skb->sk assignment and skb->mem_len skb->mem_addr removed.
-	eth.h became linux/etherdevice.h.
-	alloc_skb nows adds the sizeof(struct sk_buff) itself.
-	Now relative to Linux 1.0.4.
-	All IP wakeups are now callbacks.
-	IPX and AX.25 callbacks now use wake_up_interruptible correctly.
-	ICMP,IP and UDP collect snmp statistics.
-	Removed the 4K limit from the /proc/net/* files.
-	Routing bugs.
-	Cleaned up skb duplication.
-	IPX /proc from Mark Evans.
-	Driver packet ordering now enforced.
-	AX.25 unused SSID bits now set.
-
-In Progress:
-	Module by module validation against specifications.
-	TCP delayed ACK	 [RFC1122 requires this].
-	Byte-order fixes.
-	Core code restructure to enable a working non IP build
-	Trying to fix /proc to do >4K correctly, as well as dynamic addition
- 		of /proc/ and /proc/net/ objects (for module protocol layers).
-	Adding the extra NET2E driver ioctl() support.
-	SNMP MIB statistic capture - finish TCP and add device layer when 
-		Donald is ready.
-	Donald Beckers latest driver mods.
-	Adding the ICMP_TIMESTAMP support patch.
-	Routing bugs.
-	Packet level time stamping.
-	Merging in support for the I^2IT 'TICK' time synchronisation chip.
-	Crynwyr compliant PLIP driver.
-
-To Do:
-	Merge in sk_buff data handling module.
-	Socket family/protocol seperation.
-	Additional BSD options (SO_LOWAT etc).
-	IP option handling, especially on ip forwards.
-	AX.25 /proc support.
-	SNMP /proc support.
-	TCP MSS/Window and route metrics in the routing table.
-	TCP mtu discovery support.
-	NetROM.
-	TCP closing side state machine bug fixes.
-	NetBEUI (Lan Manager) [ie IEE802.3/IEE802.2/NetBIOS].
-	Make drivers record type and addressing category(Multicast/Broadcast..)
-	Speed it up.
-	Unix domain cleanup/rewrite.
+o	RARP compiles in properly
+o	Using new Tytso TTY drivers
+o	IP forwarding is configurable
+o	PC/TCP support
+o	AX.25 builds fine without SLIP driver (just PI)
+o	MTU recognized in routing table (but only by TCP currently)
+o	AX.25 PI driver merged into AX.25 code and kernel stubs
+o	UNIX /proc trap hopefully fixed
+o	DDI removed totally ready to use the PCMICA people's stuff
+o	Unix domain lock/unlock now static (needlessly visible before)
+o	Split net/inet/sock.c into generic and IP components
+o	NFS client works correctly with 8K NFS
+o	Non IP builds work correctly
+o	Renamed inet_bh etc to net_bh to reflect true nature
+o	TCP handling of poor routes much improved
+o	TCP connect timing fixed
+o	Incredibly unlikely SLIP memory leak removed
+o	Loopback maintains IFF_LOOPBACK flag
+o	Johnathon Naylor(G4KLX) AX.25 changes
+o	Out of sync bug in lance driver fixed.
+o	First cut at ethernet loadable modules
+o	PCMICA people have ifmap stuff. Will extend this to other drivers.
+o	New de600.c
+o	Clean up of IP layer - sorted a lot of redundant and duplicated code
+	out.
+o	Removed all the old non working debugging junk from the kernel.
+o	Removed all the devices the kernel used to use as a legacy from
+	FvK's days writing the linux networking.
+o	Greg Page's latest and greatest IPX fixes (including 802.2). Now
+	I've got the stuff to write a free Netware client too and some
+	volunteers to do it.
+o	Fixed the 1.1.12 ARP fragment bug.
diff -u --recursive --new-file v1.1.12/linux/net/inet/af_inet.c linux/net/inet/af_inet.c
--- v1.1.12/linux/net/inet/af_inet.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/af_inet.c	Mon May 23 08:17:46 1994
@@ -0,0 +1,1391 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		AF_INET protocol family socket handler.
+ *
+ * Version:	@(#)af_inet.c	(from sock.c) 1.0.17	06/02/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Florian La Roche, <flla@stud.uni-sb.de>
+ *		Alan Cox, <A.Cox@swansea.ac.uk>
+ *
+ *		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/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "arp.h"
+#include "rarp.h"
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "raw.h"
+#include "icmp.h"
+
+#define min(a,b)	((a)<(b)?(a):(b))
+
+extern struct proto packet_prot;
+
+
+/*
+ *	See if a socket number is in use.
+ */
+ 
+static int sk_inuse(struct proto *prot, int num)
+{
+	struct sock *sk;
+
+	for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
+		sk != NULL;  sk=sk->next) 
+	{
+		if (sk->num == num) 
+			return(1);
+	}
+	return(0);
+}
+
+
+/*
+ *	Pick a new socket number
+ */
+
+unsigned short get_new_socknum(struct proto *prot, unsigned short base)
+{
+	static int start=0;
+
+	/*
+	 * Used to cycle through the port numbers so the
+	 * chances of a confused connection drop.
+	 */
+	 
+	int i, j;
+	int best = 0;
+	int size = 32767; /* a big num. */
+	struct sock *sk;
+
+	if (base == 0) 
+		base = PROT_SOCK+1+(start % 1024);
+	if (base <= PROT_SOCK) 
+	{
+		base += PROT_SOCK+(start % 1024);
+	}
+
+	/* Now look through the entire array and try to find an empty ptr. */
+	for(i=0; i < SOCK_ARRAY_SIZE; i++) 
+	{
+		j = 0;
+		sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
+		while(sk != NULL) 
+		{
+			sk = sk->next;
+			j++;
+		}
+		if (j == 0) 
+		{
+			start =(i+1+start )%1024;
+			return(i+base+1);
+		}
+		if (j < size) 
+		{
+			best = i;
+			size = j;
+		}
+	}
+
+	/* Now make sure the one we want is not in use. */
+
+	while(sk_inuse(prot, base +best+1)) 
+	{
+		best += SOCK_ARRAY_SIZE;
+	}
+	return(best+base+1);
+}
+
+/*
+ *	Add a socket into the socket tables by number.
+ */
+
+void put_sock(unsigned short num, struct sock *sk)
+{
+	struct sock *sk1;
+	struct sock *sk2;
+	int mask;
+
+	sk->num = num;
+	sk->next = NULL;
+	num = num &(SOCK_ARRAY_SIZE -1);
+
+	/* We can't have an interupt re-enter here. */
+	cli();
+	if (sk->prot->sock_array[num] == NULL) 
+	{
+		sk->prot->sock_array[num] = sk;
+		sti();
+		return;
+	}
+	sti();
+	for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) 
+	{
+		if ((mask & sk->saddr) &&
+		    (mask & sk->saddr) != (mask & 0xffffffff)) 
+		{
+			mask = mask << 8;
+			break;
+		}
+	}
+	cli();
+	sk1 = sk->prot->sock_array[num];
+	for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) 
+	{
+		if (!(sk2->saddr & mask)) 
+		{
+			if (sk2 == sk1) 
+			{
+				sk->next = sk->prot->sock_array[num];
+				sk->prot->sock_array[num] = sk;
+				sti();
+				return;
+			}
+			sk->next = sk2;
+			sk1->next= sk;
+			sti();
+			return;
+		}
+		sk1 = sk2;
+	}
+
+	/* Goes at the end. */
+	sk->next = NULL;
+	sk1->next = sk;
+	sti();
+}
+
+/*
+ *	Remove a socket from the socket tables.
+ */
+
+static void remove_sock(struct sock *sk1)
+{
+	struct sock *sk2;
+
+	if (!sk1->prot) 
+	{
+		printk("sock.c: remove_sock: sk1->prot == NULL\n");
+		return;
+	}
+
+	/* We can't have this changing out from under us. */
+	cli();
+	sk2 = sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)];
+	if (sk2 == sk1) 
+	{
+		sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)] = sk1->next;
+		sti();
+		return;
+	}
+
+	while(sk2 && sk2->next != sk1) 
+	{
+		sk2 = sk2->next;
+	}
+
+	if (sk2) 
+	{
+		sk2->next = sk1->next;
+		sti();
+		return;
+	}
+	sti();
+}
+
+/*
+ *	Destroy an AF_INET socket
+ */
+ 
+void destroy_sock(struct sock *sk)
+{
+	struct sk_buff *skb;
+
+  	sk->inuse = 1;			/* just to be safe. */
+
+  	/* Incase it's sleeping somewhere. */
+  	if (!sk->dead) 
+  		sk->write_space(sk);
+
+  	remove_sock(sk);
+  
+  	/* Now we can no longer get new packets. */
+  	delete_timer(sk);
+
+	while ((skb = tcp_dequeue_partial(sk)) != NULL) {
+		IS_SKB(skb);
+		kfree_skb(skb, FREE_WRITE);
+	}
+
+	/* Cleanup up the write buffer. */
+  	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
+		IS_SKB(skb);
+		kfree_skb(skb, FREE_WRITE);
+  	}
+
+  	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
+	/*
+	 * This will take care of closing sockets that were
+	 * listening and didn't accept everything.
+	 */
+		if (skb->sk != NULL && skb->sk != sk) 
+		{
+			IS_SKB(skb);
+			skb->sk->dead = 1;
+			skb->sk->prot->close(skb->sk, 0);
+		}
+		IS_SKB(skb);
+		kfree_skb(skb, FREE_READ);
+	}
+
+	/* Now we need to clean up the send head. */
+	cli();
+	for(skb = sk->send_head; skb != NULL; )
+	{
+		struct sk_buff *skb2;
+
+		/*
+		 * We need to remove skb from the transmit queue,
+		 * or maybe the arp queue.
+		 */
+		if (skb->next  && skb->prev) {
+/*			printk("destroy_sock: unlinked skb\n");*/
+			IS_SKB(skb);
+			skb_unlink(skb);
+		}
+		skb->dev = NULL;
+		skb2 = skb->link3;
+		kfree_skb(skb, FREE_WRITE);
+		skb = skb2;
+	}
+	sk->send_head = NULL;
+	sti();
+
+  	/* And now the backlog. */
+  	while((skb=skb_dequeue(&sk->back_log))!=NULL) 
+  	{
+		/* this should never happen. */
+/*		printk("cleaning back_log\n");*/
+		kfree_skb(skb, FREE_READ);
+	}
+
+	/* Now if it has a half accepted/ closed socket. */
+	if (sk->pair) 
+	{
+		sk->pair->dead = 1;
+		sk->pair->prot->close(sk->pair, 0);
+		sk->pair = NULL;
+  	}
+
+	/*
+	 * Now if everything is gone we can free the socket
+	 * structure, otherwise we need to keep it around until
+	 * everything is gone.
+	 */
+
+	  if (sk->dead && sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
+	  {
+		kfree_s((void *)sk,sizeof(*sk));
+	  } 
+	  else 
+	  {
+		/* this should never happen. */
+		/* actually it can if an ack has just been sent. */
+		sk->destroy = 1;
+		sk->ack_backlog = 0;
+		sk->inuse = 0;
+		reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
+  	}
+}
+
+/*
+ *	The routines beyond this point handle the behaviour of an AF_INET
+ *	socket object. Mostly it punts to the subprotocols of IP to do
+ *	the work.
+ */
+ 
+static int inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk;
+
+	sk = (struct sock *) sock->data;
+
+	switch(cmd) 
+	{
+		case F_SETOWN:
+			/*
+			 * This is a little restrictive, but it's the only
+			 * way to make sure that you can't send a sigurg to
+			 * another process.
+			 */
+			if (!suser() && current->pgrp != -arg &&
+				current->pid != arg) return(-EPERM);
+			sk->proc = arg;
+			return(0);
+		case F_GETOWN:
+			return(sk->proc);
+		default:
+			return(-EINVAL);
+	}
+}
+
+/*
+ *	Set socket options on an inet socket.
+ */
+ 
+static int inet_setsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int optlen)
+{
+  	struct sock *sk = (struct sock *) sock->data;  
+	if (level == SOL_SOCKET)
+		return sock_setsockopt(sk,level,optname,optval,optlen);
+	if (sk->prot->setsockopt==NULL)
+		return(-EOPNOTSUPP);
+	else
+		return sk->prot->setsockopt(sk,level,optname,optval,optlen);
+}
+
+
+
+static int inet_getsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int *optlen)
+{
+  	struct sock *sk = (struct sock *) sock->data;  	
+  	if (level == SOL_SOCKET) 
+  		return sock_getsockopt(sk,level,optname,optval,optlen);
+  	if(sk->prot->getsockopt==NULL)  	
+  		return(-EOPNOTSUPP);
+  	else
+  		return sk->prot->getsockopt(sk,level,optname,optval,optlen);
+}
+
+
+static int inet_autobind(struct sock *sk)
+{
+	/* We may need to bind the socket. */
+	if (sk->num == 0) 
+	{
+		sk->num = get_new_socknum(sk->prot, 0);
+		if (sk->num == 0) 
+			return(-EAGAIN);
+		put_sock(sk->num, sk);
+		sk->dummy_th.source = ntohs(sk->num);
+	}
+	return 0;
+}
+
+static int inet_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = (struct sock *) sock->data;
+
+	if(inet_autobind(sk)!=0)
+		return -EAGAIN;
+
+	/* We might as well re use these. */ 
+	sk->max_ack_backlog = backlog;
+	if (sk->state != TCP_LISTEN) 
+	{
+		sk->ack_backlog = 0;
+		sk->state = TCP_LISTEN;
+	}
+	return(0);
+}
+
+/*
+ *	Default callbacks for user INET sockets. These just wake up
+ *	the user owning the socket.
+ */
+
+static void def_callback1(struct sock *sk)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk,int len)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+
+/*
+ *	Create an inet socket.
+ *
+ *	FIXME: Gcc would generate much better code if we set the parameters
+ *	up in in-memory structure order. Gcc68K even more so
+ */
+
+static int inet_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	struct proto *prot;
+	int err;
+
+	sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
+	if (sk == NULL) 
+		return(-ENOBUFS);
+	sk->num = 0;
+	sk->reuse = 0;
+	switch(sock->type) 
+	{
+		case SOCK_STREAM:
+		case SOCK_SEQPACKET:
+			if (protocol && protocol != IPPROTO_TCP) 
+			{
+				kfree_s((void *)sk, sizeof(*sk));
+				return(-EPROTONOSUPPORT);
+			}
+			protocol = IPPROTO_TCP;
+			sk->no_check = TCP_NO_CHECK;
+			prot = &tcp_prot;
+			break;
+
+		case SOCK_DGRAM:
+			if (protocol && protocol != IPPROTO_UDP) 
+			{
+				kfree_s((void *)sk, sizeof(*sk));
+				return(-EPROTONOSUPPORT);
+			}
+			protocol = IPPROTO_UDP;
+			sk->no_check = UDP_NO_CHECK;
+			prot=&udp_prot;
+			break;
+      
+		case SOCK_RAW:
+			if (!suser()) 
+			{
+				kfree_s((void *)sk, sizeof(*sk));
+				return(-EPERM);
+			}
+			if (!protocol) 
+			{
+				kfree_s((void *)sk, sizeof(*sk));
+				return(-EPROTONOSUPPORT);
+			}
+			prot = &raw_prot;
+			sk->reuse = 1;
+			sk->no_check = 0;	/*
+						 * Doesn't matter no checksum is
+						 * preformed anyway.
+						 */
+			sk->num = protocol;
+			break;
+
+		case SOCK_PACKET:
+			if (!suser()) 
+			{
+				kfree_s((void *)sk, sizeof(*sk));
+				return(-EPERM);
+			}
+			if (!protocol) 
+			{
+				kfree_s((void *)sk, sizeof(*sk));
+				return(-EPROTONOSUPPORT);
+			}
+			prot = &packet_prot;
+			sk->reuse = 1;
+			sk->no_check = 0;	/* Doesn't matter no checksum is
+						 * preformed anyway.
+						 */
+			sk->num = protocol;
+			break;
+
+		default:
+			kfree_s((void *)sk, sizeof(*sk));
+			return(-ESOCKTNOSUPPORT);
+	}
+	sk->socket = sock;
+#ifdef CONFIG_TCP_NAGLE_OFF
+	sk->nonagle = 1;
+#else    
+	sk->nonagle = 0;
+#endif  
+	sk->type = sock->type;
+	sk->stamp.tv_sec=0;
+	sk->protocol = protocol;
+	sk->wmem_alloc = 0;
+	sk->rmem_alloc = 0;
+	sk->sndbuf = SK_WMEM_MAX;
+	sk->rcvbuf = SK_RMEM_MAX;
+	sk->pair = NULL;
+	sk->opt = NULL;
+	sk->write_seq = 0;
+	sk->acked_seq = 0;
+	sk->copied_seq = 0;
+	sk->fin_seq = 0;
+	sk->urg_seq = 0;
+	sk->urg_data = 0;
+	sk->proc = 0;
+	sk->rtt = 0;				/*TCP_WRITE_TIME << 3;*/
+	sk->rto = TCP_TIMEOUT_INIT;		/*TCP_WRITE_TIME*/
+	sk->mdev = 0;
+	sk->backoff = 0;
+	sk->packets_out = 0;
+	sk->cong_window = 1; /* start with only sending one packet at a time. */
+	sk->cong_count = 0;
+	sk->ssthresh = 0;
+	sk->max_window = 0;
+	sk->urginline = 0;
+	sk->intr = 0;
+	sk->linger = 0;
+	sk->destroy = 0;
+	sk->priority = 1;
+	sk->shutdown = 0;
+	sk->keepopen = 0;
+	sk->zapped = 0;
+	sk->done = 0;
+	sk->ack_backlog = 0;
+	sk->window = 0;
+	sk->bytes_rcv = 0;
+	sk->state = TCP_CLOSE;
+	sk->dead = 0;
+	sk->ack_timed = 0;
+	sk->partial = NULL;
+	sk->user_mss = 0;
+	sk->debug = 0;
+
+	/* this is how many unacked bytes we will accept for this socket.  */
+	sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
+
+	/* how many packets we should send before forcing an ack. 
+	   if this is set to zero it is the same as sk->delay_acks = 0 */
+	sk->max_ack_backlog = 0;
+	sk->inuse = 0;
+	sk->delay_acks = 0;
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->receive_queue);
+	sk->mtu = 576;
+	sk->prot = prot;
+	sk->sleep = sock->wait;
+	sk->daddr = 0;
+	sk->saddr = ip_my_addr();
+	sk->err = 0;
+	sk->next = NULL;
+	sk->pair = NULL;
+	sk->send_tail = NULL;
+	sk->send_head = NULL;
+	sk->timeout = 0;
+	sk->broadcast = 0;
+	sk->localroute = 0;
+	sk->timer.data = (unsigned long)sk;
+	sk->timer.function = &net_timer;
+	skb_queue_head_init(&sk->back_log);
+	sk->blog = 0;
+	sock->data =(void *) sk;
+	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
+	sk->dummy_th.res1=0;
+	sk->dummy_th.res2=0;
+	sk->dummy_th.urg_ptr = 0;
+	sk->dummy_th.fin = 0;
+	sk->dummy_th.syn = 0;
+	sk->dummy_th.rst = 0;
+	sk->dummy_th.psh = 0;
+	sk->dummy_th.ack = 0;
+	sk->dummy_th.urg = 0;
+	sk->dummy_th.dest = 0;
+	sk->ip_tos=0;
+	sk->ip_ttl=64;
+  	
+	sk->state_change = def_callback1;
+	sk->data_ready = def_callback2;
+	sk->write_space = def_callback1;
+	sk->error_report = def_callback1;
+
+	if (sk->num) 
+	{
+	/*
+	 * It assumes that any protocol which allows
+	 * the user to assign a number at socket
+	 * creation time automatically
+	 * shares.
+	 */
+		put_sock(sk->num, sk);
+		sk->dummy_th.source = ntohs(sk->num);
+	}
+
+	if (sk->prot->init) 
+	{
+		err = sk->prot->init(sk);
+		if (err != 0) 
+		{
+			destroy_sock(sk);
+			return(err);
+		}
+	}
+	return(0);
+}
+
+
+/*
+ *	Duplicate a socket.
+ */
+ 
+static int inet_dup(struct socket *newsock, struct socket *oldsock)
+{
+	return(inet_create(newsock,((struct sock *)(oldsock->data))->protocol));
+}
+
+
+/*
+ *	The peer socket should always be NULL (or else). When we call this
+ *	function we are destroying the object and from then on nobody
+ *	should refer to it.
+ */
+ 
+static int inet_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = (struct sock *) sock->data;
+	if (sk == NULL) 
+		return(0);
+
+	sk->state_change(sk);
+
+	/* Start closing the connection.  This may take a while. */
+
+	/*
+	 * If linger is set, we don't return until the close
+	 * is complete.  Other wise we return immediately. The
+	 * actually closing is done the same either way.
+	 */
+
+	if (sk->linger == 0) 
+	{
+		sk->prot->close(sk,0);
+		sk->dead = 1;
+	} 
+	else 
+	{
+		sk->prot->close(sk, 0);
+		cli();
+		if (sk->lingertime)
+			current->timeout = jiffies + HZ*sk->lingertime;
+		while(sk->state != TCP_CLOSE && current->timeout>0) 
+		{
+			interruptible_sleep_on(sk->sleep);
+			if (current->signal & ~current->blocked) 
+			{
+				break;
+#if 0
+				/* not working now - closes can't be restarted */
+				sti();
+				current->timeout=0;
+				return(-ERESTARTSYS);
+#endif
+			}
+		}
+		current->timeout=0;
+		sti();
+		sk->dead = 1;
+	}
+	sk->inuse = 1;
+
+	/* This will destroy it. */
+	release_sock(sk);
+	sock->data = NULL;
+	return(0);
+}
+
+
+/* this needs to be changed to dissallow
+   the rebinding of sockets.   What error
+   should it return? */
+
+static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
+	       int addr_len)
+{
+	struct sockaddr_in addr;
+	struct sock *sk=(struct sock *)sock->data, *sk2;
+	unsigned short snum;
+	int err;
+	int chk_addr_ret;
+
+	/* check this error. */
+	if (sk->state != TCP_CLOSE)
+		return(-EIO);
+	if (sk->num != 0) 
+		return(-EINVAL);
+
+	err=verify_area(VERIFY_READ, uaddr, addr_len);
+	if(err)
+  		return err;
+	memcpy_fromfs(&addr, uaddr, min(sizeof(addr), addr_len));
+
+	snum = ntohs(addr.sin_port);
+
+	/*
+	 * We can't just leave the socket bound wherever it is, it might
+	 * be bound to a privileged port. However, since there seems to
+	 * be a bug here, we will leave it if the port is not privileged.
+	 */
+	if (snum == 0) 
+	{
+		snum = get_new_socknum(sk->prot, 0);
+	}
+	if (snum < PROT_SOCK && !suser()) 
+		return(-EACCES);
+
+	chk_addr_ret = ip_chk_addr(addr.sin_addr.s_addr);
+	if (addr.sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR)
+		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
+  	
+	if (chk_addr_ret || addr.sin_addr.s_addr == 0)
+		sk->saddr = addr.sin_addr.s_addr;
+
+	/* Make sure we are allowed to bind here. */
+	cli();
+outside_loop:
+	for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
+					sk2 != NULL; sk2 = sk2->next) 
+	{
+/* should be below! */
+		if (sk2->num != snum) continue;
+		if (sk2->dead) 
+		{
+			destroy_sock(sk2);
+			goto outside_loop;
+		}
+		if (!sk->reuse) 
+		{
+			sti();
+			return(-EADDRINUSE);
+		}
+		
+		if (sk2->num != snum) 
+			continue;		/* more than one */
+		if (sk2->saddr != sk->saddr) 
+			continue;	/* socket per slot ! -FB */
+		if (!sk2->reuse) 
+		{
+			sti();
+			return(-EADDRINUSE);
+		}
+	}
+	sti();
+
+	remove_sock(sk);
+	put_sock(snum, sk);
+	sk->dummy_th.source = ntohs(sk->num);
+	sk->daddr = 0;
+	sk->dummy_th.dest = 0;
+	return(0);
+}
+
+/*
+ *	Handle sk->err properly. The cli/sti matter.
+ */
+ 
+static int inet_error(struct sock *sk)
+{
+	unsigned long flags;
+	int err;
+	save_flags(flags);
+	cli();	
+	err=sk->err;
+	sk->err=0;
+	sti();
+	return -err;
+}
+
+/*
+ *	Connect to a remote host. There is regretably still a little
+ *	TCP 'magic' in here.
+ */
+ 
+static int inet_connect(struct socket *sock, struct sockaddr * uaddr,
+		  int addr_len, int flags)
+{
+	struct sock *sk=(struct sock *)sock->data;
+	int err;
+	sock->conn = NULL;
+
+	if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
+	{
+		sock->state = SS_CONNECTED;
+		/* Connection completing after a connect/EINPROGRESS/select/connect */
+		return 0;	/* Rock and roll */
+	}
+
+	if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK))
+		return -EALREADY;	/* Connecting is currently in progress */
+  	
+	if (sock->state != SS_CONNECTING) 
+	{
+		/* We may need to bind the socket. */
+		if(inet_autobind(sk)!=0)
+			return(-EAGAIN);
+		if (sk->prot->connect == NULL) 
+			return(-EOPNOTSUPP);
+		err = sk->prot->connect(sk, (struct sockaddr_in *)uaddr, addr_len);
+		if (err < 0) 
+			return(err);
+  		sock->state = SS_CONNECTING;
+	}
+
+	if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK)) 
+	  	return(-EINPROGRESS);
+
+	cli(); /* avoid the race condition */
+	while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
+	{
+		interruptible_sleep_on(sk->sleep);
+		if (current->signal & ~current->blocked) 
+		{
+			sti();
+			return(-ERESTARTSYS);
+		}
+		/* This fixes a nasty in the tcp/ip code. There is a hideous hassle with
+		   icmp error packets wanting to close a tcp or udp socket. */
+		if(sk->err && sk->protocol == IPPROTO_TCP)
+		{
+			sti();
+			sock->state = SS_UNCONNECTED;
+			err = -sk->err;
+			sk->err=0;
+			return err; /* set by tcp_err() */
+		}
+	}
+	sti();
+	sock->state = SS_CONNECTED;
+
+	if (sk->state != TCP_ESTABLISHED && sk->err) 
+	{
+		sock->state = SS_UNCONNECTED;
+		err=sk->err;
+		sk->err=0;
+		return(-err);
+	}
+	return(0);
+}
+
+
+static int inet_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	 return(-EOPNOTSUPP);
+}
+
+
+/*
+ *	FIXME: Get BSD behaviour
+ */
+
+static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk1, *sk2;
+	int err;
+
+	sk1 = (struct sock *) sock->data;
+
+	/*
+	 * We've been passed an extra socket.
+	 * We need to free it up because the tcp module creates
+	 * it's own when it accepts one.
+	 */
+	if (newsock->data)
+	{
+	  	struct sock *sk=(struct sock *)newsock->data;
+	  	newsock->data=NULL;
+	  	sk->dead = 1;
+	  	destroy_sock(sk);
+	}
+  
+	if (sk1->prot->accept == NULL) 
+		return(-EOPNOTSUPP);
+
+	/* Restore the state if we have been interrupted, and then returned. */
+	if (sk1->pair != NULL ) 
+	{
+		sk2 = sk1->pair;
+		sk1->pair = NULL;
+	} 
+	else
+	{
+		sk2 = sk1->prot->accept(sk1,flags);
+		if (sk2 == NULL) 
+		{
+			if (sk1->err <= 0)
+				printk("Warning sock.c:sk1->err <= 0.  Returning non-error.\n");
+			err=sk1->err;
+			sk1->err=0;
+			return(-err);
+		}
+	}
+	newsock->data = (void *)sk2;
+	sk2->sleep = newsock->wait;
+	newsock->conn = NULL;
+	if (flags & O_NONBLOCK) 
+		return(0);
+
+	cli(); /* avoid the race. */
+	while(sk2->state == TCP_SYN_RECV) 
+	{
+		interruptible_sleep_on(sk2->sleep);
+		if (current->signal & ~current->blocked) 
+		{
+			sti();
+			sk1->pair = sk2;
+			sk2->sleep = NULL;
+			newsock->data = NULL;
+			return(-ERESTARTSYS);
+		}
+	}
+	sti();
+
+	if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
+	{
+		err = -sk2->err;
+		sk2->err=0;
+		destroy_sock(sk2);
+		newsock->data = NULL;
+		return(err);
+	}
+	newsock->state = SS_CONNECTED;
+	return(0);
+}
+
+
+/*
+ *	This does both peername and sockname.
+ */
+ 
+static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
+		 int *uaddr_len, int peer)
+{
+	struct sockaddr_in sin;
+	struct sock *sk;
+	int len;
+	int err;
+  
+  
+	err = verify_area(VERIFY_WRITE,uaddr_len,sizeof(long));
+	if(err)
+		return err;
+  	
+	len=get_fs_long(uaddr_len);
+  
+	err = verify_area(VERIFY_WRITE, uaddr, len);
+	if(err)
+		return err;
+  	
+	/* Check this error. */
+	if (len < sizeof(sin)) 
+		return(-EINVAL);
+
+	sin.sin_family = AF_INET;
+	sk = (struct sock *) sock->data;
+	if (peer) 
+	{
+		if (!tcp_connected(sk->state)) 
+			return(-ENOTCONN);
+		sin.sin_port = sk->dummy_th.dest;
+		sin.sin_addr.s_addr = sk->daddr;
+	} 
+	else 
+	{
+		sin.sin_port = sk->dummy_th.source;
+		if (sk->saddr == 0) 
+			sin.sin_addr.s_addr = ip_my_addr();
+		else 
+			sin.sin_addr.s_addr = sk->saddr;
+	}
+	len = sizeof(sin);
+	memcpy_tofs(uaddr, &sin, sizeof(sin));
+	put_fs_long(len, uaddr_len);
+	return(0);
+}
+
+
+/*
+ *	The assorted BSD I/O operations
+ */
+
+
+static int inet_recv(struct socket *sock, void *ubuf, int size, int noblock,
+	  unsigned flags)
+{
+	struct sock *sk = (struct sock *) sock->data;
+	int err;
+	
+	if(sk->err)
+		return inet_error(sk);
+	if(size<0)
+		return -EINVAL;
+	if(size==0)
+		return 0;
+	err=verify_area(VERIFY_WRITE,ubuf,size);
+	if(err)
+		return err;
+
+	/* We may need to bind the socket. */
+	if(inet_autobind(sk))
+		return(-EAGAIN);	
+	return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock, flags));
+}
+
+
+static int inet_read(struct socket *sock, char *ubuf, int size, int noblock)
+{
+	return inet_recv(sock,ubuf,size,noblock,0);
+}
+
+static int inet_send(struct socket *sock, void *ubuf, int size, int noblock, 
+	       unsigned flags)
+{
+	struct sock *sk = (struct sock *) sock->data;
+	int err;
+	if (sk->shutdown & SEND_SHUTDOWN) 
+	{
+		send_sig(SIGPIPE, current, 1);
+		return(-EPIPE);
+	}
+	if(sk->err)
+		return inet_error(sk);
+	if(size<0)
+		return -EINVAL;
+	if(size==0)
+		return 0;
+	err=verify_area(VERIFY_READ,ubuf,size);
+	if(err)
+		return err;
+	/* We may need to bind the socket. */
+	if(inet_autobind(sk)!=0)
+		return(-EAGAIN);
+	return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));
+}
+
+static int inet_write(struct socket *sock, char *ubuf, int size, int noblock)
+{
+	return inet_send(sock,ubuf,size,noblock,0);
+}
+
+static int inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, 
+	    unsigned flags, struct sockaddr *sin, int addr_len)
+{
+	int err;
+	struct sock *sk = (struct sock *) sock->data;
+	if (sk->shutdown & SEND_SHUTDOWN) 
+	{
+		send_sig(SIGPIPE, current, 1);
+		return(-EPIPE);
+	}
+	if (sk->prot->sendto == NULL) 
+		return(-EOPNOTSUPP);
+	if(sk->err)
+		return inet_error(sk);
+	if(size<0)
+		return -EINVAL;
+	if(size==0)
+		return 0;
+	err=verify_area(VERIFY_READ,ubuf,size);
+	if(err)
+		return err;
+
+	/* We may need to bind the socket. */
+	
+	if(inet_autobind(sk)!=0)
+		return -EAGAIN;
+	return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, 
+			   (struct sockaddr_in *)sin, addr_len));
+}
+
+
+static int inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, 
+		   unsigned flags, struct sockaddr *sin, int *addr_len )
+{
+	struct sock *sk = (struct sock *) sock->data;
+	int err;
+	
+	if (sk->prot->recvfrom == NULL) 
+		return(-EOPNOTSUPP);
+	if(sk->err)
+		return inet_error(sk);
+	if(size<0)
+		return -EINVAL;
+	if(size==0)
+		return 0;
+	err=verify_area(VERIFY_READ,ubuf,size);
+	if(err)
+		return err;
+
+	/* We may need to bind the socket. */
+	if(inet_autobind(sk)!=0)
+		return(-EAGAIN);
+	return(sk->prot->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags,
+			     (struct sockaddr_in*)sin, addr_len));
+}
+
+
+static int inet_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk=(struct sock*)sock->data;
+
+	/*
+	 * This should really check to make sure
+	 * the socket is a TCP socket. (WHY AC...)
+	 */
+	how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
+		       1->2 bit 2 snds.
+		       2->3 */
+	if ((how & ~SHUTDOWN_MASK) || how==0)	/* MAXINT->0 */
+		return(-EINVAL);
+	if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
+		sock->state = SS_CONNECTED;
+	if (!tcp_connected(sk->state)) 
+		return(-ENOTCONN);
+	sk->shutdown |= how;
+	if (sk->prot->shutdown)
+		sk->prot->shutdown(sk, how);
+	return(0);
+}
+
+
+static int inet_select(struct socket *sock, int sel_type, select_table *wait )
+{
+	struct sock *sk=(struct sock *) sock->data;
+	if (sk->prot->select == NULL) 
+	{
+		return(0);
+	}
+	return(sk->prot->select(sk, sel_type, wait));
+}
+
+/*
+ *	ioctl() calls you can issue on an INET socket. Most of these are
+ *	device configuration and stuff and very rarely used. Some ioctls
+ *	pass on to the socket itself.
+ *
+ *	NOTE: I like the idea of a module for the config stuff. ie ifconfig
+ *	loads the devconfigure module does its configuring and unloads it.
+ *	Theres a good 20K of config code hanging around the kernel.
+ */
+
+static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk=(struct sock *)sock->data;
+	int err;
+
+	switch(cmd) 
+	{
+		case FIOSETOWN:
+		case SIOCSPGRP:
+			err=verify_area(VERIFY_READ,(int *)arg,sizeof(long));
+			if(err)
+				return err;
+			sk->proc = get_fs_long((int *) arg);
+			return(0);
+		case FIOGETOWN:
+		case SIOCGPGRP:
+			err=verify_area(VERIFY_WRITE,(void *) arg, sizeof(long));
+			if(err)
+				return err;
+			put_fs_long(sk->proc,(int *)arg);
+			return(0);			
+		case SIOCGSTAMP:
+			if(sk->stamp.tv_sec==0)
+				return -ENOENT;
+			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval));
+			if(err)
+				return err;
+			memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval));
+			return 0;
+		case SIOCADDRT: case SIOCADDRTOLD:
+		case SIOCDELRT: case SIOCDELRTOLD:
+			return(ip_rt_ioctl(cmd,(void *) arg));
+		case SIOCDARP:
+		case SIOCGARP:
+		case SIOCSARP:
+			return(arp_ioctl(cmd,(void *) arg));
+#ifdef CONFIG_INET_RARP			
+		case SIOCDRARP:
+		case SIOCGRARP:
+		case SIOCSRARP:
+			return(rarp_ioctl(cmd,(void *) arg));
+#endif
+		case SIOCGIFCONF:
+		case SIOCGIFFLAGS:
+		case SIOCSIFFLAGS:
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFMETRIC:
+		case SIOCSIFMETRIC:
+		case SIOCGIFMEM:
+		case SIOCSIFMEM:
+		case SIOCGIFMTU:
+		case SIOCSIFMTU:
+		case SIOCSIFLINK:
+		case SIOCGIFHWADDR:
+		case SIOCSIFHWADDR:
+		case OLD_SIOCGIFHWADDR:
+		case SIOCSIFMAP:
+		case SIOCGIFMAP:
+		case SIOCDEVPRIVATE:
+			return(dev_ioctl(cmd,(void *) arg));
+
+		default:
+			if (sk->prot->ioctl==NULL) 
+				return(-EINVAL);
+			return(sk->prot->ioctl(sk, cmd, arg));
+	}
+	/*NOTREACHED*/
+	return(0);
+}
+
+/*
+ * This routine must find a socket given a TCP or UDP header.
+ * Everyhting is assumed to be in net order.
+ */
+
+struct sock *get_sock(struct proto *prot, unsigned short num,
+				unsigned long raddr,
+				unsigned short rnum, unsigned long laddr)
+{
+	struct sock *s;
+	unsigned short hnum;
+
+	hnum = ntohs(num);
+
+	/*
+	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
+	 * than a prime unless 3 or more sockets end up using the same
+	 * array entry.  This should not be a problem because most
+	 * well known sockets don't overlap that much, and for
+	 * the other ones, we can just be careful about picking our
+	 * socket number when we choose an arbitrary one.
+	 */
+
+	for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
+			s != NULL; s = s->next) 
+	{
+		if (s->num != hnum) 
+			continue;
+		if(s->dead && (s->state == TCP_CLOSE))
+			continue;
+		if(prot == &udp_prot)
+			return s;
+		if(ip_addr_match(s->daddr,raddr)==0)
+			continue;
+		if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) 
+			continue;
+		if(ip_addr_match(s->saddr,laddr) == 0)
+			continue;
+		return(s);
+  	}
+  	return(NULL);
+}
+
+static struct proto_ops inet_proto_ops = {
+	AF_INET,
+
+	inet_create,
+	inet_dup,
+	inet_release,
+	inet_bind,
+	inet_connect,
+	inet_socketpair,
+	inet_accept,
+	inet_getname, 
+	inet_read,
+	inet_write,
+	inet_select,
+	inet_ioctl,
+	inet_listen,
+	inet_send,
+	inet_recv,
+	inet_sendto,
+	inet_recvfrom,
+	inet_shutdown,
+	inet_setsockopt,
+	inet_getsockopt,
+	inet_fcntl,
+};
+
+extern unsigned long seq_offset;
+
+/*
+ *	Called by socket.c on kernel startup.  
+ */
+ 
+void inet_proto_init(struct net_proto *pro)
+{
+	struct inet_protocol *p;
+	int i;
+
+	printk("Swansea University Computer Society NET3.014\n");
+
+	/*
+	 *	Tell SOCKET that we are alive... 
+	 */
+   
+  	(void) sock_register(inet_proto_ops.family, &inet_proto_ops);
+
+  	seq_offset = CURRENT_TIME*250;
+
+	/*
+	 *	Add all the protocols. 
+	 */
+	 
+	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
+	{
+		tcp_prot.sock_array[i] = NULL;
+		udp_prot.sock_array[i] = NULL;
+		raw_prot.sock_array[i] = NULL;
+  	}
+
+	printk("IP Protocols: ");
+	for(p = inet_protocol_base; p != NULL;) 
+	{
+		struct inet_protocol *tmp = (struct inet_protocol *) p->next;
+		inet_add_protocol(p);
+		printk("%s%s",p->name,tmp?", ":"\n");
+		p = tmp;
+	}
+	/*
+	 *	Set the ARP module up
+	 */
+	arp_init();
+  	/*
+  	 *	Set the IP module up
+  	 */
+	ip_init();
+}
+
diff -u --recursive --new-file v1.1.12/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.12/linux/net/inet/arp.c	Mon May 23 12:14:26 1994
+++ linux/net/inet/arp.c	Mon May 23 08:17:46 1994
@@ -722,7 +722,17 @@
 {
 	struct arp_table *entry;
 	unsigned long hash;
-
+/* SHOULD BE FIXED NOW */	
+	if(paddr==0)
+	{
+		printk("ADDRESS BOTCH 0\n");
+		if(skb)
+		{
+			printk("skb(saddr=%lx, daddr=%lx, raddr=%lx)\n",
+				skb->saddr,skb->daddr,skb->raddr);
+		}
+	}	
+/* ------------- */
 	switch (ip_chk_addr(paddr))
 	{
 		case IS_MYADDR:
@@ -1075,8 +1085,6 @@
 
 	switch(cmd)
 	{
-		case DDIOCSDBG:
-			return dbg_ioctl(arg, DBG_ARP);
 		case SIOCDARP:
 			if (!suser())
 				return -EPERM;
diff -u --recursive --new-file v1.1.12/linux/net/inet/datalink.h linux/net/inet/datalink.h
--- v1.1.12/linux/net/inet/datalink.h	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/datalink.h	Mon May 23 08:17:46 1994
@@ -0,0 +1,17 @@
+#ifndef _NET_INET_DATALINK_H_
+#define _NET_INET_DATALINK_H_
+
+struct datalink_proto {
+	unsigned short	type_len;
+	unsigned char	type[8];
+	unsigned short	datalink_type;
+	unsigned short	header_length;
+	int	(*rcvfunc)(struct sk_buff *, struct device *, 
+				struct packet_type *);
+	void	(*datalink_header)(struct datalink_proto *, struct sk_buff *,
+					unsigned char *);
+	struct datalink_proto	*next;
+};
+
+#endif
+
diff -u --recursive --new-file v1.1.12/linux/net/inet/dev.c linux/net/inet/dev.c
--- v1.1.12/linux/net/inet/dev.c	Tue Apr 19 22:20:35 1994
+++ linux/net/inet/dev.c	Mon May 23 08:17:47 1994
@@ -14,6 +14,7 @@
  *	Additional Authors:
  *		Florian la Roche <rzsfl@rz.uni-sb.de>
  *		Alan Cox <gw4pts@gw4pts.ampr.org>
+ *		David Hinds <dhinds@allegro.stanford.edu>
  *
  *	Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have
  *	the rest as well commented in the end.
@@ -136,7 +137,7 @@
 	}
   
   /*
-   *	NIT taps must go at the end or inet_bh will leak!
+   *	NIT taps must go at the end or net_bh will leak!
    */
    
 	if (pt->type == htons(ETH_P_ALL))
@@ -289,7 +290,9 @@
 		/*
 		 *	Delete the route to the device.
 		 */
+#ifdef CONFIG_INET		 
 		ip_rt_flush(dev);
+#endif		
 		/*
 		 *	Blank the IP addresses
 		 */
@@ -324,14 +327,19 @@
 				/* at the front or the back of the	*/
 				/* queue.				*/
 
-	DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
-							skb, dev, pri));
-
 	if (dev == NULL) 
 	{
 		printk("dev.c: dev_queue_xmit: dev = NULL\n");
 		return;
 	}
+#ifdef CONFIG_SLAVE_BALANCING
+	save_flags(flags);
+	cli();
+	if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue &&
+				(dev->slave->flags & IFF_UP))
+		dev=dev->slave;
+	restore_flags(flags);
+#endif		
  
 	IS_SKB(skb);
     
@@ -370,8 +378,8 @@
 	}
 
 	/*
-	 *	If the address has not been resolved called the device header rebuilder.
-	 *	This can cover all protocols and technically no just ARP either.
+	 *	If the address has not been resolved. Call the device header rebuilder.
+	 *	This can cover all protocols and technically not just ARP either.
 	 */
 	 
 	if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
@@ -381,12 +389,21 @@
 	save_flags(flags);
 	cli();	
 	if (!where) {
+#ifdef CONFIG_SLAVE_BALANCING	
+		skb->in_dev_queue=1;
+#endif		
 		skb_queue_tail(dev->buffs + pri,skb);
 		skb = skb_dequeue(dev->buffs + pri);
+#ifdef CONFIG_SLAVE_BALANCING		
+		skb->in_dev_queue=0;
+#endif		
 	}
 	restore_flags(flags);
 
 	if (dev->hard_start_xmit(skb, dev) == 0) {
+#ifdef CONFIG_SLAVE_BALANCING	
+		dev->pkt_queue--;
+#endif
 		return;
 	}
 
@@ -394,6 +411,10 @@
 	 *	Transmission failed, put skb back into a list. 
 	 */
 	cli();
+#ifdef CONFIG_SLAVE_BALANCING
+	skb->in_dev_queue=1;
+	dev->pkt_queue++;
+#endif		
 	skb_queue_head(dev->buffs + pri,skb);
 	restore_flags(flags);
 }
@@ -447,7 +468,7 @@
 	 *	hardware interrupt returns.
 	 */
 
-	mark_bh(INET_BH);
+	mark_bh(NET_BH);
 	return;
 }
 
@@ -573,7 +594,7 @@
  
 volatile char in_bh = 0;	/* Non-rentrant remember */
 
-int in_inet_bh()	/* Used by timer.c */
+int in_net_bh()	/* Used by timer.c */
 {
 	return(in_bh==0?0:1);
 }
@@ -583,10 +604,10 @@
  *	on and hardware can interrupt and queue to the receive queue a we
  *	run with no problems.
  *	This is run as a bottom half after an interrupt handler that does
- *	mark_bh(INET_BH);
+ *	mark_bh(NET_BH);
  */
  
-void inet_bh(void *tmp)
+void net_bh(void *tmp)
 {
 	struct sk_buff *skb;
 	struct packet_type *ptype;
@@ -724,7 +745,6 @@
 	 
 		if (!flag) 
 		{
-			DPRINTF((DBG_DEV,"INET: unknown packet type 0x%04X (ignored)\n", type));
 			kfree_skb(skb, FREE_WRITE);
 		}
 
@@ -995,10 +1015,21 @@
 		case SIOCSIFFLAGS:	/* Set interface flags */
 			{
 				int old_flags = dev->flags;
+#ifdef CONFIG_SLAVE_BALANCING				
+				if(dev->flags&IFF_SLAVE)
+					return -EBUSY;
+#endif					
 				dev->flags = ifr.ifr_flags & (
 					IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
 					IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
-					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER);
+#ifdef CONFIG_SLAVE_BALANCING				
+				if(!(dev->flags&IFF_MASTER) && dev->slave)
+				{
+					dev->slave->flags&=~IFF_SLAVE;
+					dev->slave=NULL;
+				}
+#endif				
 				
 				/*
 				 *	Has promiscuous mode been turned off
@@ -1053,7 +1084,12 @@
 			dev->pa_addr = (*(struct sockaddr_in *)
 				 &ifr.ifr_addr).sin_addr.s_addr;
 			dev->family = ifr.ifr_addr.sa_family;
+			
+#ifdef CONFIG_INET	
+			/* This is naughty. When net-032e comes out It wants moving into the net032
+			   code not the kernel. Till then it can sit here (SIGH) */		
 			dev->pa_mask = ip_get_mask(dev->pa_addr);
+#endif			
 			dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
 			ret = 0;
 			break;
@@ -1179,7 +1215,83 @@
 				return -EINVAL;
 			ret=dev->set_mac_address(dev,ifr.ifr_hwaddr.sa_data);
 			break;
+		
+		case SIOCDEVPRIVATE:
+			if(dev->do_ioctl==NULL)
+				return -EOPNOTSUPP;
+			return dev->do_ioctl(dev, &ifr);
+			
+		case SIOCGIFMAP:
+			ifr.ifr_map.mem_start=dev->mem_start;
+			ifr.ifr_map.mem_end=dev->mem_end;
+			ifr.ifr_map.base_addr=dev->base_addr;
+			ifr.ifr_map.irq=dev->irq;
+			ifr.ifr_map.dma=dev->dma;
+			ifr.ifr_map.port=dev->if_port;
+			memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+			ret=0;
+			break;
 			
+		case SIOCSIFMAP:
+			if(dev->set_config==NULL)
+				return -EOPNOTSUPP;
+			return dev->set_config(dev,&ifr.ifr_map);
+			
+		case SIOCGIFSLAVE:
+#ifdef CONFIG_SLAVE_BALANCING		
+			if(dev->slave==NULL)
+				return -ENOENT;
+			strncpy(ifr.ifr_name,dev->name,sizeof(ifr.ifr_name));
+			memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+			ret=0;
+#else
+			return -ENOENT;
+#endif			
+			break;
+#ifdef CONFIG_SLAVE_BALANCING			
+		case SIOCSIFSLAVE:
+		{
+		
+		/*
+		 *	Fun game. Get the device up and the flags right without
+		 *	letting some scummy user confuse us.
+		 */
+			unsigned long flags;
+			struct device *slave=dev_get(ifr.ifr_slave);
+			save_flags(flags);
+			if(slave==NULL)
+			{
+				return -ENODEV;
+			}
+			cli();
+			if(slave->flags&(IFF_UP|IFF_RUNNING)!=(IFF_UP|IFF_RUNNING))
+			{
+				restore_flags(flags);
+				return -EINVAL;
+			}
+			if(dev->flags&IFF_SLAVE)
+			{
+				restore_flags(flags);
+				return -EINVAL;
+			}
+			if(dev->slave!=NULL)
+			{
+				restore_flags(flags);
+				return -EBUSY;
+			}
+			if(slave->flags&IFF_SLAVE)
+			{
+				restore_flags(flags);
+				return -EBUSY;
+			}
+			dev->slave=slave;
+			slave->flags|=IFF_SLAVE;
+			dev->flags|=IFF_MASTER;
+			restore_flags(flags);
+			ret=0;
+		}
+		break;
+#endif			
 		/*
 		 *	Unknown ioctl
 		 */
@@ -1224,6 +1336,8 @@
 		case SIOCGIFHWADDR:
 		case SIOCSIFHWADDR:
 		case OLD_SIOCGIFHWADDR:
+		case SIOCGIFSLAVE:
+		case SIOCGIFMAP:
 			return dev_ifsioc(arg, cmd);
 
 		/*
@@ -1238,6 +1352,9 @@
 		case SIOCSIFMETRIC:
 		case SIOCSIFMTU:
 		case SIOCSIFMEM:
+		case SIOCSIFMAP:
+		case SIOCSIFSLAVE:
+		case SIOCDEVPRIVATE:
 			if (!suser())
 				return -EPERM;
 			return dev_ifsioc(arg, cmd);
diff -u --recursive --new-file v1.1.12/linux/net/inet/dev.h linux/net/inet/dev.h
--- v1.1.12/linux/net/inet/dev.h	Tue Dec 14 12:12:07 1993
+++ linux/net/inet/dev.h	Mon May 23 08:17:47 1994
@@ -1,191 +0,0 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Definitions for the Interfaces handler.
- *
- * Version:	@(#)dev.h	1.0.10	08/12/93
- *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *		Corey Minyard <wf-rch!minyard@relay.EU.net>
- *		Donald J. Becker, <becker@super.org>
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- */
-#ifndef _DEV_H
-#define _DEV_H
-
-#include <linux/if.h>
-#include <linux/if_ether.h>
-
-
-/* for future expansion when we will have different priorities. */
-#define DEV_NUMBUFFS	3
-#define MAX_ADDR_LEN	7
-#define MAX_HEADER	18
-
-#define IS_MYADDR	1		/* address is (one of) our own	*/
-#define IS_LOOPBACK	2		/* address is for LOOPBACK	*/
-#define IS_BROADCAST	3		/* address is a valid broadcast	*/
-#define IS_INVBCAST	4		/* Wrong netmask bcast not for us */
-
-/*
- * The DEVICE structure.
- * Actually, this whole structure is a big mistake.  It mixes I/O
- * data with strictly "high-level" data, and it has to know about
- * almost every data structure used in the INET module.  We will
- * gradually phase out this structure, and replace it with the
- * more general (but stolen :-) BSD "ifnet" structure. -FvK
- */
-struct device {
-
-  /*
-   * This is the first field of the "visible" part of this structure
-   * (i.e. as seen by users in the "Space.c" file).  It is the name
-   * the interface.
-   */
-  char			  *name;
-
-  /* I/O specific fields.  These will be moved to DDI soon. */
-  unsigned long		  rmem_end;		/* shmem "recv" end	*/
-  unsigned long		  rmem_start;		/* shmem "recv" start	*/
-  unsigned long		  mem_end;		/* sahared mem end	*/
-  unsigned long		  mem_start;		/* shared mem start	*/
-  unsigned short	  base_addr;		/* device I/O address	*/
-  unsigned char		  irq;			/* device IRQ number	*/
-
-  /* Low-level status flags. */
-  volatile unsigned char  start,		/* start an operation	*/
-                          tbusy,		/* transmitter busy	*/
-                          interrupt;		/* interrupt arrived	*/
-
-  /*
-   * Another mistake.
-   * This points to the next device in the "dev" chain. It will
-   * be moved to the "invisible" part of the structure as soon as
-   * it has been cleaned up. -FvK
-   */
-  struct device		  *next;
-
-  /* The device initialization function. Called only once. */
-  int			  (*init)(struct device *dev);
-
-  /* Some hardware also needs these fields, but they are not part of the
-     usual set specified in Space.c. */
-  unsigned char		  if_port;		/* Selectable AUI, TP,..*/
-  unsigned char		  dma;			/* DMA channel		*/
-
-  struct enet_statistics* (*get_stats)(struct device *dev);
-
-  /*
-   * This marks the end of the "visible" part of the structure. All
-   * fields hereafter are internal to the system, and may change at
-   * will (read: may be cleaned up at will).
-   */
-
-  /* These may be needed for future network-power-down code. */
-  unsigned long		  trans_start;	/* Time (in jiffies) of last Tx	*/
-  unsigned long		  last_rx;	/* Time of last Rx		*/
-
-  unsigned short	  flags;	/* interface flags (a la BSD)	*/
-  unsigned short	  family;	/* address family ID (AF_INET)	*/
-  unsigned short	  metric;	/* routing metric (not used)	*/
-  unsigned short	  mtu;		/* interface MTU value		*/
-  unsigned short	  type;		/* interface hardware type	*/
-  unsigned short	  hard_header_len;	/* hardware hdr length	*/
-  void			  *priv;	/* pointer to private data	*/
-
-  /* Interface address info. */
-  unsigned char		  broadcast[MAX_ADDR_LEN];	/* hw bcast add	*/
-  unsigned char		  dev_addr[MAX_ADDR_LEN];	/* hw address	*/
-  unsigned char		  addr_len;	/* harfware address length	*/
-  unsigned long		  pa_addr;	/* protocol address		*/
-  unsigned long		  pa_brdaddr;	/* protocol broadcast addr	*/
-  unsigned long		  pa_dstaddr;	/* protocol P-P other side addr	*/
-  unsigned long		  pa_mask;	/* protocol netmask		*/
-  unsigned short	  pa_alen;	/* protocol address length	*/
-
-  /* Pointer to the interface buffers. */
-  struct sk_buff	  *volatile buffs[DEV_NUMBUFFS];
-
-  /* Pointers to interface service routines. */
-  int			  (*open)(struct device *dev);
-  int			  (*stop)(struct device *dev);
-  int			  (*hard_start_xmit) (struct sk_buff *skb,
-					      struct device *dev);
-  int			  (*hard_header) (unsigned char *buff,
-					  struct device *dev,
-					  unsigned short type,
-					  unsigned long daddr,
-					  unsigned long saddr,
-					  unsigned len);
-  void			  (*add_arp) (unsigned long addr,
-				      struct sk_buff *skb,
-				      struct device *dev);
-  void			  (*queue_xmit)(struct sk_buff *skb,
-					struct device *dev, int pri);
-  int			  (*rebuild_header)(void *eth, struct device *dev);
-  unsigned short	  (*type_trans) (struct sk_buff *skb,
-					 struct device *dev);
-#define HAVE_MULTICAST			 
-  void			  (*set_multicast_list)(struct device *dev,
-  					 int num_addrs, void *addrs);
-#define HAVE_SET_MAC_ADDR  		 
-  int			  (*set_mac_address)(struct device *dev, void *addr);
-};
-
-
-struct packet_type {
-  unsigned short	type;	/* This is really NET16(ether_type) other
-				 * devices will have to translate
-				 * appropriately.
-				 */
-  unsigned short	copy:1;
-  int			(*func) (struct sk_buff *, struct device *,
-				 struct packet_type *);
-  void			*data;
-  struct packet_type	*next;
-};
-
-
-/* Used by dev_rint */
-#define IN_SKBUFF	1
-#define DEV_QUEUE_MAGIC	0x17432895
-
-
-extern struct device	*dev_base;
-extern struct packet_type *ptype_base;
-
-
-extern int		ip_addr_match(unsigned long addr1, unsigned long addr2);
-extern int		chk_addr(unsigned long addr);
-extern struct device	*dev_check(unsigned long daddr);
-extern unsigned long	my_addr(void);
-
-extern void		dev_add_pack(struct packet_type *pt);
-extern void		dev_remove_pack(struct packet_type *pt);
-extern struct device	*dev_get(char *name);
-extern int		dev_open(struct device *dev);
-extern int		dev_close(struct device *dev);
-extern void		dev_queue_xmit(struct sk_buff *skb, struct device *dev,
-				       int pri);
-#define HAVE_NETIF_RX 1
-extern void		netif_rx(struct sk_buff *skb);
-/* The old interface to netif_rx(). */
-extern int		dev_rint(unsigned char *buff, long len, int flags,
-				 struct device * dev);
-extern void		dev_transmit(void);
-extern int		in_inet_bh(void);
-extern void		inet_bh(void *tmp);
-extern void		dev_tint(struct device *dev);
-extern int		dev_get_info(char *buffer);
-extern int		dev_ioctl(unsigned int cmd, void *);
-
-extern void		dev_init(void);
-
-#endif	/* _DEV_H */
diff -u --recursive --new-file v1.1.12/linux/net/inet/devinet.c linux/net/inet/devinet.c
--- v1.1.12/linux/net/inet/devinet.c	Tue Apr 19 10:53:55 1994
+++ linux/net/inet/devinet.c	Mon May 23 08:17:47 1994
@@ -75,8 +75,6 @@
 {
 	int i;
 	unsigned long mask=0xFFFFFFFF;
-	DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
-	DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
 
 	/*
 	 *	Simple case
@@ -196,7 +194,7 @@
  *
  *	Because the loopback address (127.0.0.1) is already recognized
  *	automatically, we can use the loopback interface's address as
- *	our "primary" interface.  This is the addressed used by IP et
+ *	our "primary" interface.  This is the address used by IP et
  *	al when it doesn't know which address to use (i.e. it does not
  *	yet know from or to which interface to go...).
  */
diff -u --recursive --new-file v1.1.12/linux/net/inet/eth.c linux/net/inet/eth.c
--- v1.1.12/linux/net/inet/eth.c	Tue Apr 19 10:53:55 1994
+++ linux/net/inet/eth.c	Mon May 23 08:17:47 1994
@@ -23,6 +23,8 @@
  *		Florian		: Removed many unnecessary functions, code cleanup
  *				  and changes for new arp and skbuff.
  *		Alan Cox	: Redid header building to reflect new format.
+ *		Alan Cox	: ARP only when compiled with CONFIG_INET
+ *		Greg Page	: 802.2 and SNAP stuff
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -147,8 +149,11 @@
 	/*
 	 *	Try and get ARP to resolve the header.
 	 */
-	 
+#ifdef CONFIG_INET	 
 	return arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
+#else
+	return 0;	
+#endif	
 }
 
 
@@ -161,10 +166,17 @@
 unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
 {
 	struct ethhdr *eth = (struct ethhdr *) skb->data;
+	char *rawp;
 
-	if (ntohs(eth->h_proto) < 1536)
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+		
+	rawp = (unsigned char *)(eth + 1);
+	
+	if (*(unsigned short *)rawp == 0xFFFF)
 		return htons(ETH_P_802_3);
-
-	return eth->h_proto;
+	if (*(unsigned short *)rawp == 0xAAAA)
+		return htons(ETH_P_SNAP);
+		
+	return htons(ETH_P_802_2);
 }
-
diff -u --recursive --new-file v1.1.12/linux/net/inet/icmp.c linux/net/inet/icmp.c
--- v1.1.12/linux/net/inet/icmp.c	Sun May  1 12:12:52 1994
+++ linux/net/inet/icmp.c	Mon May 23 08:17:47 1994
@@ -19,12 +19,11 @@
  *		Alan Cox	:	Protocol violations
  *		Alan Cox	:	SNMP Statistics		
  *		Alan Cox	:	Routing errors
+ *		Alan Cox	:	Changes for newer routing code
+ *		Alan Cox	:	Removed old debugging junk
  *
  * 
- *	FIXME:
- *		When 1.0.6 is out merge in the NET channel diffs for TIMESTAMP
  *
- *
  *		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
@@ -82,21 +81,6 @@
 };
 
 
-/* 
- *	Display the contents of an ICMP header. 
- */
- 
-static void print_icmp(struct icmphdr *icmph)
-{
-  	if (inet_debug != DBG_ICMP) 
-  		return;
-
-  	printk("ICMP: type = %d, code = %d, checksum = %X\n",
-			icmph->type, icmph->code, icmph->checksum);
-  	printk("      gateway = %s\n", in_ntoa(icmph->un.gateway));
-}
-
-
 /*
  *	Send an ICMP message in response to a situation
  *
@@ -112,9 +96,6 @@
 	int len;
 	struct device *ndev=NULL;	/* Make this =dev to force replies on the same interface */
 
-	DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
-	   					skb_in, type, code, dev));
-
 	/*
 	 *	Find the original IP header.
 	 */
@@ -223,9 +204,6 @@
 	icmph->checksum = ip_compute_csum((unsigned char *)icmph,
                          sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);
 
-	DPRINTF((DBG_ICMP, ">>\n"));
-	print_icmp(icmph);
-
 	/*
 	 *	Send it and free it once sent.
 	 */
@@ -250,20 +228,14 @@
 	switch(icmph->code & 7) 
 	{
 		case ICMP_NET_UNREACH:
-			DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
-								in_ntoa(iph->daddr)));
 			break;
 		case ICMP_HOST_UNREACH:
-			DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
-							in_ntoa(iph->daddr)));
 			break;
 		case ICMP_PROT_UNREACH:
 			printk("ICMP: %s:%d: protocol unreachable.\n",
 				in_ntoa(iph->daddr), ntohs(iph->protocol));
 			break;
 		case ICMP_PORT_UNREACH:
-			DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
-				in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
 			break;
 		case ICMP_FRAG_NEEDED:
 			printk("ICMP: %s: fragmentation needed and DF set.\n",
@@ -273,8 +245,6 @@
 			printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
 			break;
 		default:
-			DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
-			    		(icmph->code & 7), in_ntoa(iph->daddr)));
 			break;
 	}
 
@@ -338,7 +308,7 @@
 			 */
 #ifdef not_a_good_idea
 			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
-				ip, 0, icmph->un.gateway, dev);
+				ip, 0, icmph->un.gateway, dev,0);
 			break;
 #endif
 		case ICMP_REDIR_HOST:
@@ -354,16 +324,14 @@
 				break;
 			printk("redirect from %08lx\n", source);
 			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
-				ip, 0, icmph->un.gateway, dev);
+				ip, 0, icmph->un.gateway, dev,0);
 			break;
 		case ICMP_REDIR_NETTOS:
 		case ICMP_REDIR_HOSTTOS:
 			printk("ICMP: cannot handle TOS redirects yet!\n");
 			break;
 		default:
-			DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
-						(icmph->code & 7)));
-		break;
+			break;
   	}
   	
   	/*
@@ -620,8 +588,6 @@
 	 
 	if (ip_chk_addr(daddr) == IS_BROADCAST) 
 	{
-		DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
-							in_ntoa(saddr)));
 		icmp_statistics.IcmpInErrors++;
 		kfree_skb(skb1, FREE_READ);
 		return(0);
@@ -646,7 +612,6 @@
 		kfree_skb(skb1, FREE_READ);
 		return(0);
 	}
-	print_icmp(icmph);
 
 	/*
 	 *	Parse the ICMP message 
@@ -708,9 +673,6 @@
 			return(0);
 		default:
 			icmp_statistics.IcmpInErrors++;
-			DPRINTF((DBG_ICMP,
-				"ICMP: Unsupported ICMP from %s, type = 0x%X\n",
-							in_ntoa(saddr), icmph->type));
 			kfree_skb(skb1, FREE_READ);
 			return(0);
  	}
@@ -729,8 +691,6 @@
 {
   	switch(cmd) 
   	{
-		case DDIOCSDBG:
-			return(dbg_ioctl((void *) arg, DBG_ICMP));
 		default:
 			return(-EINVAL);
   	}
diff -u --recursive --new-file v1.1.12/linux/net/inet/inet.h linux/net/inet/inet.h
--- v1.1.12/linux/net/inet/inet.h	Thu Dec 30 14:17:28 1993
+++ linux/net/inet/inet.h	Mon May 23 08:17:48 1994
@@ -1,97 +0,0 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		General Definitions for the TCP/IP (INET) module. This is
- *		mostly a bunch of "general" macros, plus the PROTOCOL link
- *		code and data.
- *
- * Version:	@(#)inet.h	1.0.6	05/25/93
- *
- * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- *		This work was derived friom Ross Biro's inspirational work
- *		for the LINUX operating system.  His version numbers were:
- *
- *		$Id: Space.c,v     0.8.4.5  1992/12/12 19:25:04 bir7 Exp $
- *		$Id: arp.c,v       0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
- *		$Id: arp.h,v       0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
- *		$Id: dev.c,v       0.8.4.13 1993/01/23 18:00:11 bir7 Exp $
- *		$Id: dev.h,v       0.8.4.7  1993/01/23 18:00:11 bir7 Exp $
- *		$Id: eth.c,v       0.8.4.4  1993/01/22 23:21:38 bir7 Exp $
- *		$Id: eth.h,v       0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
- *		$Id: icmp.c,v      0.8.4.9  1993/01/23 18:00:11 bir7 Exp $
- *		$Id: icmp.h,v      0.8.4.2  1992/11/15 14:55:30 bir7 Exp $
- * 		$Id: ip.c,v        0.8.4.8  1992/12/12 19:25:04 bir7 Exp $
- * 		$Id: ip.h,v        0.8.4.2  1993/01/23 18:00:11 bir7 Exp $
- * 		$Id: loopback.c,v  0.8.4.8  1993/01/23 18:00:11 bir7 Exp $
- * 		$Id: packet.c,v    0.8.4.7  1993/01/26 22:04:00 bir7 Exp $
- *		$Id: protocols.c,v 0.8.4.3  1992/11/15 14:55:30 bir7 Exp $
- *		$Id: raw.c,v       0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
- *		$Id: sock.c,v      0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
- *		$Id: sock.h,v      0.8.4.7  1993/01/26 22:04:00 bir7 Exp $
- *		$Id: tcp.c,v       0.8.4.16 1993/01/26 22:04:00 bir7 Exp $
- *		$Id: tcp.h,v       0.8.4.7  1993/01/22 22:58:08 bir7 Exp $
- *		$Id: timer.c,v     0.8.4.8  1993/01/23 18:00:11 bir7 Exp $
- *		$Id: timer.h,v     0.8.4.2  1993/01/23 18:00:11 bir7 Exp $
- *		$Id: udp.c,v       0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
- *		$Id: udp.h,v       0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
- *		$Id: we.c,v        0.8.4.10 1993/01/23 18:00:11 bir7 Exp $
- *		$Id: wereg.h,v     0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
- *
- *		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 _INET_H
-#define _INET_H
-
-
-#include <linux/ddi.h>
-
-
-#define NET16(x)	((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00))
-
-
-#undef	INET_DEBUG
-#ifdef	INET_DEBUG
-#  define	DPRINTF(x)	dprintf x 
-#else
-#   define	DPRINTF(x)	do ; while (0)
-#endif
-
-/* Debug levels. One per module. */
-#define DBG_OFF		0			/* no debugging		*/
-#define DBG_INET	1			/* sock.c		*/
-#define DBG_RT		2			/* route.c		*/
-#define DBG_DEV		3			/* dev.c		*/
-#define DBG_ETH		4			/* eth.c		*/
-#define DBG_PROTO	5			/* protocol.c		*/
-#define DBG_TMR		6			/* timer.c		*/
-#define DBG_PKT		7			/* packet.c		*/
-#define DBG_RAW		8			/* raw.c		*/
-
-#define DBG_LOOPB	10			/* loopback.c		*/
-#define DBG_SLIP	11			/* slip.c		*/
-
-#define DBG_ARP		20			/* arp.c		*/
-#define DBG_IP		21			/* ip.c			*/
-#define DBG_ICMP	22			/* icmp.c		*/
-#define DBG_TCP		23			/* tcp.c		*/
-#define DBG_UDP		24			/* udp.c		*/
-
-
-extern int		inet_debug;
-
-
-extern void		inet_proto_init(struct ddi_proto *pro);
-extern char		*in_ntoa(unsigned long in);
-extern unsigned long	in_aton(char *str);
-
-extern void		dprintf(int level, char *fmt, ...);
-
-extern int		dbg_ioctl(void *arg, int level);
-
-#endif	/* _INET_H */
diff -u --recursive --new-file v1.1.12/linux/net/inet/ip.c linux/net/inet/ip.c
--- v1.1.12/linux/net/inet/ip.c	Mon May 23 12:14:26 1994
+++ linux/net/inet/ip.c	Mon May 23 08:17:48 1994
@@ -50,10 +50,11 @@
  *		Alan Cox	:	BSD address rule semantics. Also see
  *					UDP as there is a nasty checksum issue
  *					if you do things the wrong way.
+ *		Alan Cox	:	Always defrag, moved IP_FORWARD to the config.in file
  *
  * To Fix:
  *		IP option processing is mostly not needed. ip_forward needs to know about routing rules
- *		and time stamp but that's about all.
+ *		and time stamp but that's about all. Use the route mtu field here too
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -83,7 +84,6 @@
 #include "arp.h"
 #include "icmp.h"
 
-#define CONFIG_IP_FORWARD
 #define CONFIG_IP_DEFRAG
 
 extern int last_retran;
@@ -97,67 +97,6 @@
  
 struct ip_mib ip_statistics={1,64,};	/* Forwarding=Yes, Default TTL=64 */
  
-/* 
- *	Print an IP packet for debugging purposes.
- *
- *	This function is exported for the IP
- *	upper layers to use also.
- */
- 
-void ip_print(const struct iphdr *ip)
-{
-	unsigned char buff[32];
-	unsigned char *ptr;
-	int addr;
-	int len;
-	int i;
-
-	/* Are we debugging IP frames */
-	
-  	if (inet_debug != DBG_IP) 
-  		return;
-
-	/* Dump the IP header. */
-	printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n",
-		ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len));
-	printk("    id=%X, ttl=%d, prot=%d, check=%X\n",
-		ip->id, ip->ttl, ip->protocol, ip->check);
-	printk("    frag_off=%d\n", ip->frag_off);
-	printk("    soucre=%s ", in_ntoa(ip->saddr));
-	printk("dest=%s\n", in_ntoa(ip->daddr));
-	printk("    ----\n");
-
-	/* Dump the data. */
-	ptr = (unsigned char *)(ip + 1);
-	addr = 0;
-	len = ntohs(ip->tot_len) - (4 * ip->ihl);
-
-	while (len > 0) 
-	{
-		printk("    %04X: ", addr);
-		for(i = 0; i < 16; i++) 
-		{
-			if (len > 0) 
-			{
-				printk("%02X ", (*ptr & 0xFF));
-				buff[i] = *ptr++;
-				if (buff[i] < 32 || buff[i] > 126) 
-					buff[i] = '.';
-			} 
-			else 
-			{
-				printk("   ");
-				buff[i] = ' ';
-			}
-			addr++;
-			len--;
-		};
-		buff[i] = '\0';
-		printk("  \"%s\"\n", buff);
-  	}
-  	printk("    ----\n\n");
-}
-
 /*
  *	Handle the issuing of an ioctl() request 
  *	for the ip device. This is scheduled to
@@ -168,8 +107,6 @@
 {
   	switch(cmd) 
   	{
-		case DDIOCSDBG:
-			return(dbg_ioctl((void *) arg, DBG_IP));
 		default:
 			return(-EINVAL);
   	}
@@ -190,12 +127,6 @@
 }
 
 
-static void
-print_ipprot(struct inet_protocol *ipprot)
-{
-  DPRINTF((DBG_IP, "handler = %X, protocol = %d, copy=%d \n",
-	   ipprot->handler, ipprot->protocol, ipprot->copy));
-}
 
 
 /* This routine will check to see if we have lost a gateway. */
@@ -272,10 +203,6 @@
   	if (saddr == 0) 
   		saddr = ip_my_addr();
  	
-	DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n"
-		   "                 type=%d, opt=%X, len = %d)\n",
-		   skb, saddr, daddr, *dev, type, opt, len));
-	   
 	buff = skb->data;
 
 	/* 
@@ -303,7 +230,6 @@
 			saddr = src;/*rt->rt_dev->pa_addr;*/
 		raddr = rt->rt_gateway;
 
-		DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr)));
 		opt = &optmem;
 	} 
 	else 
@@ -754,8 +680,7 @@
    	struct ipq *qp;
  
    	qp = (struct ipq *)arg;
-   	DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp));
- 
+
    	/*
    	 *	Send an ICMP "Fragment Reassembly Timeout" message. 
    	 */
@@ -1063,7 +988,6 @@
  		i = prev->end - offset;
  		offset += i;	/* ptr into datagram */
  		ptr += i;	/* ptr into fragment data */
- 		DPRINTF((DBG_IP, "IP: defrag: fixed low overlap %d bytes\n", i));
    	}	
  
    	/*
@@ -1088,8 +1012,6 @@
  		 */
  		if (next->len <= 0) 
  		{
- 			DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n",
- 							next, next->len));
  			if (next->prev != NULL) 
  				next->prev->next = next->next;
  		  	else 
@@ -1101,7 +1023,6 @@
 			kfree_skb(next->skb,FREE_READ); 			
  			kfree_s(next, sizeof(struct ipfrag));
  		}
- 		DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i));
    	}
  
    	/* 
@@ -1188,22 +1109,12 @@
    	mtu = (dev->mtu - hlen);		/* Size of data space */
    	ptr = (raw + hlen);			/* Where to start from */
  	
-   	DPRINTF((DBG_IP, "IP: Fragmentation Desired\n"));
-   	DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
- 		dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
-   	DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
- 
    	/*
    	 *	Check for any "DF" flag. [DF means do not fragment]
    	 */
    	 
    	if (ntohs(iph->frag_off) & IP_DF) 
    	{
- 		DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n"));
- 		DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
- 			dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
- 		DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
- 
  		ip_statistics.IpFragFails++;
  		icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); 
  		return;
@@ -1255,9 +1166,6 @@
 			len/=8;
 			len*=8;
 		}
- 		DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n",
- 							len, len + hlen));
- 
  		/*
  		 *	Allocate buffer. 
  		 */
@@ -1277,7 +1185,7 @@
  		skb2->free = skb->free;
  		skb2->len = len + hlen;
  		skb2->h.raw=(char *) skb2->data;
- 
+ 		skb2->raddr = skb->raddr;	/* For rebuild_header */
 		/*
 		 *	Charge the memory for the fragment to any owner
 		 *	it might posess
@@ -1345,14 +1253,20 @@
 	/*
 	 * Only forward packets that were fired at us when we are in promiscuous
 	 * mode. In standard mode we rely on the driver to filter for us.
+	 *
+	 * This is a mess. When the drivers class packets on the upcall this
+	 * will tidy up!
 	 */
    
 	if(dev->flags&IFF_PROMISC)
 	{
-  		if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len))
+  		if(memcmp(skb->data,dev->dev_addr,dev->addr_len))
   			return;
 	}
 	
+	if(memcmp(skb->data,dev->broadcast, dev->addr_len))
+		return;
+	
 
   
   	/*
@@ -1370,10 +1284,6 @@
 	iph->ttl--;
 	if (iph->ttl <= 0) 
 	{
-		DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n"));
-		DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->saddr)));
-		DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->daddr)));
-	
 		/* Tell the sender its packet died... */
 		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
 		return;
@@ -1395,8 +1305,6 @@
 	rt = ip_rt_route(iph->daddr, NULL, NULL);
 	if (rt == NULL) 
 	{
-		DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n"));
-
 		/*
 		 *	Tell the sender its packet cannot be delivered. Again
 		 *	ICMP is screened later.
@@ -1425,8 +1333,6 @@
 		rt = ip_rt_route(raddr, NULL, NULL);
 		if (rt == NULL) 
 		{
-			DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n"));
-
 			/* 
 			 *	Tell the sender its packet cannot be delivered... 
 			 */
@@ -1460,10 +1366,6 @@
 	 * If the indicated interface is up and running, kick it.
 	 */
 
-	DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr)));
-	DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
-			in_ntoa(raddr), dev2->name, skb->len));
-
 	if (dev2->flags & IFF_UP) 
 	{
 	
@@ -1548,8 +1450,6 @@
 
 	ip_statistics.IpInReceives++;
 	
-	DPRINTF((DBG_IP, "<<\n"));
-	
 	/*
 	 *	Tag the ip header of this packet so we can find it
 	 */
@@ -1568,9 +1468,6 @@
   	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) 
   	{
   		ip_statistics.IpInHdrErrors++;
-		DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n"));
-		DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->saddr)));
-		DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->daddr)));
 		kfree_skb(skb, FREE_WRITE);
 		return(0);
 	}
@@ -1589,7 +1486,6 @@
 	   
 	if (iph->ihl != 5) 
 	{  	/* Fast path for the typical optionless IP packet. */
-      		ip_print(iph);		/* Bogus, only for debugging. */
       		memset((char *) &opt, 0, sizeof(opt));
       		if (do_options(iph, &opt) != 0)
 	  		return 0;
@@ -1628,7 +1524,7 @@
 #ifdef CONFIG_IP_FORWARD
 		ip_forward(skb, dev, is_frag);
 #else
-		printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n",
+		printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
 			iph->saddr,iph->daddr);
 		ip_statistics.IpInAddrErrors++;
 #endif			
@@ -1647,20 +1543,11 @@
 
   	if(is_frag)
   	{
-#ifdef CONFIG_IP_DEFRAG
 		/* Defragment. Obtain the complete packet if there is one */
 		skb=ip_defrag(iph,skb,dev);
         	if(skb==NULL)
         		return 0;
 	        iph=skb->h.iph;
-#else
-		printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
-		printk("    SRC = %s   ", in_ntoa(iph->saddr));
-		printk("    DST = %s (ignored)\n", in_ntoa(iph->daddr));
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
-		kfree_skb(skb, FREE_WRITE);
-		return(0);
-#endif
 	}
 
 	/*
@@ -1681,9 +1568,6 @@
 
        		if (ipprot->protocol != iph->protocol) 
        			continue;
-		DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot));
-		print_ipprot(ipprot);
-
        /*
 	* 	See if we need to make a copy of it.  This will
 	* 	only be set if more than one protocol wants it. 
@@ -1786,8 +1670,6 @@
   	skb->dev = dev;
   	skb->when = jiffies;
   
-	DPRINTF((DBG_IP, ">>\n"));
-
 	/*
 	 *	Find the IP header and set the length. This is bad
 	 *	but once we get the skb data handling code in the
@@ -1825,7 +1707,6 @@
 	/*
 	 *	Print the frame when debugging
 	 */
-	ip_print(iph);
 
 	/*
 	 *	More debugging. You cannot queue a packet already on a list
diff -u --recursive --new-file v1.1.12/linux/net/inet/ipx.c linux/net/inet/ipx.c
--- v1.1.12/linux/net/inet/ipx.c	Tue Apr 19 22:20:35 1994
+++ linux/net/inet/ipx.c	Mon May 23 08:17:49 1994
@@ -22,6 +22,10 @@
  *     			Adding a route will overwrite any existing route to the same
  *			network.
  *	Revision 0.24:	Supports new /proc with no 4K limit
+ *	Revision 0.25:	Add ephemeral sockets, passive local network 
+ *			identification, support for local net 0 and
+ *			multiple datalinks
+ *			
  *
  */
  
@@ -46,6 +50,7 @@
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
+#include "p8022.h"
 
 #ifdef CONFIG_IPX
 /***********************************************************************************************************************\
@@ -184,7 +189,40 @@
 \*******************************************************************************************************************/
 
 
+static struct datalink_proto	*p8022_datalink = NULL;
+static struct datalink_proto	*pEII_datalink = NULL;
+static struct datalink_proto	*p8023_datalink = NULL;
+static struct datalink_proto	*pSNAP_datalink = NULL;
+
 static ipx_route *ipx_router_list=NULL;
+static ipx_route *ipx_localnet_list=NULL;
+
+static ipx_route *
+ipxrtr_get_local_net(struct device *dev, unsigned short datalink)
+{
+	ipx_route *r;
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	r=ipx_localnet_list;
+	while(r!=NULL)
+	{
+		if((r->dev==dev) && (r->dlink_type == datalink))
+		{
+			restore_flags(flags);
+			return r;
+		}
+		r=r->next;
+	}
+	restore_flags(flags);
+	return NULL;
+}
+	
+static ipx_route *
+ipxrtr_get_default_net(void)
+{
+	return ipx_localnet_list;
+}
 
 static ipx_route *ipxrtr_get_dev(long net)
 {
@@ -206,10 +244,55 @@
 	return NULL;
 }
 
+static void ipxrtr_add_localnet(ipx_route *newnet)
+{
+	ipx_route *r;
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+
+	newnet->nextlocal = NULL;
+	if (ipx_localnet_list == NULL) {
+		ipx_localnet_list = newnet;
+		restore_flags(flags);
+		return;
+	}
+
+	r=ipx_localnet_list;
+	while(r->nextlocal!=NULL)
+		r=r->nextlocal;
+
+	r->nextlocal = newnet;
+	
+	restore_flags(flags);
+	return;
+}
+
 static int ipxrtr_create(struct ipx_route_def *r)
 {
 	ipx_route *rt=ipxrtr_get_dev(r->ipx_network);
 	struct device *dev;
+	unsigned short	dlink_type;
+	struct datalink_proto *datalink = NULL;
+
+	if (r->ipx_flags & IPX_RT_BLUEBOOK) {
+		dlink_type = htons(ETH_P_IPX);
+		datalink = pEII_datalink;
+	} else if (r->ipx_flags & IPX_RT_8022) {
+		dlink_type = htons(ETH_P_802_2);
+		datalink = p8022_datalink;
+	} else if (r->ipx_flags & IPX_RT_SNAP) {
+		dlink_type = htons(ETH_P_SNAP);
+		datalink = pSNAP_datalink;
+	} else {
+		dlink_type = htons(ETH_P_802_3);
+		datalink = p8023_datalink;
+	}
+
+	if (datalink == NULL) {
+		printk("IPX: Unsupported datalink protocol.\n");
+		return -EPROTONOSUPPORT;
+	}
 
 	if(r->ipx_router_network!=0)
 	{
@@ -230,7 +313,9 @@
 		rt->net=r->ipx_network;
 		rt->router_net=r->ipx_router_network;
 		memcpy(rt->router_node,r->ipx_router_node,sizeof(rt->router_node));
-		rt->flags=(rt1->flags&IPX_RT_BLUEBOOK)|IPX_RT_ROUTED;
+		rt->flags=IPX_RT_ROUTED;
+		rt->dlink_type = dlink_type;
+		rt->datalink = datalink;
 		rt->dev=rt1->dev;
 		return 0;
 	}
@@ -243,29 +328,53 @@
 		return -EINVAL;
 	if(dev->addr_len<2)
 		return -EINVAL;
+	if (ipxrtr_get_local_net(dev, dlink_type) != NULL)
+		return -EEXIST;
 	/* Ok now create */
-	if (rt==NULL)
-	{
-		rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);	/* Because we are brave and don't lock the table! */
-		if(rt==NULL)
-			return -EAGAIN;
-		rt->next=ipx_router_list;
-		ipx_router_list=rt;
-	}
+	rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);	/* Because we are brave and don't lock the table! */
+	if(rt==NULL)
+		return -EAGAIN;
+	rt->next=ipx_router_list;
+	ipx_router_list=rt;
 	rt->router_net=0;
 	memset(rt->router_node,0,sizeof(rt->router_node));
 	rt->dev=dev;
 	rt->net=r->ipx_network;
-	rt->flags=r->ipx_flags&IPX_RT_BLUEBOOK;
+	rt->flags=0;
+	rt->dlink_type = dlink_type;
+	rt->datalink = datalink;
+	ipxrtr_add_localnet(rt);
 	return 0;
 }
 
+
+static int ipxrtr_delete_localnet(ipx_route *d)
+{
+	ipx_route *r=ipx_localnet_list;
+	if(r==d)
+	{
+		ipx_localnet_list=r->next;
+		return 0;
+	}
+	while(r->next!=NULL)
+	{
+		if(r->nextlocal==d)
+		{
+			r->nextlocal=d->nextlocal;
+			return 0;
+		}
+		r=r->nextlocal;
+	}
+	return -ENOENT;
+}
+
 static int ipxrtr_delete(long net)
 {
 	ipx_route *r=ipx_router_list;
 	if(r->net==net)
 	{
 		ipx_router_list=r->next;
+		kfree_s(r,sizeof(ipx_route));
 		return 0;
 	}
 	while(r->next!=NULL)
@@ -274,6 +383,9 @@
 		{
 			ipx_route *d=r->next;
 			r->next=d->next;
+			if (d->router_net == 0) {
+				ipxrtr_delete_localnet(d);
+			}
 			kfree_s(d,sizeof(ipx_route));
 			return 0;
 		}
@@ -479,8 +591,6 @@
 			kfree_s((void *)sk,sizeof(*sk));
 			return(-ESOCKTNOSUPPORT);
 	}
-	sk->stamp.tv_sec=0;
-	sk->rmem_alloc=0;
 	sk->dead=0;
 	sk->next=NULL;
 	sk->broadcast=0;
@@ -489,7 +599,7 @@
 	sk->wmem_alloc=0;
 	sk->rmem_alloc=0;
 	sk->inuse=0;
-	sk->dead=0;
+	sk->shutdown=0;
 	sk->prot=NULL;	/* So we use default free mechanisms */
 	sk->broadcast=0;
 	sk->err=0;
@@ -497,13 +607,11 @@
 	skb_queue_head_init(&sk->write_queue);
 	sk->send_head=NULL;
 	skb_queue_head_init(&sk->back_log);
-	sk->mtu=512;
 	sk->state=TCP_CLOSE;
 	sk->socket=sock;
 	sk->type=sock->type;
 	sk->ipx_type=0;		/* General user level IPX */
 	sk->debug=0;
-	sk->localroute=0;
 	
 	memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
 	memset(&sk->ipx_source_addr,'\0',sizeof(sk->ipx_source_addr));
@@ -542,12 +650,30 @@
 	return(0);
 }
 		
+static unsigned short first_free_socketnum(void)
+{
+	static unsigned short	socketNum = 0x4000;
+	unsigned short	startNum, foundNum = 0;
+
+	startNum = socketNum;
+	do {
+		if (ipx_find_socket(htons(socketNum)) == NULL) {
+			foundNum = socketNum;
+		}
+		socketNum++;
+		if (socketNum > 0x7ffc) socketNum = 0x4000;
+	}	while (!foundNum && (socketNum != startNum));
+
+	return	htons(foundNum);
+}
+	
 static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
 {
 	ipx_socket *sk;
 	int err;
 	struct sockaddr_ipx addr;
 	struct ipx_route *rt;
+	unsigned char	*nodestart;
 	
 	sk=(ipx_socket *)sock->data;
 	if(sk==NULL)
@@ -555,7 +681,7 @@
 		printk("IPX:bind:sock->data=NULL\n");
 		return 0;
 	}
-
+	
 	if(sk->zapped==0)
 		return(-EIO);
 		
@@ -566,6 +692,11 @@
 		return -EINVAL;
 	memcpy_fromfs(&addr,uaddr,addr_len);
 	
+	if (addr.sipx_port == 0) {
+		addr.sipx_port = first_free_socketnum();
+		if (addr.sipx_port == 0) return -EINVAL;
+	}
+		
 	if(ntohs(addr.sipx_port)<0x4000 && !suser())
 		return(-EPERM);	/* protect IPX system stuff like routing/sap */
 	
@@ -577,20 +708,32 @@
 		if(sk->debug)
 			printk("IPX: bind failed because port %X in use.\n",
 				(int)addr.sipx_port);
-		return(-EADDRINUSE);	   
+		return -EADDRINUSE;	   
 	}
+
 	sk->ipx_source_addr.sock=addr.sipx_port;
-	memcpy(sk->ipx_source_addr.node,addr.sipx_node,sizeof(sk->ipx_source_addr.node));
-	sk->ipx_source_addr.net=addr.sipx_network;
-	if((rt=ipxrtr_get_dev(sk->ipx_source_addr.net))==NULL)
+
+	if (addr.sipx_network == 0L) {
+		rt = ipxrtr_get_default_net();
+	} else {
+		rt = ipxrtr_get_dev(addr.sipx_network);
+	}
+
+	if(rt == NULL)
 	{
 		if(sk->debug)
 			printk("IPX: bind failed (no device for net %lX)\n",
 				sk->ipx_source_addr.net);
-		return(-EADDRNOTAVAIL);
+		return -EADDRNOTAVAIL;
 	}
+
+	sk->ipx_source_addr.net=rt->net;
+
+	/* IPX addresses zero pad physical addresses less than 6 */
 	memset(sk->ipx_source_addr.node,'\0',6);
-	memcpy(sk->ipx_source_addr.node,rt->dev->dev_addr,rt->dev->addr_len);
+	nodestart = sk->ipx_source_addr.node + (6 - rt->dev->addr_len);
+	memcpy(nodestart,rt->dev->dev_addr,rt->dev->addr_len);
+
 	ipx_insert_socket(sk);
 	sk->zapped=0;
 	if(sk->debug)
@@ -621,8 +764,6 @@
 		return err;
 	memcpy_fromfs(&addr,uaddr,sizeof(addr));
 	
-	if(ntohs(addr.sipx_port)<0x4000 && !suser())
-		return -EPERM;
 	if(sk->ipx_source_addr.net==0)	/* Must bind first - no autobinding in this */
 		return -EINVAL;
 		
@@ -691,18 +832,16 @@
 	return(0);
 }
 
-
 int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	/* NULL here for pt means the packet was looped back */
 	ipx_socket *sock;
-	unsigned char *buff;
 	ipx_packet *ipx;
 	ipx_route *rt;
+	ipx_route *ln;
+	unsigned char IPXaddr[6];
 	
-	buff=skb->data;
-	buff+=dev->hard_header_len;
-	ipx=(ipx_packet *)buff;
+	ipx=(ipx_packet *)skb->h.raw;
 	
 	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM)
 	{
@@ -714,7 +853,7 @@
 	}
 	
 	/* Too small */
-	if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet)+dev->hard_header_len)
+	if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet))
 	{
 		kfree_skb(skb,FREE_READ);
 		return(0);
@@ -727,9 +866,20 @@
 		return(0);
 	}
 	
+	/* Determine what local ipx endpoint this is */
+	ln = ipxrtr_get_local_net(dev, pt->type);
+	if (ln == NULL) 
+	{
+		kfree_skb(skb,FREE_READ);
+		return(0);
+	}
+
+	memset(IPXaddr, '\0', 6);
+	memcpy(IPXaddr+(6 - dev->addr_len), dev->dev_addr, dev->addr_len);
+
 	/* Not us/broadcast */
-	if(memcmp(dev->dev_addr,ipx->ipx_dest.node,dev->addr_len)!=0
-	     && memcmp(ipx_broadcast_node,ipx->ipx_dest.node,dev->addr_len)!=0)
+	if(memcmp(IPXaddr,ipx->ipx_dest.node,6)!=0
+	     && memcmp(ipx_broadcast_node,ipx->ipx_dest.node,6)!=0)
 	{
 		/**********************************************************************************************
 		
@@ -739,6 +889,8 @@
 			
 		***********************************************************************************************/
 		
+		int incoming_size;
+		int outgoing_size;
 		struct sk_buff *skb2;
 		int free_it=0;
 		
@@ -749,6 +901,7 @@
 			return(0);
 		}
 
+		ipx->ipx_tctrl++;
 		/* Don't forward if we don't have a route. We ought to go off and start hunting out routes but
 		   if someone needs this _THEY_ can add it */		
 		rt=ipxrtr_get_dev(ipx->ipx_dest.net);
@@ -757,11 +910,16 @@
 			kfree_skb(skb,FREE_READ);
 			return(0);
 		}
-		if(rt->dev->hard_header_len!=dev->hard_header_len)
+
+		/* Check for differences in outgoing and incoming packet size */
+		incoming_size = skb->len - ntohs(ipx->ipx_pktsize);
+		outgoing_size = rt->datalink->header_length + rt->dev->hard_header_len;
+		if(incoming_size != outgoing_size)
 		{
 			/* A different header length causes a copy. Awkward to avoid with the current
 			   sk_buff stuff. */
-			skb2=alloc_skb(skb->len,GFP_ATOMIC);
+			skb2=alloc_skb(ntohs(ipx->ipx_pktsize) + outgoing_size,
+					GFP_ATOMIC);
 			if(skb2==NULL)
 			{
 				kfree_skb(skb,FREE_READ);
@@ -769,30 +927,31 @@
 			}
 			free_it=1;
 			skb2->free=1;
-			skb2->len=skb->len;
-			memcpy((char *)(skb2+1),(char *)(skb+1),skb->len);
-			buff=(char *)(skb2+1);
-			ipx=(ipx_packet *)(buff+dev->hard_header_len);
+			skb2->len=ntohs(ipx->ipx_pktsize) + outgoing_size;
+			skb2->mem_addr = skb2;
+			skb2->arp = 1;
+			skb2->sk = NULL;
+
+			/* Need to copy with appropriate offsets */
+			memcpy((char *)(skb2+1)+outgoing_size,
+				(char *)(skb+1)+incoming_size,
+				ntohs(ipx->ipx_pktsize));
 		}
 		else
 		{
 			skb2=skb;
-			buff=(char *)(skb+1);
 		}
+
 		/* Now operate on the buffer */
 		/* Increase hop count */
-		ipx->ipx_tctrl++;
-		/* If the route is a gateway then forward to it */
 		
-		dev->hard_header(buff, dev, 
-			(rt->flags&IPX_RT_BLUEBOOK)?ntohs(ETH_P_IPX):ntohs(skb2->len),
-			(rt->flags&IPX_RT_ROUTED)?rt->router_node:ipx->ipx_dest.node,
-			NULL,	/* Our source */
-			skb2->len,
-			skb2);
-			
-		dev_queue_xmit(skb2,dev,SOPRI_NORMAL);
+		skb2->dev = rt->dev;
+		rt->datalink->datalink_header(rt->datalink, skb2, 
+			(rt->flags&IPX_RT_ROUTED)?rt->router_node
+						:ipx->ipx_dest.node);
 
+		dev_queue_xmit(skb2,rt->dev,SOPRI_NORMAL);
+
 		if(free_it)
 			kfree_skb(skb,FREE_READ);
 		return(0);
@@ -800,13 +959,22 @@
 	/************ End of router: Now sanity check stuff for us ***************/
 	
 	/* Ok its for us ! */
-	
+	if (ln->net == 0L) {
+		printk("IPX: Registering local net %lx\n", ipx->ipx_dest.net);
+		ln->net = ipx->ipx_dest.net;
+	}
+
 	sock=ipx_find_socket(ipx->ipx_dest.sock);
 	if(sock==NULL)	/* But not one of our sockets */
 	{
 		kfree_skb(skb,FREE_READ);
 		return(0);
 	}
+
+	/* Check to see if this socket needs its network number */
+	ln = ipxrtr_get_default_net();
+	if (sock->ipx_source_addr.net == 0L)
+		sock->ipx_source_addr.net = ln->net;
 	
 	if(sock->rmem_alloc>=sock->rcvbuf)
 	{
@@ -815,15 +983,14 @@
 	}
 	
 	sock->rmem_alloc+=skb->mem_len;
+	skb->sk = sock;
+
 	skb_queue_tail(&sock->receive_queue,skb);
 	if(!sock->dead)
 		sock->data_ready(sock,skb->len);
 	return(0);
 }
 
-
-
-
 static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
 	unsigned flags, struct sockaddr *usip, int addr_len)
 {
@@ -836,8 +1003,12 @@
 	struct ipx_packet *ipx;
 	int size;
 	ipx_route *rt;
+	struct datalink_proto *dl = NULL;
+	unsigned char IPXaddr[6];
+	int self_addressing = 0;
+	int broadcast = 0;
 
-	if(flags&~MSG_DONTROUTE)
+	if(flags)
 		return -EINVAL;
 	if(len<0)
 		return -EINVAL;
@@ -869,8 +1040,13 @@
 	
 	if(sk->debug)
 		printk("IPX: sendto: Addresses built.\n");
-	if(!sk->broadcast && memcmp(&sipx.sipx_node,&ipx_broadcast_node,6)==0)
-		return -ENETUNREACH;
+
+	if(memcmp(&sipx.sipx_node,&ipx_broadcast_node,6)==0) {
+		if (!sk->broadcast)
+			return -ENETUNREACH;
+		broadcast = 1;
+	}
+
 	/* Build a packet */
 	
 	if(sk->debug)
@@ -882,57 +1058,70 @@
 	size=sizeof(ipx_packet)+len;	/* For mac headers */
 
 	/* Find out where this has to go */
-	rt=ipxrtr_get_dev(sipx.sipx_network);
-	/* No suitable route - no gateways when not routing */
-	if(rt==NULL || ((flags&IPX_RT_ROUTED)&& ((flags&MSG_DONTROUTE)||sk->localroute)))
+	if (sipx.sipx_network == 0L) {
+		rt = ipxrtr_get_default_net();
+		if (rt != NULL)
+			sipx.sipx_network = rt->net;
+	} else
+		rt=ipxrtr_get_dev(sipx.sipx_network);
+
+	if(rt==NULL)
 	{
 		return -ENETUNREACH;
 	}
 	
 	dev=rt->dev;
+	dl = rt->datalink;
 	
-	size+=dev->hard_header_len;
-		
+	size += dev->hard_header_len;
+	size += dl->header_length;
+
 	if(sk->debug)
-		printk("IPX: sendto: allocating buffer (%d)\n",size-sizeof(struct sk_buff));
+		printk("IPX: sendto: allocating buffer (%d)\n",size);
 	
-	if(size+sk->wmem_alloc>sk->sndbuf)
+	if(size+sk->wmem_alloc>sk->sndbuf) {
 		return -EAGAIN;
+	}
 		
 	skb=alloc_skb(size,GFP_KERNEL);
 	if(skb==NULL)
 		return -ENOMEM;
-	if(skb->mem_len+sk->wmem_alloc>sk->sndbuf)
-	{
-		kfree_skb(skb,FREE_WRITE);
-		return -EAGAIN;
-	}
-
-	sk->wmem_alloc+=skb->mem_len;
+		
+	skb->mem_addr=skb;
 	skb->sk=sk;
 	skb->free=1;
 	skb->arp=1;
-	skb->len=size-sizeof(struct sk_buff);
+	skb->len=size;
+
+	sk->wmem_alloc+=skb->mem_len;
 
 	if(sk->debug)
 		printk("Building MAC header.\n");		
 	skb->dev=rt->dev;
 	
-	dev->hard_header(skb->data,skb->dev,
-		(rt->flags&IPX_RT_BLUEBOOK)?ETH_P_IPX:ETH_P_802_3),
-		(rt->flags&IPX_RT_ROUTED)?rt->router_node:sipx.sipx_node,
-		NULL,
-		len+sizeof(ipx_packet),
-		skb);
+	/* Build Data Link header */
+	dl->datalink_header(dl, skb, 
+		(rt->flags&IPX_RT_ROUTED)?rt->router_node:sipx.sipx_node);
+
+	/* See if we are sending to ourself */
+	memset(IPXaddr, '\0', 6);
+	memcpy(IPXaddr+(6 - skb->dev->addr_len), skb->dev->dev_addr, 
+			skb->dev->addr_len);
+
+	self_addressing = !memcmp(IPXaddr, 
+				(rt->flags&IPX_RT_ROUTED)?rt->router_node
+				:sipx.sipx_node,
+				6);
 
 	/* Now the IPX */
 	if(sk->debug)
 		printk("Building IPX Header.\n");
-	ipx=(ipx_packet *)skb->data+skb->dev->hard_header_len;
+	ipx=(ipx_packet *)skb->h.raw;
 	ipx->ipx_checksum=0xFFFF;
 	ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
 	ipx->ipx_tctrl=0;
-	ipx->ipx_type=sk->ipx_type;
+	ipx->ipx_type=sipx.sipx_type;
+
 	memcpy(&ipx->ipx_source,&sk->ipx_source_addr,sizeof(ipx->ipx_source));
 	ipx->ipx_dest.net=sipx.sipx_network;
 	memcpy(ipx->ipx_dest.node,sipx.sipx_node,sizeof(ipx->ipx_dest.node));
@@ -943,11 +1132,35 @@
 	memcpy_fromfs((char *)(ipx+1),ubuf,len);
 	if(sk->debug)
 		printk("IPX: Transmitting buffer\n");
-	if(dev->flags&IFF_LOOPBACK)
+	if((dev->flags&IFF_LOOPBACK) || self_addressing) {
+		struct packet_type	pt;
+
 		/* loop back */
-		ipx_rcv(skb,dev,NULL);
-	else
+		pt.type = rt->dlink_type;
+		sk->wmem_alloc-=skb->mem_len;
+		skb->sk = NULL;
+		ipx_rcv(skb,dev,&pt);
+	} else {
+		if (broadcast) {
+			struct packet_type	pt;
+			struct sk_buff		*skb2;
+
+			/* loop back */
+			pt.type = rt->dlink_type;
+			
+			skb2=alloc_skb(skb->len, GFP_ATOMIC);
+			skb2->mem_addr=skb2;
+			skb2->free=1;
+			skb2->arp=1;
+			skb2->len=skb->len;
+			skb2->sk = NULL;
+			skb2->h.raw = skb2->data + rt->datalink->header_length
+				+ dev->hard_header_len;
+			memcpy(skb2->data, skb->data, skb->len);
+			ipx_rcv(skb2,dev,&pt);
+		}
 		dev_queue_xmit(skb,dev,SOPRI_NORMAL);
+	}
 	return len;
 }
 
@@ -956,21 +1169,17 @@
 	return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0);
 }
 
-static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
-{
-	return ipx_send(sock,ubuf,size,noblock,0);
-}
-
 static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
 		   unsigned flags, struct sockaddr *sip, int *addr_len)
 {
 	ipx_socket *sk=(ipx_socket *)sock->data;
 	struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)sip;
+	struct ipx_packet	*ipx = NULL;
 	/* FILL ME IN */
 	int copied = 0;
 	struct sk_buff *skb;
 	int er;
-
+	
 	if(sk->err)
 	{
 		er= -sk->err;
@@ -1001,24 +1210,33 @@
 	skb=skb_recv_datagram(sk,flags,noblock,&er);
 	if(skb==NULL)
 		return er;
-	copied=(size<skb->len)?size:skb->len;
+
+	ipx = (ipx_packet *)(skb->h.raw);
+	copied=ntohs(ipx->ipx_pktsize) - sizeof(ipx_packet);
 	skb_copy_datagram(skb,sizeof(struct ipx_packet),ubuf,copied);
-	sk->stamp=skb->stamp;
 	
 	if(sipx)
 	{
 		struct sockaddr_ipx addr;
 		
 		addr.sipx_family=AF_IPX;
-		addr.sipx_port=((ipx_packet*)skb->h.raw)->ipx_source.sock;
-		memcpy(addr.sipx_node,((ipx_packet*)skb->h.raw)->ipx_source.node,sizeof(addr.sipx_node));
-		addr.sipx_network=((ipx_packet*)skb->h.raw)->ipx_source.net;
+		addr.sipx_port=ipx->ipx_source.sock;
+		memcpy(addr.sipx_node,ipx->ipx_source.node,sizeof(addr.sipx_node));
+		addr.sipx_network=ipx->ipx_source.net;
+		addr.sipx_type = ipx->ipx_type;
 		memcpy_tofs(sipx,&addr,sizeof(*sipx));
 	}
 	skb_free_datagram(skb);
 	return(copied);
 }		
 
+
+static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
+{
+	return ipx_send(sock,ubuf,size,noblock,0);
+}
+
+
 static int ipx_recv(struct socket *sock, void *ubuf, int size , int noblock,
 	unsigned flags)
 {
@@ -1110,16 +1328,26 @@
 };
  
 
-void ipx_proto_init(struct ddi_proto *pro)
+extern struct datalink_proto	*make_EII_client(void);
+extern struct datalink_proto	*make_8023_client(void);
+
+void ipx_proto_init(struct net_proto *pro)
 {
+	unsigned char	val = 0xE0;
 	(void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
+
+	pEII_datalink = make_EII_client();
 	ipx_dix_packet_type.type=htons(ETH_P_IPX);
 	dev_add_pack(&ipx_dix_packet_type);
+
+	p8023_datalink = make_8023_client();
 	ipx_8023_packet_type.type=htons(ETH_P_802_3);
 	dev_add_pack(&ipx_8023_packet_type);
 	
-	printk("Swansea University Computer Society IPX 0.24 BETA for NET3 ALPHA.008\n");
+	if ((p8022_datalink = register_8022_client(val, ipx_rcv)) == NULL)
+		printk("IPX: Unable to register with 802.2\n");
 	
+	printk("Swansea University Computer Society IPX 0.25 BETA for NET3 014\n");
+	
 }
-
 #endif
diff -u --recursive --new-file v1.1.12/linux/net/inet/ipx.h linux/net/inet/ipx.h
--- v1.1.12/linux/net/inet/ipx.h	Tue Apr 19 10:53:59 1994
+++ linux/net/inet/ipx.h	Mon May 23 08:17:49 1994
@@ -8,10 +8,11 @@
  *	Which is available from ftp.novell.com
  */
 
-#ifndef _IPX_H
-#define _IPX_H
+#ifndef _NET_INET_IPX_H_
+#define _NET_INET_IPX_H_
 
-#include <linux/ddi.h>
+#include <linux/ipx.h>
+#include "datalink.h"
 
 typedef struct
 {
@@ -46,10 +47,15 @@
 	unsigned char router_node[6];
 	unsigned long router_net;
 	unsigned short flags;
-	struct device *dev;
 #define IPX_RT_ROUTED	1		/* This isn't a direct route. Send via this if to node router_node */
-#define IPX_RT_BLUEBOOK	2		/* Use DIX 8137 frames not IEE802.3 */
+#define IPX_RT_BLUEBOOK	2		
+#define IPX_RT_8022	4		
+#define IPX_RT_SNAP	8		
+	unsigned short dlink_type;
+	struct device *dev;
+	struct datalink_proto *datalink;
 	struct ipx_route *next;
+	struct ipx_route *nextlocal;
 } ipx_route;
 
 
diff -u --recursive --new-file v1.1.12/linux/net/inet/ipxcall.h linux/net/inet/ipxcall.h
--- v1.1.12/linux/net/inet/ipxcall.h	Tue Apr 19 10:54:00 1994
+++ linux/net/inet/ipxcall.h	Mon May 23 08:17:49 1994
@@ -1,2 +1,2 @@
-/* Seperate to keep compilation of Space.c simpler */
-extern void ipx_proto_init(struct ddi_proto *pro);
+/* Seperate to keep compilation of protocols.c simpler */
+extern void ipx_proto_init(struct net_proto *pro);
diff -u --recursive --new-file v1.1.12/linux/net/inet/p8022.c linux/net/inet/p8022.c
--- v1.1.12/linux/net/inet/p8022.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/p8022.c	Mon May 23 08:17:49 1994
@@ -0,0 +1,98 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+#include <linux/ddi.h>
+
+static struct datalink_proto *p8022_list = NULL;
+
+static struct datalink_proto *
+find_8022_client(unsigned char type)
+{
+	struct datalink_proto	*proto;
+
+	for (proto = p8022_list;
+		((proto != NULL) && (*(proto->type) != type));
+		proto = proto->next)
+		;
+
+	return proto;
+}
+
+int
+p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct datalink_proto	*proto;
+
+	proto = find_8022_client(*(skb->h.raw));
+	if (proto != NULL) {
+		skb->h.raw += 3;
+		return proto->rcvfunc(skb, dev, pt);
+	}
+
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+static void
+p8022_datalink_header(struct datalink_proto *dl, 
+		struct sk_buff *skb, unsigned char *dest_node)
+{
+	struct device	*dev = skb->dev;
+	unsigned long	len = skb->len;
+	unsigned long	hard_len = dev->hard_header_len;
+	unsigned char	*rawp;
+
+	dev->hard_header(skb->data, dev, len - hard_len,
+			dest_node, NULL, len - hard_len, skb);
+	rawp = skb->data + hard_len;
+	*rawp = dl->type[0];
+	rawp++;
+	*rawp = dl->type[0];
+	rawp++;
+	*rawp = 0x03;	/* UI */
+	rawp++;
+	skb->h.raw = rawp;
+}
+
+static struct packet_type p8022_packet_type = 
+{
+	0,	/* MUTTER ntohs(ETH_P_IPX),*/
+	0,		/* copy */
+	p8022_rcv,
+	NULL,
+	NULL,
+};
+ 
+
+void p8022_proto_init(struct net_proto *pro)
+{
+	p8022_packet_type.type=htons(ETH_P_802_2);
+	dev_add_pack(&p8022_packet_type);
+}
+	
+struct datalink_proto *
+register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+{
+	struct datalink_proto	*proto;
+
+	if (find_8022_client(type) != NULL)
+		return NULL;
+
+	proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+	if (proto != NULL) {
+		proto->type[0] = type;
+		proto->type_len = 1;
+		proto->rcvfunc = rcvfunc;
+		proto->header_length = 3;
+		proto->datalink_header = p8022_datalink_header;
+	}
+
+	proto->next = p8022_list;
+	p8022_list = proto;
+
+	return proto;
+}
+
diff -u --recursive --new-file v1.1.12/linux/net/inet/p8022.h linux/net/inet/p8022.h
--- v1.1.12/linux/net/inet/p8022.h	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/p8022.h	Mon May 23 08:17:49 1994
@@ -0,0 +1,2 @@
+struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+
diff -u --recursive --new-file v1.1.12/linux/net/inet/p8022call.h linux/net/inet/p8022call.h
--- v1.1.12/linux/net/inet/p8022call.h	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/p8022call.h	Mon May 23 08:17:49 1994
@@ -0,0 +1,2 @@
+/* Seperate to keep compilation of Space.c simpler */
+extern void p8022_proto_init(struct net_proto *);
diff -u --recursive --new-file v1.1.12/linux/net/inet/p8023.c linux/net/inet/p8023.c
--- v1.1.12/linux/net/inet/p8023.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/p8023.c	Mon May 23 08:17:49 1994
@@ -0,0 +1,34 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static void
+p8023_datalink_header(struct datalink_proto *dl, 
+		struct sk_buff *skb, unsigned char *dest_node)
+{
+	struct device	*dev = skb->dev;
+	unsigned long	len = skb->len;
+	unsigned long	hard_len = dev->hard_header_len;
+
+	dev->hard_header(skb->data, dev, len - hard_len,
+			dest_node, NULL, len - hard_len, skb);
+	skb->h.raw = skb->data + hard_len;
+}
+
+struct datalink_proto *
+make_8023_client(void)
+{
+	struct datalink_proto	*proto;
+
+	proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+	if (proto != NULL) {
+		proto->type_len = 0;
+		proto->header_length = 0;
+		proto->datalink_header = p8023_datalink_header;
+	}
+
+	return proto;
+}
+
diff -u --recursive --new-file v1.1.12/linux/net/inet/packet.c linux/net/inet/packet.c
--- v1.1.12/linux/net/inet/packet.c	Tue Apr 19 10:54:00 1994
+++ linux/net/inet/packet.c	Mon May 23 08:17:49 1994
@@ -193,8 +193,7 @@
 	 
 	if (skb == NULL) 
 	{
-		DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n"));
-		return(-ENOMEM);
+		return(-ENOBUFS);
 	}
 	
 	/*
diff -u --recursive --new-file v1.1.12/linux/net/inet/pe2.c linux/net/inet/pe2.c
--- v1.1.12/linux/net/inet/pe2.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/pe2.c	Mon May 23 08:17:50 1994
@@ -0,0 +1,34 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "datalink.h"
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static void
+pEII_datalink_header(struct datalink_proto *dl, 
+		struct sk_buff *skb, unsigned char *dest_node)
+{
+	struct device	*dev = skb->dev;
+	unsigned long	len = skb->len;
+	unsigned long	hard_len = dev->hard_header_len;
+
+	dev->hard_header(skb->data, dev, ETH_P_IPX,
+			dest_node, NULL, len - hard_len, skb);
+	skb->h.raw = skb->data + hard_len;
+}
+
+struct datalink_proto *
+make_EII_client(void)
+{
+	struct datalink_proto	*proto;
+
+	proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+	if (proto != NULL) {
+		proto->type_len = 0;
+		proto->header_length = 0;
+		proto->datalink_header = pEII_datalink_header;
+	}
+
+	return proto;
+}
+
diff -u --recursive --new-file v1.1.12/linux/net/inet/protocol.c linux/net/inet/protocol.c
--- v1.1.12/linux/net/inet/protocol.c	Tue Apr 19 10:54:01 1994
+++ linux/net/inet/protocol.c	Mon May 23 08:17:50 1994
@@ -88,10 +88,8 @@
   unsigned char hash;
   struct inet_protocol *p;
 
-  DPRINTF((DBG_PROTO, "get_protocol (%d)\n ", prot));
   hash = prot & (MAX_INET_PROTOS - 1);
   for (p = inet_protos[hash] ; p != NULL; p=p->next) {
-	DPRINTF((DBG_PROTO, "trying protocol %d\n", p->protocol));
 	if (p->protocol == prot) return((struct inet_protocol *) p);
   }
   return(NULL);
diff -u --recursive --new-file v1.1.12/linux/net/inet/raw.c linux/net/inet/raw.c
--- v1.1.12/linux/net/inet/raw.c	Thu Apr 21 09:46:35 1994
+++ linux/net/inet/raw.c	Mon May 23 08:17:50 1994
@@ -23,6 +23,7 @@
  *		Alan Cox	:	Fixed error return for broadcasts
  *		Alan Cox	:	Removed wake_up calls
  *		Alan Cox	:	Use ttl/tos
+ *		Alan Cox	:	Cleaned up old debugging
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -44,9 +45,6 @@
 #include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
-#if 0
-#include "tcp.h"
-#endif
 #include <linux/skbuff.h>
 #include "sock.h"
 #include "icmp.h"
@@ -68,9 +66,6 @@
 {
   struct sock *sk;
    
-  DPRINTF((DBG_RAW, "raw_err(err=%d, hdr=%X, daddr=%X, saddr=%X, protocl=%X)\n",
-		err, header, daddr, saddr, protocol));
-
   if (protocol == NULL) return;
   sk = (struct sock *) protocol->data;
   if (sk == NULL) return;
@@ -99,10 +94,6 @@
 {
   struct sock *sk;
 
-  DPRINTF((DBG_RAW, "raw_rcv(skb=%X, dev=%X, opt=%X, daddr=%X,\n"
-	   "         len=%d, saddr=%X, redo=%d, protocol=%X)\n",
-	   skb, dev, opt, daddr, len, saddr, redo, protocol));
-
   if (skb == NULL)
   	return(0);
   	
@@ -155,22 +146,12 @@
 	int tmp;
 	int err;
 
-	DPRINTF((DBG_RAW, "raw_sendto(sk=%X, from=%X, len=%d, noblock=%d, flags=%X,\n"
-		"            usin=%X, addr_len = %d)\n", sk, from, len, noblock,
-		flags, usin, addr_len));
-
 	/*
 	 *	Check the flags. Only MSG_DONTROUTE is permitted.
 	 */
 	 
 	if (flags&MSG_DONTROUTE)
 		return(-EINVAL);
-	if (len < 0) 
-		return(-EINVAL);
-
-	err=verify_area(VERIFY_READ,from,len);
-	if(err)
-		return err;
 	/*
 	 *	Get and verify the address. 
 	 */
@@ -222,7 +203,6 @@
 		{
 			int tmp;
 
-			DPRINTF((DBG_RAW, "raw_sendto: write buffer full?\n"));
 			if (noblock) 
 				return(-EAGAIN);
 			tmp = sk->wmem_alloc;
@@ -248,7 +228,6 @@
 			       sk->protocol, sk->opt, skb->mem_len, sk->ip_tos,sk->ip_ttl);
 	if (tmp < 0) 
 	{
-		DPRINTF((DBG_RAW, "raw_sendto: error building ip header.\n"));
 		kfree_skb(skb,FREE_WRITE);
 		release_sock(sk);
 		return(tmp);
@@ -293,11 +272,7 @@
 	sk->inuse = 1;
 	sk->state = TCP_CLOSE;
 
-	DPRINTF((DBG_RAW, "raw_close: deleting protocol %d\n",
-	   ((struct inet_protocol *)sk->pair)->protocol));
-
-	if (inet_del_protocol((struct inet_protocol *)sk->pair) < 0)
-		DPRINTF((DBG_RAW, "raw_close: del_protocol failed.\n"));
+	inet_del_protocol((struct inet_protocol *)sk->pair);
 	kfree_s((void *)sk->pair, sizeof (struct inet_protocol));
 	sk->pair = NULL;
 	release_sock(sk);
@@ -323,8 +298,6 @@
   /* We need to remember this somewhere. */
   sk->pair = (struct sock *)p;
 
-  DPRINTF((DBG_RAW, "raw init added protocol %d\n", sk->protocol));
-
   return(0);
 }
 
@@ -341,10 +314,6 @@
   int copied=0;
   struct sk_buff *skb;
   int err;
-
-  DPRINTF((DBG_RAW, "raw_recvfrom (sk=%X, to=%X, len=%d, noblock=%d, flags=%X,\n"
-	   "              sin=%X, addr_len=%X)\n",
-		sk, to, len, noblock, flags, sin, addr_len));
 
   if (len == 0) return(0);
   if (len < 0) return(-EINVAL);
diff -u --recursive --new-file v1.1.12/linux/net/inet/route.c linux/net/inet/route.c
--- v1.1.12/linux/net/inet/route.c	Tue Apr 19 22:20:35 1994
+++ linux/net/inet/route.c	Mon May 23 08:17:50 1994
@@ -20,6 +20,7 @@
  *		Linus Torvalds	:	Rewrote bits to be sensible
  *		Alan Cox	:	Added BSD route gw semantics
  *		Alan Cox	:	Super /proc >4K 
+ *		Alan Cox	:	MTU in route table
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -60,26 +61,6 @@
 static struct rtable *rt_loopback = NULL;
 
 /*
- *	Dump the contents of a routing table entry. 
- */
- 
-static void rt_print(struct rtable *rt)
-{
-
-	if (rt == NULL || inet_debug != DBG_RT) 
-	  	return;
-
-	printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
-		(long) rt, (long) rt->rt_next, rt->rt_flags);
-	printk("    TARGET=%s ", in_ntoa(rt->rt_dst));
-	printk("GW=%s ", in_ntoa(rt->rt_gateway));
-	printk("    DEV=%s USE=%ld REF=%d\n",
-		(rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
-		rt->rt_use, rt->rt_refcnt);
-}
-
-
-/*
  *	Remove a routing table entry.
  */
 
@@ -88,7 +69,6 @@
 	struct rtable *r, **rp;
 	unsigned long flags;
 
-	DPRINTF((DBG_RT, "RT: flushing for dst %s\n", in_ntoa(dst)));
 	rp = &rt_base;
 	
 	/*
@@ -130,7 +110,6 @@
 	struct rtable **rp;
 	unsigned long flags;
 
-	DPRINTF((DBG_RT, "RT: flushing for dev 0x%08lx (%s)\n", (long)dev, dev->name));
 	rp = &rt_base;
 	cli();
 	save_flags(flags);
@@ -216,7 +195,7 @@
  */
  
 void ip_rt_add(short flags, unsigned long dst, unsigned long mask,
-	unsigned long gw, struct device *dev)
+	unsigned long gw, struct device *dev, unsigned short mtu)
 {
 	struct rtable *r, *rt;
 	struct rtable **rp;
@@ -280,7 +259,6 @@
 	rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
 	if (rt == NULL) 
 	{
-		DPRINTF((DBG_RT, "RT: no memory for new route!\n"));
 		return;
 	}
 	memset(rt, 0, sizeof(struct rtable));
@@ -290,8 +268,12 @@
 	rt->rt_gateway = gw;
 	rt->rt_mask = mask;
 	rt->rt_mtu = dev->mtu;
-	rt_print(rt);
 
+	/* Are the MSS/Window valid ? */
+
+	if(rt->rt_flags & RTF_MTU)
+		rt->rt_mtu = mtu;
+
 	/*
 	 *	What we have to do is loop though this until we have
 	 *	found the first address which has a higher generality than
@@ -467,7 +449,7 @@
 	 *	Add the route
 	 */
 	 
-	ip_rt_add(flags, daddr, mask, gw, dev);
+	ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mtu);
 	return 0;
 }
 
@@ -499,7 +481,7 @@
 	int size;
 
 	len += sprintf(buffer,
-		 "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\n");
+		 "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\n");
 	pos=len;
   
 	/*
@@ -508,10 +490,10 @@
 	 
 	for (r = rt_base; r != NULL; r = r->rt_next) 
 	{
-        	size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n",
+        	size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\n",
 			r->rt_dev->name, r->rt_dst, r->rt_gateway,
 			r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
-			r->rt_mask);
+			r->rt_mask, (int)r->rt_mtu);
 		len+=size;
 		pos+=size;
 		if(pos<offset)
@@ -644,9 +626,6 @@
 
 	switch(cmd) 
 	{
-		case DDIOCSDBG:		/* Control debugging */
-			return dbg_ioctl(arg, DBG_RT);
-	
 		case SIOCADDRTOLD:	/* Old style add route */
 		case SIOCDELRTOLD:	/* Old style delete route */
 			if (!suser())
diff -u --recursive --new-file v1.1.12/linux/net/inet/route.h linux/net/inet/route.h
--- v1.1.12/linux/net/inet/route.h	Tue Apr 19 22:20:36 1994
+++ linux/net/inet/route.h	Mon May 23 08:17:50 1994
@@ -11,6 +11,7 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  * Fixes:
  *		Alan Cox	:	Reformatted. Added ip_rt_local()
+ *		Alan Cox	:	Support for TCP parameters.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -42,7 +43,7 @@
 
 extern void		ip_rt_flush(struct device *dev);
 extern void		ip_rt_add(short flags, unsigned long addr, unsigned long mask,
-			       unsigned long gw, struct device *dev);
+			       unsigned long gw, struct device *dev, unsigned short);
 extern struct rtable	*ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr);
 extern struct rtable 	*ip_rt_local(unsigned long daddr, struct options *opt, unsigned long *src_addr);
 extern int		rt_get_info(char * buffer, char **start, off_t offset, int length);
diff -u --recursive --new-file v1.1.12/linux/net/inet/skbuff.c linux/net/inet/skbuff.c
--- v1.1.12/linux/net/inet/skbuff.c	Sun May  1 12:12:52 1994
+++ linux/net/inet/skbuff.c	Mon May 23 09:43:14 1994
@@ -421,12 +421,22 @@
 			else
 				skb->sk->wmem_alloc-=skb->mem_len;
 			if(!skb->sk->dead)
-				wake_up_interruptible(skb->sk->sleep);
+				skb->sk->write_space(skb->sk);
+#ifdef CONFIG_SLAVE_BALANCING				
+			if(skb->in_dev_queue && skb->dev!=NULL)
+				skb->dev->pkt_queue--;
+#endif
 			kfree_skbmem(skb->mem_addr,skb->mem_len);
 		}
 	} 
 	else 
+	{
+#ifdef CONFIG_SLAVE_BALANCING				
+		if(skb->in_dev_queue && skb->dev!=NULL)
+			skb->dev->pkt_queue--;
+#endif
 		kfree_skbmem(skb->mem_addr, skb->mem_len);
+	}
 }
 
 /*
@@ -457,6 +467,9 @@
  	skb->truesize = size;
  	skb->mem_len = size;
  	skb->mem_addr = skb;
+#ifdef CONFIG_SLAVE_BALANCING 	
+ 	skb->in_dev_queue = 0;
+#endif 	
  	skb->fraglist = NULL;
 	skb->prev = skb->next = NULL;
 	skb->link3 = NULL;
@@ -477,8 +490,19 @@
 
 void kfree_skbmem(void *mem,unsigned size)
 {
+#ifdef CONFIG_SLAVE_BALANCING
+	struct sk_buff *x = mem;
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if(x->in_dev_queue && x->dev!=NULL)
+		x->dev->pkt_queue--;
+	restore_flags(flags);
+#endif	
 #if CONFIG_SKB_CHECK
+#ifndef CONFIG_SLAVE_BALANCING
 	struct sk_buff *x = mem;
+#endif	
 	IS_SKB(x);
 	if(x->magic_debug_cookie == SK_GOOD_SKB)
 	{
diff -u --recursive --new-file v1.1.12/linux/net/inet/sock.c linux/net/inet/sock.c
--- v1.1.12/linux/net/inet/sock.c	Mon May 23 12:14:27 1994
+++ linux/net/inet/sock.c	Mon May 23 08:17:51 1994
@@ -3,13 +3,16 @@
  *		operating system.  INET is implemented using the  BSD Socket
  *		interface as the means of communication with the user level.
  *
- *		SOCK - AF_INET protocol family socket handler.
+ *		Generic socket support routines. Memory allocators, sk->inuse/release
+ *		handler for protocols to use and generic option handler.
  *
+ *
  * Version:	@(#)sock.c	1.0.17	06/02/93
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Florian La Roche, <flla@stud.uni-sb.de>
+ *		Alan Cox, <A.Cox@swansea.ac.uk>
  *
  * Fixes:
  *		Alan Cox	: 	Numerous verify_area() problems
@@ -53,6 +56,10 @@
  *		Alan Cox	:	Callbacks
  *		Alan Cox	:	Nagle flag for Charles & Johannes stuff
  *		Alex		:	Removed restriction on inet fioctl
+ *		Alan Cox	:	Splitting INET from NET core
+ *		Alan Cox	:	Fixed bogus SO_TYPE handling in getsockopt()
+ *		Adam Caldwell	:	Missing return in SO_DONTROUTE/SO_DEBUG code
+ *		Alan Cox	:	Split IP from generic code
  *
  * To Fix:
  *
@@ -96,373 +103,8 @@
 #include "raw.h"
 #include "icmp.h"
 
-
-int inet_debug = DBG_OFF;		/* INET module debug flag	*/
-
-
 #define min(a,b)	((a)<(b)?(a):(b))
 
-extern struct proto packet_prot;
-
-
-void
-print_sk(struct sock *sk)
-{
-  if (!sk) {
-	printk("  print_sk(NULL)\n");
-	return;
-  }
-  printk("  wmem_alloc = %lu\n", sk->wmem_alloc);
-  printk("  rmem_alloc = %lu\n", sk->rmem_alloc);
-  printk("  state = %d\n",sk->state);
-  printk("  daddr = %lX, saddr = %lX\n", sk->daddr,sk->saddr);
-  printk("  num = %d", sk->num);
-  printk(" next = %p\n", sk->next);
-  printk("  write_seq = %ld, acked_seq = %ld, copied_seq = %ld\n",
-	  sk->write_seq, sk->acked_seq, sk->copied_seq);
-  printk("  rcv_ack_seq = %ld, window_seq = %ld, fin_seq = %ld\n",
-	  sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
-  printk("  prot = %p\n", sk->prot);
-  printk("  pair = %p\n", sk->pair);
-  printk("  inuse = %d , blog = %d\n", sk->inuse, sk->blog);
-  printk("  dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
-  printk("  retransmits = %ld, timeout = %d\n", sk->retransmits, sk->timeout);
-  printk("  cong_window = %d, packets_out = %d\n", sk->cong_window,
-	  sk->packets_out);
-  printk("  shutdown=%d\n", sk->shutdown);
-}
-
-
-#if 0
-void
-print_skb(struct sk_buff *skb)
-{
-  if (!skb) {
-	printk("  print_skb(NULL)\n");
-	return;
-  }
-  printk("  prev = %p, next = %p\n", skb->prev, skb->next);
-  printk("  sk = %p link3 = %p\n", skb->sk, skb->link3);
-  printk("  mem_addr = %p, mem_len = %lu\n", skb->mem_addr, skb->mem_len);
-  printk("  used = %d free = %d\n", skb->used,skb->free);
-}
-#endif
-
-
-
-static int
-sk_inuse(struct proto *prot, int num)
-{
-  struct sock *sk;
-
-  for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
-      sk != NULL;
-      sk=sk->next) {
-	if (sk->num == num) return(1);
-  }
-  return(0);
-}
-
-
-unsigned short
-get_new_socknum(struct proto *prot, unsigned short base)
-{
-  static int start=0;
-
-  /*
-   * Used to cycle through the port numbers so the
-   * chances of a confused connection drop.
-   */
-  int i, j;
-  int best = 0;
-  int size = 32767; /* a big num. */
-  struct sock *sk;
-
-  if (base == 0) base = PROT_SOCK+1+(start % 1024);
-  if (base <= PROT_SOCK) {
-	base += PROT_SOCK+(start % 1024);
-  }
-
-  /* Now look through the entire array and try to find an empty ptr. */
-  for(i=0; i < SOCK_ARRAY_SIZE; i++) {
-	j = 0;
-	sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
-	while(sk != NULL) {
-		sk = sk->next;
-		j++;
-	}
-	if (j == 0) {
-		start =(i+1+start )%1024;
-		DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n",
-							i + base + 1, start));
-		return(i+base+1);
-	}
-	if (j < size) {
-		best = i;
-		size = j;
-	}
-  }
-
-  /* Now make sure the one we want is not in use. */
-  while(sk_inuse(prot, base +best+1)) {
-	best += SOCK_ARRAY_SIZE;
-  }
-  DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n",
-						best + base + 1, start));
-  return(best+base+1);
-}
-
-
-void
-put_sock(unsigned short num, struct sock *sk)
-{
-  struct sock *sk1;
-  struct sock *sk2;
-  int mask;
-
-  DPRINTF((DBG_INET, "put_sock(num = %d, sk = %X\n", num, sk));
-  sk->num = num;
-  sk->next = NULL;
-  num = num &(SOCK_ARRAY_SIZE -1);
-
-  /* We can't have an interupt re-enter here. */
-  cli();
-  if (sk->prot->sock_array[num] == NULL) {
-	sk->prot->sock_array[num] = sk;
-	sti();
-	return;
-  }
-  sti();
-  for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) {
-	if ((mask & sk->saddr) &&
-	    (mask & sk->saddr) != (mask & 0xffffffff)) {
-		mask = mask << 8;
-		break;
-	}
-  }
-  DPRINTF((DBG_INET, "mask = %X\n", mask));
-
-  cli();
-  sk1 = sk->prot->sock_array[num];
-  for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) {
-	if (!(sk2->saddr & mask)) {
-		if (sk2 == sk1) {
-			sk->next = sk->prot->sock_array[num];
-			sk->prot->sock_array[num] = sk;
-			sti();
-			return;
-		}
-		sk->next = sk2;
-		sk1->next= sk;
-		sti();
-		return;
-	}
-	sk1 = sk2;
-  }
-
-  /* Goes at the end. */
-  sk->next = NULL;
-  sk1->next = sk;
-  sti();
-}
-
-
-static void
-remove_sock(struct sock *sk1)
-{
-  struct sock *sk2;
-
-  DPRINTF((DBG_INET, "remove_sock(sk1=%X)\n", sk1));
-
-  if (!sk1->prot) {
-	printk("sock.c: remove_sock: sk1->prot == NULL\n");
-	return;
-  }
-
-  /* We can't have this changing out from under us. */
-  cli();
-  sk2 = sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)];
-  if (sk2 == sk1) {
-	sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)] = sk1->next;
-	sti();
-	return;
-  }
-
-  while(sk2 && sk2->next != sk1) {
-	sk2 = sk2->next;
-  }
-
-  if (sk2) {
-	sk2->next = sk1->next;
-	sti();
-	return;
-  }
-  sti();
-
-  if (sk1->num != 0) DPRINTF((DBG_INET, "remove_sock: sock not found.\n"));
-}
-
-
-void
-destroy_sock(struct sock *sk)
-{
-	struct sk_buff *skb;
-
-  	DPRINTF((DBG_INET, "destroying socket %X\n", sk));
-  	sk->inuse = 1;			/* just to be safe. */
-
-  	/* Incase it's sleeping somewhere. */
-  	if (!sk->dead) 
-  		sk->write_space(sk);
-
-  	remove_sock(sk);
-  
-  	/* Now we can no longer get new packets. */
-  	delete_timer(sk);
-
-	while ((skb = tcp_dequeue_partial(sk)) != NULL) {
-		IS_SKB(skb);
-		kfree_skb(skb, FREE_WRITE);
-	}
-
-	/* Cleanup up the write buffer. */
-  	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
-		IS_SKB(skb);
-		kfree_skb(skb, FREE_WRITE);
-  	}
-
-  	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
-	/*
-	 * This will take care of closing sockets that were
-	 * listening and didn't accept everything.
-	 */
-		if (skb->sk != NULL && skb->sk != sk) 
-		{
-			IS_SKB(skb);
-			skb->sk->dead = 1;
-			skb->sk->prot->close(skb->sk, 0);
-		}
-		IS_SKB(skb);
-		kfree_skb(skb, FREE_READ);
-	}
-
-  /* Now we need to clean up the send head. */
-	cli();
-	for(skb = sk->send_head; skb != NULL; )
-	{
-		struct sk_buff *skb2;
-
-		/*
-		 * We need to remove skb from the transmit queue,
-		 * or maybe the arp queue.
-		 */
-		if (skb->next  && skb->prev) {
-			printk("destroy_sock: unlinked skb\n");
-			IS_SKB(skb);
-			skb_unlink(skb);
-		}
-		skb->dev = NULL;
-		skb2 = skb->link3;
-		kfree_skb(skb, FREE_WRITE);
-		skb = skb2;
-	}
-	sk->send_head = NULL;
-	sti();
-
-  	/* And now the backlog. */
-  	while((skb=skb_dequeue(&sk->back_log))!=NULL) {
-		/* this should never happen. */
-		printk("cleaning back_log\n");
-		kfree_skb(skb, FREE_READ);
-	}
-
-  /* Now if it has a half accepted/ closed socket. */
-	if (sk->pair) 
-	{
-		sk->pair->dead = 1;
-		sk->pair->prot->close(sk->pair, 0);
-		sk->pair = NULL;
-  	}
-
-  /*
-   * Now if everything is gone we can free the socket
-   * structure, otherwise we need to keep it around until
-   * everything is gone.
-   */
-	  if (sk->dead && sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
-	  {
-		kfree_s((void *)sk,sizeof(*sk));
-	  } 
-	  else 
-	  {
-		/* this should never happen. */
-		/* actually it can if an ack has just been sent. */
-		DPRINTF((DBG_INET, "possible memory leak in socket = %X\n", sk));
-		sk->destroy = 1;
-		sk->ack_backlog = 0;
-		sk->inuse = 0;
-		reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
-  	}
-  	DPRINTF((DBG_INET, "leaving destroy_sock\n"));
-}
-
-
-static int
-inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-
-  switch(cmd) {
-	case F_SETOWN:
-		/*
-		 * This is a little restrictive, but it's the only
-		 * way to make sure that you can't send a sigurg to
-		 * another process.
-		 */
-		if (!suser() && current->pgrp != -arg &&
-				current->pid != arg) return(-EPERM);
-		sk->proc = arg;
-		return(0);
-	case F_GETOWN:
-		return(sk->proc);
-	default:
-		return(-EINVAL);
-  }
-}
-
-/*
- *	Set socket options on an inet socket.
- */
- 
-static int inet_setsockopt(struct socket *sock, int level, int optname,
-		    char *optval, int optlen)
-{
-  	struct sock *sk = (struct sock *) sock->data;  
-	if (level == SOL_SOCKET)
-		return sock_setsockopt(sk,level,optname,optval,optlen);
-	if (sk->prot->setsockopt==NULL)
-		return(-EOPNOTSUPP);
-	else
-		return sk->prot->setsockopt(sk,level,optname,optval,optlen);
-}
-
-
-
-
-static int inet_getsockopt(struct socket *sock, int level, int optname,
-		    char *optval, int *optlen)
-{
-  	struct sock *sk = (struct sock *) sock->data;  	
-  	if (level == SOL_SOCKET) 
-  		return sock_getsockopt(sk,level,optname,optval,optlen);
-  	if(sk->prot->getsockopt==NULL)  	
-  		return(-EOPNOTSUPP);
-  	else
-  		return sk->prot->getsockopt(sk,level,optname,optval,optlen);
-}
-
 /*
  *	This is meant for all protocols to use and covers goings on
  *	at the socket level. Everything here is generic.
@@ -491,9 +133,10 @@
 
 		case SO_DEBUG:	
 			sk->debug=val?1:0;
+			return 0;
 		case SO_DONTROUTE:
 			sk->localroute=val?1:0;
-			return(0);
+			return 0;
 		case SO_BROADCAST:
 			sk->broadcast=val?1:0;
 			return 0;
@@ -621,10 +264,13 @@
 			break;
 
 		case SO_TYPE:
+#if 0		
 			if (sk->prot == &tcp_prot) 
 				val = SOCK_STREAM;
 		  	else 
 		  		val = SOCK_DGRAM;
+#endif
+			val = sk->type;		  		
 			break;
 
 		case SO_ERROR:
@@ -661,866 +307,6 @@
 }
 
 
-
-
-static int
-inet_listen(struct socket *sock, int backlog)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-
-  /* We might as well re use these. */ 
-  sk->max_ack_backlog = backlog;
-  if (sk->state != TCP_LISTEN) {
-	sk->ack_backlog = 0;
-	sk->state = TCP_LISTEN;
-  }
-  return(0);
-}
-
-/*
- *	Default callbacks for user INET sockets. These just wake up
- *	the user owning the socket.
- */
-
-static void def_callback1(struct sock *sk)
-{
-	if(!sk->dead)
-		wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk,int len)
-{
-	if(!sk->dead)
-		wake_up_interruptible(sk->sleep);
-}
-
-
-static int
-inet_create(struct socket *sock, int protocol)
-{
-  struct sock *sk;
-  struct proto *prot;
-  int err;
-
-  sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
-  if (sk == NULL) 
-  	return(-ENOMEM);
-  sk->num = 0;
-  sk->reuse = 0;
-  switch(sock->type) {
-	case SOCK_STREAM:
-	case SOCK_SEQPACKET:
-		if (protocol && protocol != IPPROTO_TCP) {
-			kfree_s((void *)sk, sizeof(*sk));
-			return(-EPROTONOSUPPORT);
-		}
-		protocol = IPPROTO_TCP;
-		sk->no_check = TCP_NO_CHECK;
-		prot = &tcp_prot;
-		break;
-
-	case SOCK_DGRAM:
-		if (protocol && protocol != IPPROTO_UDP) {
-			kfree_s((void *)sk, sizeof(*sk));
-			return(-EPROTONOSUPPORT);
-		}
-		protocol = IPPROTO_UDP;
-		sk->no_check = UDP_NO_CHECK;
-		prot=&udp_prot;
-		break;
-      
-	case SOCK_RAW:
-		if (!suser()) {
-			kfree_s((void *)sk, sizeof(*sk));
-			return(-EPERM);
-		}
-		if (!protocol) {
-			kfree_s((void *)sk, sizeof(*sk));
-			return(-EPROTONOSUPPORT);
-		}
-		prot = &raw_prot;
-		sk->reuse = 1;
-		sk->no_check = 0;	/*
-					 * Doesn't matter no checksum is
-					 * preformed anyway.
-					 */
-		sk->num = protocol;
-		break;
-
-	case SOCK_PACKET:
-		if (!suser()) {
-			kfree_s((void *)sk, sizeof(*sk));
-			return(-EPERM);
-		}
-		if (!protocol) {
-			kfree_s((void *)sk, sizeof(*sk));
-			return(-EPROTONOSUPPORT);
-		}
-		prot = &packet_prot;
-		sk->reuse = 1;
-		sk->no_check = 0;	/* Doesn't matter no checksum is
-					 * preformed anyway.
-					 */
-		sk->num = protocol;
-		break;
-
-	default:
-		kfree_s((void *)sk, sizeof(*sk));
-		return(-ESOCKTNOSUPPORT);
-  }
-  sk->socket = sock;
-#ifdef CONFIG_TCP_NAGLE_OFF
-  sk->nonagle = 1;
-#else    
-  sk->nonagle = 0;
-#endif  
-  sk->type = sock->type;
-  sk->stamp.tv_sec=0;
-  sk->protocol = protocol;
-  sk->wmem_alloc = 0;
-  sk->rmem_alloc = 0;
-  sk->sndbuf = SK_WMEM_MAX;
-  sk->rcvbuf = SK_RMEM_MAX;
-  sk->pair = NULL;
-  sk->opt = NULL;
-  sk->write_seq = 0;
-  sk->acked_seq = 0;
-  sk->copied_seq = 0;
-  sk->fin_seq = 0;
-  sk->urg_seq = 0;
-  sk->urg_data = 0;
-  sk->proc = 0;
-  sk->rtt = TCP_WRITE_TIME << 3;
-  sk->rto = TCP_WRITE_TIME;
-  sk->mdev = 0;
-  sk->backoff = 0;
-  sk->packets_out = 0;
-  sk->cong_window = 1; /* start with only sending one packet at a time. */
-  sk->cong_count = 0;
-  sk->ssthresh = 0;
-  sk->max_window = 0;
-  sk->urginline = 0;
-  sk->intr = 0;
-  sk->linger = 0;
-  sk->destroy = 0;
-
-  sk->priority = 1;
-  sk->shutdown = 0;
-  sk->keepopen = 0;
-  sk->zapped = 0;
-  sk->done = 0;
-  sk->ack_backlog = 0;
-  sk->window = 0;
-  sk->bytes_rcv = 0;
-  sk->state = TCP_CLOSE;
-  sk->dead = 0;
-  sk->ack_timed = 0;
-  sk->partial = NULL;
-  sk->user_mss = 0;
-  sk->debug = 0;
-
-  /* this is how many unacked bytes we will accept for this socket.  */
-  sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
-
-  /* how many packets we should send before forcing an ack. 
-     if this is set to zero it is the same as sk->delay_acks = 0 */
-  sk->max_ack_backlog = 0;
-  sk->inuse = 0;
-  sk->delay_acks = 0;
-  skb_queue_head_init(&sk->write_queue);
-  skb_queue_head_init(&sk->receive_queue);
-  sk->mtu = 576;
-  sk->prot = prot;
-  sk->sleep = sock->wait;
-  sk->daddr = 0;
-  sk->saddr = ip_my_addr();
-  sk->err = 0;
-  sk->next = NULL;
-  sk->pair = NULL;
-  sk->send_tail = NULL;
-  sk->send_head = NULL;
-  sk->timeout = 0;
-  sk->broadcast = 0;
-  sk->localroute = 0;
-  sk->timer.data = (unsigned long)sk;
-  sk->timer.function = &net_timer;
-  skb_queue_head_init(&sk->back_log);
-  sk->blog = 0;
-  sock->data =(void *) sk;
-  sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-  sk->dummy_th.res1=0;
-  sk->dummy_th.res2=0;
-  sk->dummy_th.urg_ptr = 0;
-  sk->dummy_th.fin = 0;
-  sk->dummy_th.syn = 0;
-  sk->dummy_th.rst = 0;
-  sk->dummy_th.psh = 0;
-  sk->dummy_th.ack = 0;
-  sk->dummy_th.urg = 0;
-  sk->dummy_th.dest = 0;
-
-  sk->ip_tos=0;
-  sk->ip_ttl=64;
-  	
-  sk->state_change = def_callback1;
-  sk->data_ready = def_callback2;
-  sk->write_space = def_callback1;
-  sk->error_report = def_callback1;
-
-  if (sk->num) {
-	/*
-	 * It assumes that any protocol which allows
-	 * the user to assign a number at socket
-	 * creation time automatically
-	 * shares.
-	 */
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-
-  if (sk->prot->init) {
-	err = sk->prot->init(sk);
-	if (err != 0) {
-		destroy_sock(sk);
-		return(err);
-	}
-  }
-  return(0);
-}
-
-
-static int
-inet_dup(struct socket *newsock, struct socket *oldsock)
-{
-  return(inet_create(newsock,
-		   ((struct sock *)(oldsock->data))->protocol));
-}
-
-
-/* The peer socket should always be NULL. */
-static int
-inet_release(struct socket *sock, struct socket *peer)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-  if (sk == NULL) return(0);
-
-  DPRINTF((DBG_INET, "inet_release(sock = %X, peer = %X)\n", sock, peer));
-  sk->state_change(sk);
-
-  /* Start closing the connection.  This may take a while. */
-  /*
-   * If linger is set, we don't return until the close
-   * is complete.  Other wise we return immediately. The
-   * actually closing is done the same either way.
-   */
-  if (sk->linger == 0) {
-	sk->prot->close(sk,0);
-	sk->dead = 1;
-  } else {
-	DPRINTF((DBG_INET, "sk->linger set.\n"));
-	sk->prot->close(sk, 0);
-	cli();
-	if (sk->lingertime)
-		current->timeout = jiffies + HZ*sk->lingertime;
-	while(sk->state != TCP_CLOSE && current->timeout>0) {
-		interruptible_sleep_on(sk->sleep);
-		if (current->signal & ~current->blocked) {
-			break;
-#if 0
-			/* not working now - closes can't be restarted */
-			sti();
-			current->timeout=0;
-			return(-ERESTARTSYS);
-#endif
-		}
-	}
-	current->timeout=0;
-	sti();
-	sk->dead = 1;
-  }
-  sk->inuse = 1;
-
-  /* This will destroy it. */
-  release_sock(sk);
-  sock->data = NULL;
-  DPRINTF((DBG_INET, "inet_release returning\n"));
-  return(0);
-}
-
-
-/* this needs to be changed to dissallow
-   the rebinding of sockets.   What error
-   should it return? */
-
-static int
-inet_bind(struct socket *sock, struct sockaddr *uaddr,
-	       int addr_len)
-{
-  struct sockaddr_in addr;
-  struct sock *sk, *sk2;
-  unsigned short snum;
-  int err;
-  int chk_addr_ret;
-
-  sk = (struct sock *) sock->data;
-  /* check this error. */
-  if (sk->state != TCP_CLOSE) return(-EIO);
-  if (sk->num != 0) return(-EINVAL);
-
-  err=verify_area(VERIFY_READ, uaddr, addr_len);
-  if(err)
-  	return err;
-  memcpy_fromfs(&addr, uaddr, min(sizeof(addr), addr_len));
-
-  snum = ntohs(addr.sin_port);
-  DPRINTF((DBG_INET, "bind sk =%X to port = %d\n", sk, snum));
-  sk = (struct sock *) sock->data;
-
-  /*
-   * We can't just leave the socket bound wherever it is, it might
-   * be bound to a privileged port. However, since there seems to
-   * be a bug here, we will leave it if the port is not privileged.
-   */
-  if (snum == 0) {
-	snum = get_new_socknum(sk->prot, 0);
-  }
-  if (snum < PROT_SOCK && !suser()) return(-EACCES);
-
-  chk_addr_ret = ip_chk_addr(addr.sin_addr.s_addr);
-  if (addr.sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR)
-  	return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
-  	
-  if (chk_addr_ret || addr.sin_addr.s_addr == 0)
-					sk->saddr = addr.sin_addr.s_addr;
-
-  DPRINTF((DBG_INET, "sock_array[%d] = %X:\n", snum &(SOCK_ARRAY_SIZE -1),
-	  		sk->prot->sock_array[snum &(SOCK_ARRAY_SIZE -1)]));
-
-  /* Make sure we are allowed to bind here. */
-  cli();
-outside_loop:
-  for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-					sk2 != NULL; sk2 = sk2->next) {
-#if 	1	/* should be below! */
-	if (sk2->num != snum) continue;
-/*	if (sk2->saddr != sk->saddr) continue; */
-#endif
-	if (sk2->dead) {
-		destroy_sock(sk2);
-		goto outside_loop;
-	}
-	if (!sk->reuse) {
-		sti();
-		return(-EADDRINUSE);
-	}
-	if (sk2->num != snum) continue;		/* more than one */
-	if (sk2->saddr != sk->saddr) continue;	/* socket per slot ! -FB */
-	if (!sk2->reuse) {
-		sti();
-		return(-EADDRINUSE);
-	}
-  }
-  sti();
-
-  remove_sock(sk);
-  put_sock(snum, sk);
-  sk->dummy_th.source = ntohs(sk->num);
-  sk->daddr = 0;
-  sk->dummy_th.dest = 0;
-  return(0);
-}
-
-
-static int
-inet_connect(struct socket *sock, struct sockaddr * uaddr,
-		  int addr_len, int flags)
-{
-  struct sock *sk;
-  int err;
-
-  sock->conn = NULL;
-  sk = (struct sock *) sock->data;
-
-  if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
-  {
-	sock->state = SS_CONNECTED;
-  /* Connection completing after a connect/EINPROGRESS/select/connect */
-	return 0;	/* Rock and roll */
-  }
-
-  if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP &&
-  	(flags & O_NONBLOCK))
-  	return -EALREADY;	/* Connecting is currently in progress */
-  	
-  if (sock->state != SS_CONNECTING) {
-	/* We may need to bind the socket. */
-	if (sk->num == 0) {
-		sk->num = get_new_socknum(sk->prot, 0);
-		if (sk->num == 0) 
-			return(-EAGAIN);
-		put_sock(sk->num, sk);
-		sk->dummy_th.source = htons(sk->num);
-	}
-
-	if (sk->prot->connect == NULL) 
-		return(-EOPNOTSUPP);
-  
-	err = sk->prot->connect(sk, (struct sockaddr_in *)uaddr, addr_len);
-	if (err < 0) return(err);
-  
-	sock->state = SS_CONNECTING;
-  }
-
-  if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK)) 
-  	return(-EINPROGRESS);
-
-  cli(); /* avoid the race condition */
-  while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
-  {
-	interruptible_sleep_on(sk->sleep);
-	if (current->signal & ~current->blocked) {
-		sti();
-		return(-ERESTARTSYS);
-	}
-	/* This fixes a nasty in the tcp/ip code. There is a hideous hassle with
-	   icmp error packets wanting to close a tcp or udp socket. */
-	if(sk->err && sk->protocol == IPPROTO_TCP)
-	{
-		sti();
-		sock->state = SS_UNCONNECTED;
-		err = -sk->err;
-		sk->err=0;
-		return err; /* set by tcp_err() */
-	}
-  }
-  sti();
-  sock->state = SS_CONNECTED;
-
-  if (sk->state != TCP_ESTABLISHED && sk->err) {
-	sock->state = SS_UNCONNECTED;
-	err=sk->err;
-	sk->err=0;
-	return(-err);
-  }
-  return(0);
-}
-
-
-static int
-inet_socketpair(struct socket *sock1, struct socket *sock2)
-{
-  return(-EOPNOTSUPP);
-}
-
-
-static int
-inet_accept(struct socket *sock, struct socket *newsock, int flags)
-{
-  struct sock *sk1, *sk2;
-  int err;
-
-  sk1 = (struct sock *) sock->data;
-
-  /*
-   * We've been passed an extra socket.
-   * We need to free it up because the tcp module creates
-   * it's own when it accepts one.
-   */
-  if (newsock->data)
-  {
-  	struct sock *sk=(struct sock *)newsock->data;
-  	newsock->data=NULL;
-  	sk->dead = 1;
-  	destroy_sock(sk);
-  }
-  
-  if (sk1->prot->accept == NULL) return(-EOPNOTSUPP);
-
-  /* Restore the state if we have been interrupted, and then returned. */
-  if (sk1->pair != NULL ) {
-	sk2 = sk1->pair;
-	sk1->pair = NULL;
-  } else {
-	sk2 = sk1->prot->accept(sk1,flags);
-	if (sk2 == NULL) {
-		if (sk1->err <= 0)
-			printk("Warning sock.c:sk1->err <= 0.  Returning non-error.\n");
-		err=sk1->err;
-		sk1->err=0;
-		return(-err);
-	}
-  }
-  newsock->data = (void *)sk2;
-  sk2->sleep = newsock->wait;
-  newsock->conn = NULL;
-  if (flags & O_NONBLOCK) return(0);
-
-  cli(); /* avoid the race. */
-  while(sk2->state == TCP_SYN_RECV) {
-	interruptible_sleep_on(sk2->sleep);
-	if (current->signal & ~current->blocked) {
-		sti();
-		sk1->pair = sk2;
-		sk2->sleep = NULL;
-		newsock->data = NULL;
-		return(-ERESTARTSYS);
-	}
-  }
-  sti();
-
-  if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) {
-
-	err = -sk2->err;
-	sk2->err=0;
-	destroy_sock(sk2);
-	newsock->data = NULL;
-	return(err);
-  }
-  newsock->state = SS_CONNECTED;
-  return(0);
-}
-
-
-static int
-inet_getname(struct socket *sock, struct sockaddr *uaddr,
-		 int *uaddr_len, int peer)
-{
-  struct sockaddr_in sin;
-  struct sock *sk;
-  int len;
-  int err;
-  
-  
-  err = verify_area(VERIFY_WRITE,uaddr_len,sizeof(long));
-  if(err)
-  	return err;
-  	
-  len=get_fs_long(uaddr_len);
-  
-  err = verify_area(VERIFY_WRITE, uaddr, len);
-  if(err)
-  	return err;
-  	
-  /* Check this error. */
-  if (len < sizeof(sin)) return(-EINVAL);
-
-  sin.sin_family = AF_INET;
-  sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
-  if (peer) {
-	if (!tcp_connected(sk->state)) return(-ENOTCONN);
-	sin.sin_port = sk->dummy_th.dest;
-	sin.sin_addr.s_addr = sk->daddr;
-  } else {
-	sin.sin_port = sk->dummy_th.source;
-	if (sk->saddr == 0) sin.sin_addr.s_addr = ip_my_addr();
-	  else sin.sin_addr.s_addr = sk->saddr;
-  }
-  len = sizeof(sin);
-/*  verify_area(VERIFY_WRITE, uaddr, len); NOW DONE ABOVE */
-  memcpy_tofs(uaddr, &sin, sizeof(sin));
-/*  verify_area(VERIFY_WRITE, uaddr_len, sizeof(len)); NOW DONE ABOVE */
-  put_fs_long(len, uaddr_len);
-  return(0);
-}
-
-
-static int
-inet_read(struct socket *sock, char *ubuf, int size, int noblock)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-  return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock,0));
-}
-
-
-static int
-inet_recv(struct socket *sock, void *ubuf, int size, int noblock,
-	  unsigned flags)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-  return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock, flags));
-}
-
-
-static int
-inet_write(struct socket *sock, char *ubuf, int size, int noblock)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-  if (sk->shutdown & SEND_SHUTDOWN) {
-	send_sig(SIGPIPE, current, 1);
-	return(-EPIPE);
-  }
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-
-  return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, 0));
-}
-
-
-static int
-inet_send(struct socket *sock, void *ubuf, int size, int noblock, 
-	       unsigned flags)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-  if (sk->shutdown & SEND_SHUTDOWN) {
-	send_sig(SIGPIPE, current, 1);
-	return(-EPIPE);
-  }
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-
-  return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));
-}
-
-
-static int
-inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, 
-	    unsigned flags, struct sockaddr *sin, int addr_len)
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-  if (sk->shutdown & SEND_SHUTDOWN) {
-	send_sig(SIGPIPE, current, 1);
-	return(-EPIPE);
-  }
-
-  if (sk->prot->sendto == NULL) return(-EOPNOTSUPP);
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-
-  return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, 
-			   (struct sockaddr_in *)sin, addr_len));
-}
-
-
-static int
-inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, 
-		   unsigned flags, struct sockaddr *sin, int *addr_len )
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-
-  if (sk->prot->recvfrom == NULL) return(-EOPNOTSUPP);
-
-  /* We may need to bind the socket. */
-  if (sk->num == 0) {
-	sk->num = get_new_socknum(sk->prot, 0);
-	if (sk->num == 0) return(-EAGAIN);
-	put_sock(sk->num, sk);
-	sk->dummy_th.source = ntohs(sk->num);
-  }
-
-  return(sk->prot->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags,
-			     (struct sockaddr_in*)sin, addr_len));
-}
-
-
-static int
-inet_shutdown(struct socket *sock, int how)
-{
-  struct sock *sk;
-
-  /*
-   * This should really check to make sure
-   * the socket is a TCP socket.
-   */
-  how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
-		       1->2 bit 2 snds.
-		       2->3 */
-  if (how & ~SHUTDOWN_MASK) return(-EINVAL);
-  sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
-  if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
-						sock->state = SS_CONNECTED;
-
-  if (!tcp_connected(sk->state)) return(-ENOTCONN);
-  sk->shutdown |= how;
-  if (sk->prot->shutdown) sk->prot->shutdown(sk, how);
-  return(0);
-}
-
-
-static int
-inet_select(struct socket *sock, int sel_type, select_table *wait )
-{
-  struct sock *sk;
-
-  sk = (struct sock *) sock->data;
-
-  if (sk->prot->select == NULL) {
-	DPRINTF((DBG_INET, "select on non-selectable socket.\n"));
-	return(0);
-  }
-  return(sk->prot->select(sk, sel_type, wait));
-}
-
-
-static int
-inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-  struct sock *sk;
-  int err;
-
-  DPRINTF((DBG_INET, "INET: in inet_ioctl\n"));
-  sk = NULL;
-  if (sock && (sk = (struct sock *) sock->data) == NULL) {
-	printk("AF_INET: Warning: sock->data = NULL: %d\n" , __LINE__);
-	return(0);
-  }
-
-  switch(cmd) {
-	case FIOSETOWN:
-	case SIOCSPGRP:
-		err=verify_area(VERIFY_READ,(int *)arg,sizeof(long));
-		if(err)
-			return err;
-		if (sk)
-			sk->proc = get_fs_long((int *) arg);
-		return(0);
-	case FIOGETOWN:
-	case SIOCGPGRP:
-		if (sk) {
-			err=verify_area(VERIFY_WRITE,(void *) arg, sizeof(long));
-			if(err)
-				return err;
-			put_fs_long(sk->proc,(int *)arg);
-		}
-		return(0);
-		
-	case SIOCGSTAMP:
-		if (sk)
-		{
-			if(sk->stamp.tv_sec==0)
-				return -ENOENT;
-			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval));
-			if(err)
-				return err;
-			memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval));
-			return 0;
-		}
-		return -EINVAL;
-		
-#if 0	/* FIXME: */
-	case SIOCATMARK:
-		printk("AF_INET: ioctl(SIOCATMARK, 0x%08X)\n",(void *) arg);
-		return(-EINVAL);
-#endif
-
-	case DDIOCSDBG:
-		return(dbg_ioctl((void *) arg, DBG_INET));
-
-	case SIOCADDRT: case SIOCADDRTOLD:
-	case SIOCDELRT: case SIOCDELRTOLD:
-		return(ip_rt_ioctl(cmd,(void *) arg));
-
-	case SIOCDARP:
-	case SIOCGARP:
-	case SIOCSARP:
-		return(arp_ioctl(cmd,(void *) arg));
-
-	case SIOCDRARP:
-	case SIOCGRARP:
-	case SIOCSRARP:
-		return(rarp_ioctl(cmd,(void *) arg));
-
-	case SIOCGIFCONF:
-	case SIOCGIFFLAGS:
-	case SIOCSIFFLAGS:
-	case SIOCGIFADDR:
-	case SIOCSIFADDR:
-	case SIOCGIFDSTADDR:
-	case SIOCSIFDSTADDR:
-	case SIOCGIFBRDADDR:
-	case SIOCSIFBRDADDR:
-	case SIOCGIFNETMASK:
-	case SIOCSIFNETMASK:
-	case SIOCGIFMETRIC:
-	case SIOCSIFMETRIC:
-	case SIOCGIFMEM:
-	case SIOCSIFMEM:
-	case SIOCGIFMTU:
-	case SIOCSIFMTU:
-	case SIOCSIFLINK:
-	case SIOCGIFHWADDR:
-	case SIOCSIFHWADDR:
-	case OLD_SIOCGIFHWADDR:
-		return(dev_ioctl(cmd,(void *) arg));
-
-	default:
-		if (!sk || !sk->prot->ioctl) return(-EINVAL);
-		return(sk->prot->ioctl(sk, cmd, arg));
-  }
-  /*NOTREACHED*/
-  return(0);
-}
-
-
 struct sk_buff *
 sock_wmalloc(struct sock *sk, unsigned long size, int force,
 	     int priority)
@@ -1535,8 +321,6 @@
 		}
 		return c;
 	}
-	DPRINTF((DBG_INET, "sock_wmalloc(%X,%d,%d,%d) returning NULL\n",
-						sk, size, force, priority));
 	return(NULL);
   }
   return(alloc_skb(size, priority));
@@ -1556,8 +340,6 @@
 		}
 		return(c);
 	}
-	DPRINTF((DBG_INET, "sock_rmalloc(%X,%d,%d,%d) returning NULL\n",
-						sk,size,force, priority));
 	return(NULL);
   }
   return(alloc_skb(size, priority));
@@ -1594,8 +376,6 @@
 void
 sock_wfree(struct sock *sk, void *mem, unsigned long size)
 {
-  DPRINTF((DBG_INET, "sock_wfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
-
   IS_SKB(mem);
   kfree_skbmem(mem, size);
   if (sk) {
@@ -1603,10 +383,6 @@
 
 	/* In case it might be waiting for more memory. */
 	if (!sk->dead) sk->write_space(sk);
-	if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
-		DPRINTF((DBG_INET,
-			"recovered lost memory, sock = %X\n", sk));
-	}
 	return;
   }
 }
@@ -1615,60 +391,11 @@
 void
 sock_rfree(struct sock *sk, void *mem, unsigned long size)
 {
-  DPRINTF((DBG_INET, "sock_rfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
   IS_SKB(mem);
   kfree_skbmem(mem, size);
   if (sk) {
 	sk->rmem_alloc -= size;
-	if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
-		DPRINTF((DBG_INET,
-			"recovered lot memory, sock = %X\n", sk));
-	}
-  }
-}
-
-
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everyhting is assumed to be in net order.
- */
-struct sock *get_sock(struct proto *prot, unsigned short num,
-				unsigned long raddr,
-				unsigned short rnum, unsigned long laddr)
-{
-  struct sock *s;
-  unsigned short hnum;
-
-  hnum = ntohs(num);
-  DPRINTF((DBG_INET, "get_sock(prot=%X, num=%d, raddr=%X, rnum=%d, laddr=%X)\n",
-	  prot, num, raddr, rnum, laddr));
-
-  /*
-   * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-   * than a prime unless 3 or more sockets end up using the same
-   * array entry.  This should not be a problem because most
-   * well known sockets don't overlap that much, and for
-   * the other ones, we can just be careful about picking our
-   * socket number when we choose an arbitrary one.
-   */
-  for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-      s != NULL; s = s->next) 
-  {
-	if (s->num != hnum) 
-		continue;
-	if(s->dead && (s->state == TCP_CLOSE))
-		continue;
-	if(prot == &udp_prot)
-		return s;
-	if(ip_addr_match(s->daddr,raddr)==0)
-		continue;
-	if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) 
-		continue;
-	if(ip_addr_match(s->saddr,laddr) == 0)
-		continue;
-	return(s);
   }
-  return(NULL);
 }
 
 
@@ -1680,170 +407,25 @@
 	return;
 
   if (sk->blog) return;
-
+#ifdef CONFIG_INET
   /* See if we have any packets built up. */
   sk->inuse = 1;
   while((skb = skb_dequeue(&sk->back_log)) != NULL) {
 	sk->blog = 1;
-	DPRINTF((DBG_INET, "release_sock: skb = %X:\n", skb));
 	if (sk->prot->rcv) sk->prot->rcv(skb, skb->dev, sk->opt,
 					 skb->saddr, skb->len, skb->daddr, 1,
 					/* Only used for/by raw sockets. */
 					(struct inet_protocol *)sk->pair); 
   }
+#endif  
   sk->blog = 0;
   sk->inuse = 0;
+#ifdef CONFIG_INET  
   if (sk->dead && sk->state == TCP_CLOSE) {
 	/* Should be about 2 rtt's */
 	reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
   }
-}
-
-
-static int
-inet_fioctl(struct inode *inode, struct file *file,
-	 unsigned int cmd, unsigned long arg)
-{
-  int minor, ret;
-
-  /* Extract the minor number on which we work. */
-  minor = MINOR(inode->i_rdev);
-
-  /* Now dispatch on the minor device. */
-  switch(minor) {
-	case 0:		/* INET */
-		ret = inet_ioctl(NULL, cmd, arg);
-		break;
-	case 1:		/* IP */
-		ret = ip_ioctl(NULL, cmd, arg);
-		break;
-	case 2:		/* ICMP */
-		ret = icmp_ioctl(NULL, cmd, arg);
-		break;
-	case 3:		/* TCP */
-		ret = tcp_ioctl(NULL, cmd, arg);
-		break;
-	case 4:		/* UDP */
-		ret = udp_ioctl(NULL, cmd, arg);
-		break;
-	default:
-		ret = -ENODEV;
-  }
-
-  return(ret);
+#endif  
 }
 
 
-
-
-static struct file_operations inet_fops = {
-  NULL,		/* LSEEK	*/
-  NULL,		/* READ		*/
-  NULL,		/* WRITE	*/
-  NULL,		/* READDIR	*/
-  NULL,		/* SELECT	*/
-  inet_fioctl,	/* IOCTL	*/
-  NULL,		/* MMAP		*/
-  NULL,		/* OPEN		*/
-  NULL		/* CLOSE	*/
-};
-
-
-static struct proto_ops inet_proto_ops = {
-  AF_INET,
-
-  inet_create,
-  inet_dup,
-  inet_release,
-  inet_bind,
-  inet_connect,
-  inet_socketpair,
-  inet_accept,
-  inet_getname, 
-  inet_read,
-  inet_write,
-  inet_select,
-  inet_ioctl,
-  inet_listen,
-  inet_send,
-  inet_recv,
-  inet_sendto,
-  inet_recvfrom,
-  inet_shutdown,
-  inet_setsockopt,
-  inet_getsockopt,
-  inet_fcntl,
-};
-
-extern unsigned long seq_offset;
-
-/*
- *	Called by ddi.c on kernel startup.  
- */
- 
-void inet_proto_init(struct ddi_proto *pro)
-{
-	struct inet_protocol *p;
-	int i;
-
-	printk("Swansea University Computer Society NET3.012\n");
-	/*
-	 *	Set up our UNIX VFS major device. (compatibility)
-	 */
-
-	if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) 
-	{
-		printk("%s: cannot register major device %d!\n",
-					pro->name, AF_INET_MAJOR);
-		return;
-	}
-
-  /*
-   *	Tell SOCKET that we are alive... 
-   */
-   
-  	(void) sock_register(inet_proto_ops.family, &inet_proto_ops);
-
-  	seq_offset = CURRENT_TIME*250;
-
-	/*
-	 *	Add all the protocols. 
-	 */
-	 
-	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-	{
-		tcp_prot.sock_array[i] = NULL;
-		udp_prot.sock_array[i] = NULL;
-		raw_prot.sock_array[i] = NULL;
-  	}
-
-	printk("IP Protocols: ");
-	for(p = inet_protocol_base; p != NULL;) 
-	{
-		struct inet_protocol *tmp;
-	
-		tmp = (struct inet_protocol *) p->next;
-		inet_add_protocol(p);
-		printk("%s%s",p->name,tmp?", ":"\n");
-		p = tmp;
-	}
-
-	/*
-	 *	Initialize the DEV module. 
-	 */	 
-	dev_init();
-	/*
-	 *	Set the ARP module up
-	 */
-	arp_init();
-  	/*
-  	 *	Set the IP module up
-  	 */
-	ip_init();
-  
-	/*
-	 *	Initialize the "Buffer Head" pointers. 
-	 */
-	 
-	bh_base[INET_BH].routine = inet_bh;
-}
diff -u --recursive --new-file v1.1.12/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.12/linux/net/inet/tcp.c	Mon May 23 12:14:28 1994
+++ linux/net/inet/tcp.c	Mon May 23 11:45:53 1994
@@ -69,6 +69,8 @@
  *					completely
  *		Gerhard Koerting:	Fixed some missing timer handling
  *		Matthew Dillon  :	Reworked TCP machine states as per RFC
+ *		Gerhard Koerting:	PC/TCP workarounds
+ *		Adam Caldwell	:	Assorted timer/timing errors
  *
  *
  * To Fix:
@@ -135,6 +137,7 @@
 #include "tcp.h"
 #include <linux/skbuff.h>
 #include "sock.h"
+#include "route.h"
 #include <linux/errno.h>
 #include <linux/timer.h>
 #include <asm/system.h>
@@ -154,31 +157,6 @@
 }
 
 
-static void __print_th(struct tcphdr *th)
-{
-	unsigned char *ptr;
-
-	printk("TCP header:\n");
-	printk("    source=%d, dest=%d, seq =%ld, ack_seq = %ld\n",
-		ntohs(th->source), ntohs(th->dest),
-		ntohl(th->seq), ntohl(th->ack_seq));
-	printk("    fin=%d, syn=%d, rst=%d, psh=%d, ack=%d, urg=%d res1=%d res2=%d\n",
-		th->fin, th->syn, th->rst, th->psh, th->ack,
-		th->urg, th->res1, th->res2);
-	printk("    window = %d, check = %d urg_ptr = %d\n",
-		ntohs(th->window), ntohs(th->check), ntohs(th->urg_ptr));
-	printk("    doff = %d\n", th->doff);
-	ptr =(unsigned char *)(th + 1);
-	printk("    options = %d %d %d %d\n", ptr[0], ptr[1], ptr[2], ptr[3]);
-}
-
-static inline void print_th(struct tcphdr *th)
-{
-	if (inet_debug == DBG_TCP)
-		__print_th(th);
-}
-
-
 /* This routine picks a TCP windows for a socket based on
    the following constraints
    
@@ -197,7 +175,7 @@
 static int tcp_select_window(struct sock *sk)
 {
 	int new_window = sk->prot->rspace(sk);
-
+	
 /*
  * two things are going on here.  First, we don't ever offer a
  * window less than min(sk->mss, MAX_WINDOW/2).  This is the
@@ -268,12 +246,9 @@
   
   header+=4*iph->ihl;
    
-  DPRINTF((DBG_TCP, "TCP: tcp_err(%d, hdr=%X, daddr=%X saddr=%X, protocol=%X)\n",
-					err, header, daddr, saddr, protocol));
 
   th =(struct tcphdr *)header;
   sk = get_sock(&tcp_prot, th->source/*dest*/, daddr, th->dest/*source*/, saddr);
-  print_th(th);
 
   if (sk == NULL) return;
   
@@ -294,7 +269,6 @@
 	return;
   }
 
-  DPRINTF((DBG_TCP, "TCP: icmp_err got error\n"));
   sk->err = icmp_err_convert[err & 0xff].errno;
 
   /*
@@ -326,7 +300,6 @@
   int sum;
   unsigned long flags;
 
-  DPRINTF((DBG_TCP, "tcp_readable(sk=%X)\n", sk));
   if(sk && sk->debug)
   	printk("tcp_readable: %p - ",sk);
 
@@ -362,7 +335,6 @@
       (sk->urg_seq - sk->copied_seq) <= (counted - sk->copied_seq))
 	amount--;		/* don't count urg data */
   restore_flags(flags);
-  DPRINTF((DBG_TCP, "tcp readable returning %d bytes\n", amount));
   if(sk->debug)
   	printk("got %lu bytes.\n",amount);
   return(amount);
@@ -377,9 +349,6 @@
 static int
 tcp_select(struct sock *sk, int sel_type, select_table *wait)
 {
-  DPRINTF((DBG_TCP, "tcp_select(sk=%X, sel_type = %d, wait = %X)\n",
-	  					sk, sel_type, wait));
-
   sk->inuse = 1;
   switch(sel_type) {
 	case SEL_IN:
@@ -417,9 +386,6 @@
 	case SEL_OUT:
 		select_wait(sk->sleep, wait);
 		if (sk->shutdown & SEND_SHUTDOWN) {
-			DPRINTF((DBG_TCP,
-				"write select on shutdown socket.\n"));
-
 			/* FIXME: should this return an error? */
 			release_sock(sk);
 			return(0);
@@ -437,13 +403,6 @@
 			    sk->state == TCP_SYN_SENT) return(0);
 			return(1);
 		}
-		DPRINTF((DBG_TCP,
-			"tcp_select: sleeping on write sk->wmem_alloc = %d, "
-			"sk->packets_out = %d\n"
-			"sk->write_seq = %u, sk->window_seq=%u\n", 
-				sk->wmem_alloc, sk->packets_out,
-				sk->write_seq, sk->window_seq));
-
 		release_sock(sk);
 		return(0);
 	case SEL_EX:
@@ -465,10 +424,7 @@
 tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
   int err;
-  DPRINTF((DBG_TCP, "tcp_ioctl(sk=%X, cmd = %d, arg=%X)\n", sk, cmd, arg));
   switch(cmd) {
-	case DDIOCSDBG:
-		return(dbg_ioctl((void *) arg, DBG_TCP));
 
 	case TIOCINQ:
 #ifdef FIXME	/* FIXME: */
@@ -482,7 +438,6 @@
 			sk->inuse = 1;
 			amount = tcp_readable(sk);
 			release_sock(sk);
-			DPRINTF((DBG_TCP, "returning %d\n", amount));
 			err=verify_area(VERIFY_WRITE,(void *)arg,
 						   sizeof(unsigned long));
 			if(err)
@@ -528,7 +483,6 @@
   unsigned long sum;
    
   if (saddr == 0) saddr = ip_my_addr();
-  print_th(th);
   __asm__("\t addl %%ecx,%%ebx\n"
 	  "\t adcl %%edx,%%ebx\n"
 	  "\t adcl $0, %%ebx\n"
@@ -625,10 +579,6 @@
 	if (after(skb->h.seq, sk->window_seq) ||
 	    (sk->retransmits && sk->timeout == TIME_WRITE) ||
 	     sk->packets_out >= sk->cong_window) {
-		DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n",
-					sk->cong_window, sk->packets_out));
-		DPRINTF((DBG_TCP, "sk->write_seq = %d, sk->window_seq = %d\n",
-					sk->write_seq, sk->window_seq));
 		if (skb->next != NULL) {
 			printk("tcp_send_partial: next != NULL\n");
 			skb_unlink(skb);
@@ -718,8 +668,6 @@
 		{
 			reset_timer(sk, TIME_WRITE, 10);
 		}
-  		if (inet_debug == DBG_SLIP)
-  			printk("\rtcp_ack: malloc failed\n");
 		return;
 	}
 
@@ -735,8 +683,6 @@
 	{
   		buff->free=1;
 		sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
-		if (inet_debug == DBG_SLIP)
-			printk("\rtcp_ack: build_header failed\n");
 		return;
 	}
 	buff->len += tmp;
@@ -823,9 +769,6 @@
 	struct proto *prot;
 	struct device *dev = NULL;
 
-	DPRINTF((DBG_TCP, "tcp_write(sk=%X, from=%X, len=%d, nonblock=%d, flags=%X)\n",
-					sk, from, len, nonblock, flags));
-
 	sk->inuse=1;
 	prot = sk->prot;
 	while(len > 0) 
@@ -874,7 +817,6 @@
 			if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) 
 			{
 				release_sock(sk);
-				DPRINTF((DBG_TCP, "tcp_write: return 1\n"));
 				if (copied) 
 					return(copied);
 
@@ -895,7 +837,6 @@
 			if (nonblock || copied) 
 			{
 				release_sock(sk);
-				DPRINTF((DBG_TCP, "tcp_write: return 2\n"));
 				if (copied) 
 					return(copied);
 				return(-EAGAIN);
@@ -911,7 +852,6 @@
 				if (current->signal & ~current->blocked) 
 				{
 					sti();
-					DPRINTF((DBG_TCP, "tcp_write: return 3\n"));
 					if (copied) 
 						return(copied);
 					return(-ERESTARTSYS);
@@ -1027,7 +967,6 @@
 			if (nonblock /* || copied */) 
 			{
 				release_sock(sk);
-				DPRINTF((DBG_TCP, "tcp_write: return 4\n"));
 				if (copied) 
 					return(copied);
 				return(-EAGAIN);
@@ -1051,7 +990,6 @@
 				if (current->signal & ~current->blocked) 
 				{
 					sti();
-					DPRINTF((DBG_TCP, "tcp_write: return 5\n"));
 					if (copied) 
 						return(copied);
 					return(-ERESTARTSYS);
@@ -1080,7 +1018,6 @@
 		{
 			prot->wfree(sk, skb->mem_addr, skb->mem_len);
 			release_sock(sk);
-			DPRINTF((DBG_TCP, "tcp_write: return 6\n"));
 			if (copied) 
 				return(copied);
 			return(tmp);
@@ -1094,7 +1031,6 @@
 		{
 			prot->wfree(sk, skb->mem_addr, skb->mem_len);
 			release_sock(sk);
-			DPRINTF((DBG_TCP, "tcp_write: return 7\n"));
 			if (copied) 
 				return(copied);
 			return(tmp);
@@ -1142,7 +1078,6 @@
   		tcp_send_partial(sk);
 
 	release_sock(sk);
-	DPRINTF((DBG_TCP, "tcp_write: return 8\n"));
 	return(copied);
 }
 
@@ -1176,7 +1111,6 @@
 	struct tcphdr *t1;
 	struct sk_buff *buff;
 
-	DPRINTF((DBG_TCP, "in tcp read wakeup\n"));
 	if (!sk->ack_backlog) 
 		return;
 
@@ -1284,9 +1218,6 @@
 	 * TCP_WINDOW_DIFF.
 	 */
 
-	DPRINTF((DBG_TCP, "sk->window left = %d, sk->prot->rspace(sk)=%d\n",
-			sk->window - sk->bytes_rcv, sk->prot->rspace(sk)));
-
 	if(sk->debug)
 		printk("sk->rspace = %lu, was %d\n", sk->prot->rspace(sk),
   					    left);
@@ -1396,18 +1327,7 @@
 	unsigned long peek_seq;
 	unsigned long *seq;
 	unsigned long used;
-	int err;
-
-	if (len == 0)
-		return 0;
-
-	if (len < 0)
-		return -EINVAL;
 
-	err = verify_area(VERIFY_WRITE, to, len);
-	if (err)
-		return err;
-
 	/* This error should be checked. */
 	if (sk->state == TCP_LISTEN)
 		return -ENOTCONN;
@@ -1527,7 +1447,6 @@
 	/* Clean up data we have read: This will do ACK frames */
 	cleanup_rbuf(sk);
 	release_sock(sk);
-	DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied));
 	return copied;
 }
 
@@ -1591,7 +1510,6 @@
 		return;
 	sk->inuse = 1;
 
-	DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff));
 	buff->sk = sk;
 	buff->len = sizeof(*t1);
 	buff->localroute = sk->localroute;
@@ -1627,7 +1545,6 @@
 			sk->state = TCP_FIN_WAIT2;
 
 		release_sock(sk);
-		DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
 		return;
 	}
 
@@ -1736,7 +1653,6 @@
 	if (buff == NULL) 
 	  	return;
 
-	DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff));
 	buff->len = sizeof(*t1);
 	buff->sk = NULL;
 	buff->dev = dev;
@@ -1848,7 +1764,11 @@
     if (! mss_seen)
       sk->mtu=min(sk->mtu, 536);  /* default MSS if none sent */
   }
+#ifdef CONFIG_INET_PCTCP
+  sk->mss = min(sk->max_window >> 1, sk->mtu);
+#else    
   sk->mss = min(sk->max_window, sk->mtu);
+#endif  
 }
 
 static inline unsigned long default_mask(unsigned long dst)
@@ -1880,10 +1800,7 @@
   struct tcphdr *th;
   struct device *ndev=NULL;
   int tmp;
-
-  DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n"
-	  "                  opt = %X, dev = %X)\n",
-	  sk, skb, daddr, saddr, opt, dev));
+  struct rtable *rt;
   
   th = skb->h.th;
 
@@ -1891,7 +1808,6 @@
   if (!sk->dead) {
   	sk->data_ready(sk,0);
   } else {
-	DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n"));
 	tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl);
 	tcp_statistics.TcpAttemptFails++;
 	kfree_skb(skb, FREE_READ);
@@ -1923,15 +1839,14 @@
 	return;
   }
 
-  DPRINTF((DBG_TCP, "newsk = %X\n", newsk));
   memcpy(newsk, sk, sizeof(*newsk));
   skb_queue_head_init(&newsk->write_queue);
   skb_queue_head_init(&newsk->receive_queue);
   newsk->send_head = NULL;
   newsk->send_tail = NULL;
   skb_queue_head_init(&newsk->back_log);
-  newsk->rtt = TCP_CONNECT_TIME << 3;
-  newsk->rto = TCP_CONNECT_TIME;
+  newsk->rtt = 0;		/*TCP_CONNECT_TIME<<3*/
+  newsk->rto = TCP_TIMEOUT_INIT;
   newsk->mdev = 0;
   newsk->max_window = 0;
   newsk->cong_window = 1;
@@ -1992,8 +1907,11 @@
 
 /* use 512 or whatever user asked for */
 /* note use of sk->user_mss, since user has no direct access to newsk */
+  rt=ip_rt_route(saddr, NULL,NULL);
   if (sk->user_mss)
     newsk->mtu = sk->user_mss;
+  else if(rt!=NULL && (rt->rt_flags&RTF_MTU))
+    newsk->mtu = rt->rt_mtu - HEADER_SIZE;
   else {
 #ifdef CONFIG_INET_SNARL	/* Sub Nets ARe Local */
     if ((saddr ^ daddr) & default_mask(saddr))
@@ -2075,7 +1993,7 @@
   tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk);
   newsk->prot->queue_xmit(newsk, dev, buff, 0);
 
-  reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME);
+  reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_TIMEOUT_INIT);
   skb->sk = newsk;
 
   /* Charge the sock_buff to newsk. */
@@ -2102,7 +2020,6 @@
 	 * We need to grab some memory, and put together a FIN,	
 	 * and then put it into the queue to be sent.
 	 */
-	DPRINTF((DBG_TCP, "tcp_close((struct sock *)%X, %d)\n",sk, timeout));
 	sk->inuse = 1;
 	sk->keepopen = 1;
 	sk->shutdown = SHUTDOWN_MASK;
@@ -2231,7 +2148,6 @@
 				if(timeout)
 					tcp_time_wait(sk);
 
-				DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
 				release_sock(sk);
 				return;
 			}
@@ -2302,8 +2218,6 @@
 {
   struct sk_buff *skb;
 
-  DPRINTF((DBG_TCP, "tcp_write_xmit(sk=%X)\n", sk));
-
   /* The bytes will have to remain here. In time closedown will
      empty the write queue and all will be happy */
   if(sk->zapped)
@@ -2317,8 +2231,6 @@
 	&& sk->packets_out < sk->cong_window) {
 		IS_SKB(skb);
 		skb_unlink(skb);
-		DPRINTF((DBG_TCP, "Sending a packet.\n"));
-
 		/* See if we really need to send the packet. */
 		if (before(skb->h.seq, sk->rcv_ack_seq +1)) {
 			sk->retransmits = 0;
@@ -2384,13 +2296,13 @@
 	return(1);	/* Dead, cant ack any more so why bother */
 
   ack = ntohl(th->ack_seq);
-  DPRINTF((DBG_TCP, "tcp_ack ack=%d, window=%d, "
-	  "sk->rcv_ack_seq=%d, sk->window_seq = %d\n",
-	  ack, ntohs(th->window), sk->rcv_ack_seq, sk->window_seq));
-
   if (ntohs(th->window) > sk->max_window) {
   	sk->max_window = ntohs(th->window);
+#ifdef CONFIG_INET_PCTCP
+	sk->mss = min(sk->max_window>>1, sk->mtu);
+#else
 	sk->mss = min(sk->max_window, sk->mtu);
+#endif	
   }
 
   if (sk->retransmits && sk->timeout == TIME_KEEPOPEN)
@@ -2496,7 +2408,6 @@
 	}
   }
 
-  DPRINTF((DBG_TCP, "tcp_ack: Updating rcv ack sequence.\n"));
   sk->rcv_ack_seq = ack;
 
   /*
@@ -2513,8 +2424,8 @@
 	  sk->rto = ((sk->rtt >> 2) + sk->mdev) >> 1;
 	  if (sk->rto > 120*HZ)
 	    sk->rto = 120*HZ;
-	  if (sk->rto < 1*HZ)
-	    sk->rto = 1*HZ;
+	  if (sk->rto < 2)	/* Was 1*HZ */
+	    sk->rto = 2;
 	}
   }
 
@@ -2564,9 +2475,6 @@
 
 		/* We have one less packet out there. */
 		if (sk->packets_out > 0) sk->packets_out --;
-		DPRINTF((DBG_TCP, "skb=%X skb->h.seq = %d acked ack=%d\n",
-				sk->send_head, sk->send_head->h.seq, ack));
-
 		/* Wake up the process, it can probably write more. */
 		if (!sk->dead) sk->write_space(sk);
 
@@ -2583,6 +2491,8 @@
 		   */
 
 		  m = jiffies - oskb->when;  /* RTT */
+		  if(m<=0)
+		  	m=1;		     /* IS THIS RIGHT FOR <0 ??? */
 		  m -= (sk->rtt >> 3);       /* m is now error in rtt est */
 		  sk->rtt += m;              /* rtt = 7/8 rtt + 1/8 new */
 		  if (m < 0)
@@ -2594,8 +2504,8 @@
 		  sk->rto = ((sk->rtt >> 2) + sk->mdev) >> 1;
 		  if (sk->rto > 120*HZ)
 		    sk->rto = 120*HZ;
-		  if (sk->rto < 1*HZ)
-		    sk->rto = 1*HZ;
+		  if (sk->rto < 2)	/* Was 1*HZ */
+		    sk->rto = 2;
 		  sk->backoff = 0;
 
 		}
@@ -2642,7 +2552,6 @@
   } else {
 	if (sk->send_head == NULL && sk->ack_backlog == 0 &&
 	    sk->state != TCP_TIME_WAIT && !sk->keepopen) {
-		DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); 
 		if (!sk->dead) sk->write_space(sk);
 
 		if (sk->keepopen)
@@ -2676,16 +2585,7 @@
   if (sk->state == TCP_LAST_ACK) {
 	if (!sk->dead)
 		sk->state_change(sk);
-	DPRINTF((DBG_TCP, "TCP_LAST_ACK-A: %d/%d %d/%d ack/sent %d %d\n",
-	    sk->rcv_ack_seq,
-	    sk->write_seq,
-	    sk->acked_seq,
-	    sk->fin_seq,
-	    ack,
-	    sk->sent_seq
-	));
 	if (sk->rcv_ack_seq == sk->write_seq && sk->acked_seq == sk->fin_seq) {
-		DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk));
 		flag |= 1;
 		sk->state = TCP_CLOSE;
 		sk->shutdown = SHUTDOWN_MASK;
@@ -2763,7 +2663,6 @@
 	reset_timer(sk, TIME_WRITE, sk->rto);
       }
 
-  DPRINTF((DBG_TCP, "leaving tcp_ack\n"));
   return(1);
 }
 
@@ -2782,11 +2681,8 @@
   int dup_dumped=0;
 
   th = skb->h.th;
-  print_th(th);
   skb->len = len -(th->doff*4);
 
-  DPRINTF((DBG_TCP, "tcp_data len = %d sk = %X:\n", skb->len, sk));
-
   sk->bytes_rcv += skb->len;
   if (skb->len == 0 && !th->fin && !th->urg && !th->psh) {
 	/* Don't want to keep passing ack's back and forth. */
@@ -2795,7 +2691,7 @@
 	return(0);
   }
 
-  if (sk->shutdown & RCV_SHUTDOWN) {
+  if (sk->shutdown & RCV_SHUTDOWN && skb->len!=0 /* Added AGC */) {
 	sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
 	tcp_reset(sk->saddr, sk->daddr, skb->h.th,
 		sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
@@ -2803,7 +2699,6 @@
 	sk->state = TCP_CLOSE;
 	sk->err = EPIPE;
 	sk->shutdown = SHUTDOWN_MASK;
-	DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk));
 	kfree_skb(skb, FREE_READ);
 	if (!sk->dead) sk->state_change(sk);
 	return(0);
@@ -2819,11 +2714,9 @@
 
   /* This should start at the last one, and then go around forwards. */
   if (skb_peek(&sk->receive_queue) == NULL) {
-	DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb));
 	skb_queue_head(&sk->receive_queue,skb);
 	skb1= NULL;
   } else {
-	DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk));
 	for(skb1=sk->receive_queue.prev; ; skb1 = skb1->prev) {
 		if(sk->debug)
 		{
@@ -2853,7 +2746,6 @@
 			break;
 		}
 	}
-	DPRINTF((DBG_TCP, "skb = %X:\n", skb));
   }
 
   th->ack_seq = th->seq + skb->len;
@@ -2973,16 +2865,12 @@
         if(sk->debug)
         	printk("Data wakeup.\n");
 	sk->data_ready(sk,0);
-  } else {
-	DPRINTF((DBG_TCP, "data received on dead socket.\n"));
-  }
+  } 
 
 #ifdef NOTDEF 	/* say what?  this is handled by tcp_ack() */
 
   if (sk->state == TCP_FIN_WAIT2 &&
       sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->write_seq) {
-	DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk));
-
 /*	tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr); */
 	sk->shutdown = SHUTDOWN_MASK;
 	sk->state = TCP_LAST_ACK;
@@ -3065,9 +2953,6 @@
 static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th, 
 	 unsigned long saddr, struct device *dev)
 {
-	DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n",
-					sk, th, saddr, dev));
-  
 	sk->fin_seq = th->seq + skb->len + th->syn + th->fin;
 
 	if (!sk->dead) 
@@ -3152,9 +3037,6 @@
   struct sock *newsk;
   struct sk_buff *skb;
   
-  DPRINTF((DBG_TCP, "tcp_accept(sk=%X, flags=%X, addr=%s)\n",
-				sk, flags, in_ntoa(sk->saddr)));
-
   /*
    * We need to make sure that this socket is listening,
    * and that it has something pending.
@@ -3209,6 +3091,7 @@
 	int tmp;
 	struct tcphdr *t1;
 	int err;
+	struct rtable *rt;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -3224,8 +3107,6 @@
 	if (sin.sin_family && sin.sin_family != AF_INET) 
 		return(-EAFNOSUPPORT);
 
-	DPRINTF((DBG_TCP, "TCP connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr)));
-  	
   	/*
   	 *	connect() to INADDR_ANY means loopback (BSD'ism).
   	 */
@@ -3239,8 +3120,7 @@
 
 	if (ip_chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) 
 	{ 
-		DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n"));
-		return(-ENETUNREACH);
+		return -ENETUNREACH;
 	}
   
 	/*
@@ -3275,6 +3155,9 @@
 	/*
 	 *	Put in the IP header and routing stuff. 
 	 */
+	 
+	rt=ip_rt_route(sk->daddr, NULL, NULL);
+	
 
 	/*
 	 *	We need to build the routing stuff fromt the things saved in skb. 
@@ -3310,6 +3193,8 @@
 
 	if (sk->user_mss)
 		sk->mtu = sk->user_mss;
+	else if(rt!=NULL && rt->rt_flags&RTF_MTU)
+		sk->mtu = rt->rt_mtu;
 	else 
 	{
 #ifdef SUBNETSARELOCAL
@@ -3325,8 +3210,11 @@
 	 *	but not bigger than device MTU 
 	 */
 
+	if(sk->mtu <32)
+		sk->mtu = 32;	/* Sanity limit */
+		
 	sk->mtu = min(sk->mtu, dev->mtu - HEADER_SIZE);
-
+	
 	/*
 	 *	Put in the TCP options to say MTU. 
 	 */
@@ -3344,8 +3232,9 @@
 	 */
 
 	sk->state = TCP_SYN_SENT;
-	sk->rtt = TCP_CONNECT_TIME;
-	reset_timer(sk, TIME_WRITE, TCP_CONNECT_TIME);	/* Timer for repeating the SYN until an answer */
+/*	sk->rtt = TCP_CONNECT_TIME;*/
+	sk->rto = TCP_TIMEOUT_INIT;
+	reset_timer(sk, TIME_WRITE, sk->rto);	/* Timer for repeating the SYN until an answer */
 	sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
 
 	sk->prot->queue_xmit(sk, dev, buff, 0);  
@@ -3390,8 +3279,6 @@
 	return 1;
 
 ignore_it:
-	DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n"));
-
 	if (th->rst)
 		return 0;
 
@@ -3422,13 +3309,11 @@
   struct sock *sk;
 
   if (!skb) {
-	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n"));
 	return(0);
   }
 
   if (!dev) 
   {
-	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n"));
 	return(0);
   }
   
@@ -3438,23 +3323,14 @@
 
   /* Find the socket. */
   sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
-  DPRINTF((DBG_TCP, "<<\n"));
-  DPRINTF((DBG_TCP, "len = %d, redo = %d, skb=%X\n", len, redo, skb));
-  
   /* If this socket has got a reset its to all intents and purposes 
      really dead */
   if (sk!=NULL && sk->zapped)
 	sk=NULL;
 
-  if (sk) {
-	 DPRINTF((DBG_TCP, "sk = %X:\n", sk));
-  }
-
   if (!redo) {
 	if (tcp_check(th, len, saddr, daddr )) {
 		skb->sk = NULL;
-		DPRINTF((DBG_TCP, "packet dropped with bad checksum.\n"));
-if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
 		kfree_skb(skb,FREE_READ);
 		/*
 		 * We don't release the socket because it was
@@ -3493,27 +3369,23 @@
 	sti();
   } else {
 	if (!sk) {
-		DPRINTF((DBG_TCP, "tcp.c: tcp_rcv bug sk=NULL redo = 1\n"));
 		return(0);
 	}
   }
 
   if (!sk->prot) {
-	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv sk->prot = NULL \n"));
 	return(0);
   }
 
   /* Charge the memory to the socket. */
   if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
 	skb->sk = NULL;
-	DPRINTF((DBG_TCP, "dropping packet due to lack of buffer space.\n"));
 	kfree_skb(skb, FREE_READ);
 	release_sock(sk);
 	return(0);
   }
   sk->rmem_alloc += skb->mem_len;
 
-  DPRINTF((DBG_TCP, "About to do switch.\n"));
 
   /* Now deal with it. */
   switch(sk->state) {
@@ -3542,8 +3414,6 @@
 	case TCP_FIN_WAIT2:
 	case TCP_TIME_WAIT:
 		if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
-			if (inet_debug == DBG_SLIP) 
-				printk("\rtcp_rcv: not in seq\n");
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
@@ -3622,7 +3492,6 @@
 
 	case TCP_CLOSE:
 		if (sk->dead || sk->daddr) {
-			DPRINTF((DBG_TCP, "packet received for closed,dead socket\n"));
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
@@ -3864,7 +3733,6 @@
 	buff->sk = sk;
 	buff->localroute = sk->localroute;
 
-	DPRINTF((DBG_TCP, "in tcp_write_wakeup\n"));
 	t1 = (struct tcphdr *) buff->data;
 
 	/* Put in the IP header and routing stuff. */
diff -u --recursive --new-file v1.1.12/linux/net/inet/tcp.h linux/net/inet/tcp.h
--- v1.1.12/linux/net/inet/tcp.h	Tue Apr 19 10:54:06 1994
+++ linux/net/inet/tcp.h	Mon May 23 08:17:53 1994
@@ -49,13 +49,13 @@
 #define TCP_TIMEOUT_LEN	(15*60*HZ) /* should be about 15 mins		*/
 #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to sucessfully 
 				  * close the socket, about 60 seconds	*/
-#define TCP_ACK_TIME	3000	/* time to delay before sending an ACK	*/
+#define TCP_ACK_TIME	(3*HZ)	/* time to delay before sending an ACK	*/
 #define TCP_DONE_TIME	250	/* maximum time to wait before actually
 				 * destroying a socket			*/
 #define TCP_WRITE_TIME	3000	/* initial time to wait for an ACK,
 			         * after last transmit			*/
-#define TCP_CONNECT_TIME 2000	/* time to retransmit first SYN		*/
-#define TCP_SYN_RETRIES	5	/* number of times to retry openning a
+#define TCP_TIMEOUT_INIT (3*HZ)	/* RFC 1122 initial timeout value	*/
+#define TCP_SYN_RETRIES	5	/* number of times to retry opening a
 				 * connection 				*/
 #define TCP_PROBEWAIT_LEN 100	/* time to wait between probes when
 				 * I've got something to write and
diff -u --recursive --new-file v1.1.12/linux/net/inet/timer.c linux/net/inet/timer.c
--- v1.1.12/linux/net/inet/timer.c	Mon May 23 12:14:29 1994
+++ linux/net/inet/timer.c	Mon May 23 08:17:53 1994
@@ -3,7 +3,7 @@
  *		operating system.  INET is implemented using the  BSD Socket
  *		interface as the means of communication with the user level.
  *
- *		TIMER - implementation of software timers.
+ *		TIMER - implementation of software timers for IP.
  *
  * Version:	@(#)timer.c	1.0.7	05/25/93
  *
@@ -22,7 +22,7 @@
  *					the socket will get removed BEFORE this is called
  *					otherwise if the timer TIME_DESTROY occurs inside
  *					of inet_bh() with this socket being handled it goes
- *					BOOM! Have to stop timer going off if inet_bh is
+ *					BOOM! Have to stop timer going off if net_bh is
  *					active or the destroy causes crashes.
  *
  *		This program is free software; you can redistribute it and/or
@@ -49,202 +49,213 @@
 #include "sock.h"
 #include "arp.h"
 
-void
-delete_timer (struct sock *t)
+void delete_timer (struct sock *t)
 {
-  unsigned long flags;
+	unsigned long flags;
 
-  save_flags (flags);
-  cli();
+	save_flags (flags);
+	cli();
 
-  t->timeout = 0;
-  del_timer (&t->timer);
+	t->timeout = 0;
+	del_timer (&t->timer);
 
-  restore_flags (flags);
+	restore_flags (flags);
 }
 
-void
-reset_timer (struct sock *t, int timeout, unsigned long len)
+void reset_timer (struct sock *t, int timeout, unsigned long len)
 {
-  delete_timer (t);
-
-  t->timeout = timeout;
-
+	delete_timer (t);
+	t->timeout = timeout;
 #if 1
   /* FIXME: ??? */
-  if ((int) len < 0)	/* prevent close to infinite timers. THEY _DO_ */
-	len = 3;	/* happen (negative values ?) - don't ask me why ! -FB */
+	if ((int) len < 0)	/* prevent close to infinite timers. THEY _DO_ */
+		len = 3;	/* happen (negative values ?) - don't ask me why ! -FB */
 #endif
-  t->timer.expires = len;
-  add_timer (&t->timer);
+	t->timer.expires = len;
+	add_timer (&t->timer);
 }
 
 
 /*
- * Now we will only be called whenever we need to do
- * something, but we must be sure to process all of the
- * sockets that need it.
+ *	Now we will only be called whenever we need to do
+ *	something, but we must be sure to process all of the
+ *	sockets that need it.
  */
-void
-net_timer (unsigned long data)
+
+void net_timer (unsigned long data)
 {
-  struct sock *sk = (struct sock*)data;
-  int why = sk->timeout;
-  /* timeout is overwritten by 'delete_timer' and 'reset_timer' */
-
-  cli();
-  if (sk->inuse || in_bh) {
-    sk->timer.expires = 10;
-    add_timer(&sk->timer);
-    sti();
-    return;
-  }
-  sk->inuse = 1;
-  sti();
-
-  DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why));
-  if (skb_peek(&sk->write_queue) && 
-      before(sk->window_seq, sk->write_queue.next->h.seq) &&
-      sk->send_head == NULL &&
-      sk->ack_backlog == 0 &&
-      sk->state != TCP_TIME_WAIT)
-    reset_timer(sk, TIME_PROBE0, sk->rto);
-  else if (sk->keepopen)
-    reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
-
-  /* Always see if we need to send an ack. */
-  if (sk->ack_backlog) {
-    sk->prot->read_wakeup (sk);
-    if (! sk->dead)
-    	sk->data_ready(sk,0);
-  }
-
-  /* Now we need to figure out why the socket was on the timer. */
-  switch (why) {
-    case TIME_DONE:
-	if (! sk->dead || sk->state != TCP_CLOSE) {
-	  printk ("non dead socket in time_done\n");
-	  release_sock (sk);
-	  break;
-	}
-	destroy_sock (sk);
-	break;
-    case TIME_DESTROY:
-	/* We've waited for a while for all the memory associated with
-	 * the socket to be freed.  We need to print an error message.
-	 */
-	if(sk->wmem_alloc!=0 || sk->rmem_alloc!=0)
-	{
-		DPRINTF ((DBG_TMR, "possible memory leak.  sk = %X\n", sk));
-		sk->wmem_alloc++;	/* So it DOESNT go away */
-		destroy_sock (sk);
-		sk->wmem_alloc--;	/* Might now have hit 0 - fall through and do it again if so */
-		sk->inuse = 0;	/* This will be ok, the destroy won't totally work */
-	}
-	if(sk->wmem_alloc==0 && sk->rmem_alloc==0)
-		destroy_sock(sk);	/* Socket gone, DONT update sk->inuse! */
-	break;
-    case TIME_CLOSE:
-	/* We've waited long enough, close the socket. */
-	sk->state = TCP_CLOSE;
-	delete_timer (sk);
-	/* Kill the ARP entry in case the hardware has changed. */
-	arp_destroy (sk->daddr, 0);
-	if (!sk->dead)
-	  sk->state_change(sk);
-	sk->shutdown = SHUTDOWN_MASK;
-	reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME);
-	release_sock (sk);
-	break;
-    case TIME_PROBE0:
-	tcp_send_probe0(sk);
-	release_sock (sk);
-	break;
-    case TIME_WRITE:	/* try to retransmit. */
-	/* It could be we got here because we needed to send an ack.
-	 * So we need to check for that.
-	 */
-	{
-	struct sk_buff *skb;
-	unsigned long flags;
+	struct sock *sk = (struct sock*)data;
+	int why = sk->timeout;
+	/* timeout is overwritten by 'delete_timer' and 'reset_timer' */
 
-	save_flags(flags);
 	cli();
-	skb = sk->send_head;
-	if (!skb) {
-	  restore_flags(flags);
-	} else {
-	  if (jiffies < skb->when + sk->rto) {
-	    reset_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
-	    restore_flags(flags);
-	    release_sock (sk);
-	    break;
-	  }
-	  restore_flags(flags);
-	  /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
-	     sk->retransmits, sk->packets_out, sk->cong_window); */
-	  DPRINTF ((DBG_TMR, "retransmitting.\n"));
-	  sk->prot->retransmit (sk, 0);
-	  if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
-	    || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
-	    DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n"));
-	    arp_destroy (sk->daddr, 0);
-	    ip_route_check (sk->daddr);
-	  }
-	  if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
-	    DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n"));
-	    sk->err = ETIMEDOUT;
-	    if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2
-	      || sk->state == TCP_CLOSING) {
-	      sk->state = TCP_TIME_WAIT;
-	      reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-	    } else {
-	      sk->prot->close (sk, 1);
-	      break;
-	    }
-	  }
-	}
-	release_sock (sk);
-	break;
-	}
-    case TIME_KEEPOPEN:
-	/* Send something to keep the connection open. */
-	if (sk->prot->write_wakeup)
-	  sk->prot->write_wakeup (sk);
-	sk->retransmits++;
-	if (sk->shutdown == SHUTDOWN_MASK) {
-	  sk->prot->close (sk, 1);
-	  sk->state = TCP_CLOSE;
-	}
+	if (sk->inuse || in_bh) 
+	{
+		sk->timer.expires = 10;
+		add_timer(&sk->timer);
+		sti();
+		return;
+	}
+
+	sk->inuse = 1;
+	sti();
+
+	if (skb_peek(&sk->write_queue) && 
+	      before(sk->window_seq, sk->write_queue.next->h.seq) &&
+	      sk->send_head == NULL &&
+	      sk->ack_backlog == 0 &&
+	      sk->state != TCP_TIME_WAIT)
+		reset_timer(sk, TIME_PROBE0, sk->rto);
+	else if (sk->keepopen)
+		reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
+
+	/* Always see if we need to send an ack. */
 
-	if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
-	  || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
-	  DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n"));
-	  arp_destroy (sk->daddr, 0);
-	  ip_route_check (sk->daddr);
-	  release_sock (sk);
-	  break;
+	if (sk->ack_backlog) 
+	{
+		sk->prot->read_wakeup (sk);
+		if (! sk->dead)
+		sk->data_ready(sk,0);
 	}
-	if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
-	  DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n"));
-	  arp_destroy (sk->daddr, 0);
-	  sk->err = ETIMEDOUT;
-	  if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) {
-	    sk->state = TCP_TIME_WAIT;
-	    if (!sk->dead)
-	      sk->state_change(sk);
-	    release_sock (sk);
-	  } else {
-	    sk->prot->close (sk, 1);
-	  }
-	  break;
+
+	/* Now we need to figure out why the socket was on the timer. */
+
+	switch (why) 
+	{
+		case TIME_DONE:
+			if (! sk->dead || sk->state != TCP_CLOSE) 
+			{
+				printk ("non dead socket in time_done\n");
+				release_sock (sk);
+				break;
+			}
+			destroy_sock (sk);
+			break;
+
+		case TIME_DESTROY:
+		/*
+		 *	We've waited for a while for all the memory associated with
+		 *	the socket to be freed.
+		 */
+			if(sk->wmem_alloc!=0 || sk->rmem_alloc!=0)
+			{
+				sk->wmem_alloc++;	/* So it DOESNT go away */
+				destroy_sock (sk);
+				sk->wmem_alloc--;	/* Might now have hit 0 - fall through and do it again if so */
+				sk->inuse = 0;	/* This will be ok, the destroy won't totally work */
+			}
+			if(sk->wmem_alloc==0 && sk->rmem_alloc==0)
+				destroy_sock(sk);	/* Socket gone, DONT update sk->inuse! */
+				break;
+		case TIME_CLOSE:
+			/* We've waited long enough, close the socket. */
+			sk->state = TCP_CLOSE;
+			delete_timer (sk);
+			/* Kill the ARP entry in case the hardware has changed. */
+			arp_destroy (sk->daddr, 0);
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->shutdown = SHUTDOWN_MASK;
+			reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME);
+			release_sock (sk);
+			break;
+		case TIME_PROBE0:
+			tcp_send_probe0(sk);
+			release_sock (sk);
+			break;
+		case TIME_WRITE:	/* try to retransmit. */
+			/* It could be we got here because we needed to send an ack.
+			 * So we need to check for that.
+			 */
+		{
+			struct sk_buff *skb;
+			unsigned long flags;
+
+			save_flags(flags);
+			cli();
+			skb = sk->send_head;
+			if (!skb) 
+			{
+				restore_flags(flags);
+			} 
+			else 
+			{
+				if (jiffies < skb->when + sk->rto) 
+				{
+					reset_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
+					restore_flags(flags);
+					release_sock (sk);
+					break;
+				}
+				restore_flags(flags);
+				/* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
+					sk->retransmits, sk->packets_out, sk->cong_window); */
+				sk->prot->retransmit (sk, 0);
+				if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+					|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) 
+				{
+					arp_destroy (sk->daddr, 0);
+					ip_route_check (sk->daddr);
+				}
+				if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) 
+				{
+					sk->err = ETIMEDOUT;
+					if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) 
+					{
+						sk->state = TCP_TIME_WAIT;
+						reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+					}
+					else
+					{
+						sk->prot->close (sk, 1);
+							break;
+					}
+				}
+			}
+			release_sock (sk);
+			break;
+		}
+		case TIME_KEEPOPEN:
+		/* Send something to keep the connection open. */
+			if (sk->prot->write_wakeup)
+				  sk->prot->write_wakeup (sk);
+			sk->retransmits++;
+			if (sk->shutdown == SHUTDOWN_MASK) 
+			{
+				sk->prot->close (sk, 1);
+				sk->state = TCP_CLOSE;
+			}
+			if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+				|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) 
+			{
+				arp_destroy (sk->daddr, 0);
+				ip_route_check (sk->daddr);
+				release_sock (sk);
+				break;
+			}
+			if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) 
+			{
+				arp_destroy (sk->daddr, 0);
+				sk->err = ETIMEDOUT;
+				if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) 
+				{
+					sk->state = TCP_TIME_WAIT;
+					if (!sk->dead)
+						sk->state_change(sk);
+					release_sock (sk);
+				  } 
+				  else 
+				  {
+					sk->prot->close (sk, 1);
+				  }
+				  break;
+			}
+			release_sock (sk);
+			break;
+		default:
+			printk ("net_timer: timer expired - reason unknown\n");
+			release_sock (sk);
+			break;
 	}
-	release_sock (sk);
-	break;
-    default:
-	printk ("net_timer: timer expired - reason unknown\n");
-	release_sock (sk);
-	break;
-  }
 }
 
diff -u --recursive --new-file v1.1.12/linux/net/inet/udp.c linux/net/inet/udp.c
--- v1.1.12/linux/net/inet/udp.c	Mon May 23 12:14:29 1994
+++ linux/net/inet/udp.c	Mon May 23 09:59:41 1994
@@ -37,6 +37,7 @@
  *		Alan Cox	:	SNMP Mibs
  *		Alan Cox	:	MSG_DONTROUTE, and 0.0.0.0 support.
  *		Matt Dillon	:	UDP length checks.
+ *		Alan Cox	:	Smarter af_inet used properly.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -79,20 +80,6 @@
 
 #define min(a,b)	((a)<(b)?(a):(b))
 
-static void print_udp(struct udphdr *uh)
-{
-	if (inet_debug != DBG_UDP) 
-		return;
-
-	if (uh == NULL) 
-	{
-		printk("(NULL)\n");
-		return;
-	}
-	printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));
-	printk("     len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));
-}
-
 
 /*
  * This routine is called by the ICMP module when it gets some
@@ -119,9 +106,6 @@
 	 */  
 	th = (struct udphdr *)header;  
    
-	DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\
-		sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest));
-
 	sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr);
 
 	if (sk == NULL) 
@@ -134,8 +118,6 @@
 		return;
 	}
 
-	sk->err = icmp_err_convert[err & 0xff].errno;
-
 	/*
 	 *	It's only fatal if we have connected to them. I'm not happy
 	 *	with this code. Some BSD comparisons need doing.
@@ -143,7 +125,8 @@
 	 
 	if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) 
 	{
-		sk->err=ECONNREFUSED;
+		sk->err = icmp_err_convert[err & 0xff].errno;
+/*		sk->err=ECONNREFUSED;*/
  	}
 	
 	sk->error_report(sk);
@@ -154,11 +137,6 @@
 {
 	unsigned long sum;
 
-	DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",
-	   						uh, len, saddr, daddr));
-
-	print_udp(uh);
-
 	__asm__(  "\t addl %%ecx,%%ebx\n"
 		  "\t adcl %%edx,%%ebx\n"
 		  "\t adcl $0, %%ebx\n"
@@ -260,16 +238,7 @@
 	unsigned char *buff;
 	unsigned long saddr;
 	int size, tmp;
-	int err;
   
-	DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
-		in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port),
-		from, len));
-
-	err=verify_area(VERIFY_READ, from, len);
-	if(err)
-	  	return(err);
-
 	/* 
 	 *	Allocate an sk_buff copy of the packet.
 	 */
@@ -279,7 +248,7 @@
 
 
 	if (skb == NULL) 
-		return(-ENOMEM);
+		return(-ENOBUFS);
 
 	skb->sk       = NULL;	/* to avoid changing sk->saddr */
 	skb->free     = 1;
@@ -292,8 +261,6 @@
 	buff = skb->data;
 	saddr = 0;
 	dev = NULL;
-	DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
-			saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
 	tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
 			&dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
 	skb->sk=sk;	/* So memory is freed correctly */
@@ -310,8 +277,6 @@
   	
 	buff += tmp;
 	saddr = skb->saddr; /*dev->pa_addr;*/
-	DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
-
 	skb->len = tmp + sizeof(struct udphdr) + len;	/* len + UDP + IP + MAC */
 	skb->dev = dev;
 	
@@ -355,18 +320,11 @@
 	int tmp;
 	int err;
 
-	DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
-
 	/* 
 	 *	Check the flags. We support no flags for UDP sending
 	 */
 	if (flags&~MSG_DONTROUTE) 
 	  	return(-EINVAL);
-	if (len < 0) 
- 	 	return(-EINVAL);
- 	if (len == 0) 
-  		return(0);
-
 	/*
 	 *	Get and verify the address. 
 	 */
@@ -434,27 +392,6 @@
 	int err;
 	switch(cmd) 
 	{
-		case DDIOCSDBG:
-		{
-			int val;
-
-			if (!suser()) return(-EPERM);
-			err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));
-			if(err)
-				return err;
-			val = get_fs_long((int *)arg);
-			switch(val) {
-				case 0:
-					inet_debug = 0;
-					break;
-				case 1:
-					inet_debug = DBG_UDP;
-					break;
-				default:
-					return(-EINVAL);
-			}
-		}
-		break;
 		case TIOCOUTQ:
 		{
 			unsigned long amount;
@@ -513,26 +450,6 @@
   	struct sk_buff *skb;
   	int er;
 
-
-  	/*
-  	 * This will pick up errors that occured while the program
-  	 * was doing something else.
-  	 */
-  
-  	if (sk->err) 
-  	{
-		int err;
-	
-		err = -sk->err;
-		sk->err = 0;
-		return(err);
-  	}
-
-  	if (len == 0) 
-  		return(0);
-  	if (len < 0) 
-  		return(-EINVAL);
-
 	/*
 	 *	Check any passed addresses
 	 */
@@ -552,14 +469,6 @@
   			return(er);
   	}
   	
-  	/*
-  	 *	Check the buffer we were given
-  	 */
-  	 
-  	er=verify_area(VERIFY_WRITE,to,len);
-	if(er)
-		return er;
-	
 	/*
 	 *	From here the generic datagram does a lot of the work. Come
 	 *	the finished NET3, it will do _ALL_ the work!
@@ -672,7 +581,6 @@
 	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) 
 	{
 		printk("UDP: short packet: %d/%d\n", ulen, len);
-		DPRINTF((DBG_UDP, "UDP: short packet %d/%d\n", ulen, len));
 		udp_statistics.UdpInErrors++;
 		kfree_skb(skb, FREE_WRITE);
 		return(0);
@@ -699,7 +607,6 @@
 	if (uh->check && udp_check(uh, len, saddr, daddr)) 
 	{
 		printk("UDP: bad checksum.\n");
-		DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
 		udp_statistics.UdpInErrors++;
 		kfree_skb(skb, FREE_WRITE);
 		return(0);
@@ -733,13 +640,6 @@
 	}
 	sk->rmem_alloc += skb->mem_len;
   	udp_statistics.UdpInDatagrams++;
-  	
-	/*
-	 *	At this point we should print the thing out. 
-	 */
-
-	DPRINTF((DBG_UDP, "<< \n"));
-	print_udp(uh);
 
 	/*
 	 *	Now add it to the data chain and wake things up. 
diff -u --recursive --new-file v1.1.12/linux/net/inet/utils.c linux/net/inet/utils.c
--- v1.1.12/linux/net/inet/utils.c	Tue Apr 19 10:54:08 1994
+++ linux/net/inet/utils.c	Mon May 23 08:17:54 1994
@@ -12,6 +12,7 @@
  *
  * Fixes:
  *		Alan Cox	:	verify_area check.
+ *		Alan Cox	:	removed old debugging.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -88,80 +89,3 @@
 	return(htonl(l));
 }
 
-
-/*
- *	Debugging print out
- */
- 
-void dprintf(int level, char *fmt, ...)
-{
-	va_list args;
-	char *buff;
-	extern int vsprintf(char * buf, const char * fmt, va_list args);
-
-	if (level != inet_debug) 
-		return;
-
-	buff = (char *) kmalloc(256, GFP_ATOMIC);
-	if (buff != NULL) 
-	{
-		va_start(args, fmt);
-		vsprintf(buff, fmt, args);
-		va_end(args);
-		printk(buff);
-		kfree(buff);
-  	}
-  	else
-		printk("Debugging output lost: No free memory.\n");  	
-}
-
-/*
- *	Debugging ioctl() requests
- */
- 
-int dbg_ioctl(void *arg, int level)
-{
-	int val;
-	int err;
-  
-	if (!suser()) 
-		return(-EPERM);
-	err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));
-	if(err)
-  		return err;
-	val = get_fs_long((int *)arg);
-	switch(val) 
-	{
-		case 0:	/* OFF */
-			inet_debug = DBG_OFF;
-			break;
-		case 1:	/* ON, INET */
-			inet_debug = level;
-			break;
-
-		case DBG_RT:		/* modules */
-		case DBG_DEV:
-		case DBG_ETH:
-		case DBG_PROTO:
-		case DBG_TMR:
-		case DBG_PKT:
-		case DBG_RAW:
-	
-		case DBG_LOOPB:		/* drivers */
-		case DBG_SLIP:
-	
-		case DBG_ARP:		/* protocols */
-		case DBG_IP:
-		case DBG_ICMP:
-		case DBG_TCP:
-		case DBG_UDP:
-	
-			inet_debug = val;
-			break;
-	
-		default:
-			return(-EINVAL);
-	}
-	
-	return(0);
-}
diff -u --recursive --new-file v1.1.12/linux/net/protocols.c linux/net/protocols.c
--- v1.1.12/linux/net/protocols.c	Thu Jan  1 02:00:00 1970
+++ linux/net/protocols.c	Mon May 23 08:17:54 1994
@@ -0,0 +1,50 @@
+/*
+ *	Protocol intialiser table. Here seperately for convenience
+ *
+ */
+ 
+ 
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+
+
+#define CONFIG_UNIX		/* always present...	*/
+
+#ifdef	CONFIG_UNIX
+#include "unix/unix.h"
+#endif
+#ifdef	CONFIG_INET
+#include <linux/inet.h>
+#endif
+#ifdef CONFIG_IPX
+#include "inet/ipxcall.h"
+#include "inet/p8022call.h"
+#endif
+#ifdef CONFIG_AX25
+#include "inet/ax25call.h"
+#endif
+
+/*
+ *	Protocol Table
+ */
+ 
+struct net_proto protocols[] = {
+#ifdef	CONFIG_UNIX
+  { "UNIX",	unix_proto_init	},
+#endif
+#ifdef  CONFIG_IPX
+  { "IPX",	ipx_proto_init },
+  { "802.2",	p8022_proto_init },
+#endif
+#ifdef CONFIG_AX25  
+  { "AX.25",	ax25_proto_init },
+#endif  
+#ifdef	CONFIG_INET
+  { "INET",	inet_proto_init	},
+#endif
+  { NULL,	NULL		}
+};
+
+
diff -u --recursive --new-file v1.1.12/linux/net/socket.c linux/net/socket.c
--- v1.1.12/linux/net/socket.c	Sat May  7 14:54:18 1994
+++ linux/net/socket.c	Mon May 23 08:17:54 1994
@@ -11,6 +11,8 @@
  *		Anonymous	:	NOTSOCK/BADF cleanup. Error fix in
  *					shutdown()
  *		Alan Cox	:	verify_area() fixes
+ *		Alan Cox	: 	Removed DDI
+ *		Jonathan Kamens	:	SOCK_DGRAM reconnect bug
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -37,20 +39,12 @@
 #include <linux/socket.h>
 #include <linux/fcntl.h>
 #include <linux/net.h>
-#include <linux/ddi.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
 
-#undef SOCK_DEBUG
-
-#ifdef SOCK_DEBUG
-#include <stdarg.h>
-#define DPRINTF(x) dprintf x
-#else
-#define DPRINTF(x) /**/
-#endif
-
 static int sock_lseek(struct inode *inode, struct file *file, off_t offset,
 		      int whence);
 static int sock_read(struct inode *inode, struct file *file, char *buf,
@@ -80,27 +74,9 @@
 static struct socket sockets[NSOCKETS];
 static struct wait_queue *socket_wait_free = NULL;
 static struct proto_ops *pops[NPROTO];
-static int net_debug = 0;
 
 #define last_socket	(sockets + NSOCKETS - 1)
 
-#ifdef SOCK_DEBUG
-/* Module debugging. */
-static void
-dprintf(int level, char *fmt, ...)
-{
-  char buff[1024];
-  va_list args;
-  extern int vsprintf(char * buf, const char * fmt, va_list args);
-
-  if (level == 0) return;
-  va_start(args, fmt);
-  vsprintf(buff, fmt, args);
-  va_end(args);
-  printk(buff);
-}
-#endif
-
 /* Obtains the first available file descriptor and sets it up for use. */
 static int
 get_fd(struct inode *inode)
@@ -207,21 +183,15 @@
 			SOCK_INODE(sock)->i_socket = sock;
 
 			sock->wait = &SOCK_INODE(sock)->i_wait;
-			DPRINTF((net_debug,
-				"NET: sock_alloc: sk 0x%x, ino 0x%x\n",
-				       			sock, SOCK_INODE(sock)));
 			return(sock);
 		}
 	}
 	sti();
 	if (!wait) return(NULL);
-	DPRINTF((net_debug, "NET: sock_alloc: no free sockets, sleeping...\n"));
 	interruptible_sleep_on(&socket_wait_free);
 	if (current->signal & ~current->blocked) {
-		DPRINTF((net_debug, "NET: sock_alloc: sleep was interrupted\n"));
 		return(NULL);
 	}
-	DPRINTF((net_debug, "NET: sock_alloc: wakeup... trying again...\n"));
   }
 }
 
@@ -241,8 +211,6 @@
   struct inode *inode;
   struct socket *peersock, *nextsock;
 
-  DPRINTF((net_debug, "NET: sock_release: socket 0x%x, inode 0x%x\n",
-						sock, SOCK_INODE(sock)));
   if ((oldstate = sock->state) != SS_UNCONNECTED)
 			sock->state = SS_DISCONNECTING;
 
@@ -271,7 +239,6 @@
 static int
 sock_lseek(struct inode *inode, struct file *file, off_t offset, int whence)
 {
-  DPRINTF((net_debug, "NET: sock_lseek: huh?\n"));
   return(-ESPIPE);
 }
 
@@ -281,7 +248,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_read: buf=0x%x, size=%d\n", ubuf, size));
   if (!(sock = socki_lookup(inode))) {
 	printk("NET: sock_read: can't find socket for inode!\n");
 	return(-EBADF);
@@ -296,7 +262,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_write: buf=0x%x, size=%d\n", ubuf, size));
   if (!(sock = socki_lookup(inode))) {
 	printk("NET: sock_write: can't find socket for inode!\n");
 	return(-EBADF);
@@ -310,7 +275,6 @@
 sock_readdir(struct inode *inode, struct file *file, struct dirent *dirent,
 	     int count)
 {
-  DPRINTF((net_debug, "NET: sock_readdir: huh?\n"));
   return(-EBADF);
 }
 
@@ -321,8 +285,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_ioctl: inode=0x%x cmd=0x%x arg=%d\n",
-							inode, cmd, arg));
   if (!(sock = socki_lookup(inode))) {
 	printk("NET: sock_ioctl: can't find socket for inode!\n");
 	return(-EBADF);
@@ -336,9 +298,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_select: inode = 0x%x, kind = %s\n", inode,
-       (sel_type == SEL_IN) ? "in" :
-       (sel_type == SEL_OUT) ? "out" : "ex"));
   if (!(sock = socki_lookup(inode))) {
 	printk("NET: sock_select: can't find socket for inode!\n");
 	return(0);
@@ -356,9 +315,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_close: inode=0x%x (cnt=%d)\n",
-						inode, inode->i_count));
-
   /* It's possible the inode is NULL if we're closing an unfinished socket. */
   if (!inode) return;
   if (!(sock = socki_lookup(inode))) {
@@ -374,12 +330,7 @@
 {
   struct socket *last;
 
-  DPRINTF((net_debug,
-	"NET: sock_awaitconn: trying to connect socket 0x%x to 0x%x\n",
-							mysock, servsock));
   if (!(servsock->flags & SO_ACCEPTCON)) {
-	DPRINTF((net_debug,
-		"NET: sock_awaitconn: server not accepting connections\n"));
 	return(-EINVAL);
   }
 
@@ -439,18 +390,13 @@
   struct socket *sock;
   struct proto_ops *ops;
 
-  DPRINTF((net_debug,
-	"NET: sock_socket: family = %d, type = %d, protocol = %d\n",
-						family, type, protocol));
-
   /* Locate the correct protocol family. */
   for (i = 0; i < NPROTO; ++i) {
 	if (pops[i] == NULL) continue;
 	if (pops[i]->family == family) break;
   }
   if (i == NPROTO) {
-	DPRINTF((net_debug, "NET: sock_socket: family not found\n"));
-	return(-EINVAL);
+  	return -EINVAL;
   }
   ops = pops[i];
 
@@ -496,10 +442,6 @@
   struct socket *sock1, *sock2;
   int er;
 
-  DPRINTF((net_debug,
-	"NET: sock_socketpair: family = %d, type = %d, protocol = %d\n",
-							family, type, protocol));
-
   /*
    * Obtain the first socket and check if the underlying protocol
    * supports the socketpair call.
@@ -547,12 +489,10 @@
   struct socket *sock;
   int i;
 
-  DPRINTF((net_debug, "NET: sock_bind: fd = %d\n", fd));
   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
   if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) {
-	DPRINTF((net_debug, "NET: sock_bind: bind failed\n"));
 	return(i);
   }
   return(0);
@@ -569,12 +509,10 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_listen: fd = %d\n", fd));
   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
   if (sock->state != SS_UNCONNECTED) {
-	DPRINTF((net_debug, "NET: sock_listen: socket isn't unconnected\n"));
 	return(-EINVAL);
   }
   if (sock->ops && sock->ops->listen) sock->ops->listen(sock, backlog);
@@ -595,18 +533,14 @@
   struct socket *sock, *newsock;
   int i;
 
-  DPRINTF((net_debug, "NET: sock_accept: fd = %d\n", fd));
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   
   if (!(sock = sockfd_lookup(fd, &file))) return(-ENOTSOCK);
   if (sock->state != SS_UNCONNECTED) {
-	DPRINTF((net_debug, "NET: sock_accept: socket isn't unconnected\n"));
 	return(-EINVAL);
   }
   if (!(sock->flags & SO_ACCEPTCON)) {
-	DPRINTF((net_debug,
-		"NET: sock_accept: socket not accepting connections!\n"));
 	return(-EINVAL);
   }
 
@@ -632,9 +566,6 @@
 	return(-EINVAL);
   }
 
-  DPRINTF((net_debug, "NET: sock_accept: connected socket 0x%x via 0x%x\n",
-							sock, newsock));
-
   if (upeer_sockaddr)
 	newsock->ops->getname(newsock, upeer_sockaddr, upeer_addrlen, 1);
 
@@ -650,7 +581,6 @@
   struct file *file;
   int i;
 
-  DPRINTF((net_debug, "NET: sock_connect: fd = %d\n", fd));
   if (fd < 0 || fd >= NR_OPEN || (file=current->files->fd[fd]) == NULL)
 								return(-EBADF);
   
@@ -661,19 +591,24 @@
 		break;
 	case SS_CONNECTED:
 		/* Socket is already connected */
+		if(sock->type == SOCK_DGRAM) /* Hack for now - move this all into the protocol */
+			break;
 		return -EISCONN;
 	case SS_CONNECTING:
 		/* Not yet connected... we will check this. */
+		
+		/*
+		 *	FIXME:  for all protocols what happens if you start
+		 *	an async connect fork and both children connect. Clean
+		 *	this up in the protocols!
+		 */
 		return(sock->ops->connect(sock, uservaddr,
 					  addrlen, file->f_flags));
 	default:
-		DPRINTF((net_debug,
-			"NET: sock_connect: socket not unconnected\n"));
 		return(-EINVAL);
   }
   i = sock->ops->connect(sock, uservaddr, addrlen, file->f_flags);
   if (i < 0) {
-	DPRINTF((net_debug, "NET: sock_connect: connect failed\n"));
 	return(i);
   }
   return(0);
@@ -685,7 +620,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_getsockname: fd = %d\n", fd));
   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -698,7 +632,6 @@
 {
   struct socket *sock;
 
-  DPRINTF((net_debug, "NET: sock_getpeername: fd = %d\n", fd));
   if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
 			return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -712,10 +645,6 @@
   struct socket *sock;
   struct file *file;
 
-  DPRINTF((net_debug,
-	"NET: sock_send(fd = %d, buff = %X, len = %d, flags = %X)\n",
-       							fd, buff, len, flags));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -731,10 +660,6 @@
   struct socket *sock;
   struct file *file;
 
-  DPRINTF((net_debug,
-	"NET: sock_sendto(fd = %d, buff = %X, len = %d, flags = %X,"
-	 " addr=%X, alen = %d\n", fd, buff, len, flags, addr, addr_len));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -750,10 +675,6 @@
   struct socket *sock;
   struct file *file;
 
-  DPRINTF((net_debug,
-	"NET: sock_recv(fd = %d, buff = %X, len = %d, flags = %X)\n",
-							fd, buff, len, flags));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -769,10 +690,6 @@
   struct socket *sock;
   struct file *file;
 
-  DPRINTF((net_debug,
-	"NET: sock_recvfrom(fd = %d, buff = %X, len = %d, flags = %X,"
-	" addr=%X, alen=%X\n", fd, buff, len, flags, addr, addr_len));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -788,11 +705,6 @@
   struct socket *sock;
   struct file *file;
 	
-  DPRINTF((net_debug, "NET: sock_setsockopt(fd=%d, level=%d, optname=%d,\n",
-							fd, level, optname));
-  DPRINTF((net_debug, "                     optval = %X, optlen = %d)\n",
-							optval, optlen));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -807,11 +719,6 @@
   struct socket *sock;
   struct file *file;
 
-  DPRINTF((net_debug, "NET: sock_getsockopt(fd=%d, level=%d, optname=%d,\n",
-						fd, level, optname));
-  DPRINTF((net_debug, "                     optval = %X, optlen = %X)\n",
-						optval, optlen));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
   if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
@@ -827,8 +734,6 @@
   struct socket *sock;
   struct file *file;
 
-  DPRINTF((net_debug, "NET: sock_shutdown(fd = %d, how = %d)\n", fd, how));
-
   if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
 								return(-EBADF);
 
@@ -981,74 +886,6 @@
   }
 }
 
-
-static int
-net_ioctl(unsigned int cmd, unsigned long arg)
-{
-  int er;
-  switch(cmd) {
-	case DDIOCSDBG:
-		er=verify_area(VERIFY_READ, (void *)arg, sizeof(long));
-		if(er)
-			return er;
-		net_debug = get_fs_long((long *)arg);
-		if (net_debug != 0 && net_debug != 1) {
-			net_debug = 0;
-			return(-EINVAL);
-		}
-		return(0);
-	default:
-		return(-EINVAL);
-  }
-  /*NOTREACHED*/
-  return(0);
-}
-
-
-/*
- * Handle the IOCTL system call for the NET devices.  This basically
- * means I/O control for the SOCKET layer (future expansions could be
- * a variable number of socket table entries, et al), and for the more
- * general protocols like ARP.  The latter currently lives in the INET
- * module, so we have to get ugly a tiny little bit.  Later... -FvK
- */
-static int
-net_fioctl(struct inode *inode, struct file *file,
-	   unsigned int cmd, unsigned long arg)
-{
-  extern int arp_ioctl(unsigned int, void *);
-
-  /* Dispatch on the minor device. */
-  switch(MINOR(inode->i_rdev)) {
-	case 0:		/* NET (SOCKET) */
-		DPRINTF((net_debug, "NET: SOCKET level I/O control request.\n"));
-		return(net_ioctl(cmd, arg));
-#ifdef CONFIG_INET
-	case 1:		/* ARP */
-		DPRINTF((net_debug, "NET: ARP level I/O control request.\n"));
-		return(arp_ioctl(cmd, (void *) arg));
-#endif
-	default:
-		return(-ENODEV);
-  }
-  /*NOTREACHED*/
-  return(-EINVAL);
-}
-
-
-static struct file_operations net_fops = {
-  NULL,		/* LSEEK	*/
-  NULL,		/* READ		*/
-  NULL,		/* WRITE	*/
-  NULL,		/* READDIR	*/
-  NULL,		/* SELECT	*/
-  net_fioctl,	/* IOCTL	*/
-  NULL,		/* MMAP		*/
-  NULL,		/* OPEN		*/
-  NULL		/* CLOSE	*/
-};
-
-
 /*
  * This function is called by a protocol handler that wants to
  * advertise its address family, and have it linked into the
@@ -1065,38 +902,46 @@
 	pops[i] = ops;
 	pops[i]->family = family;
 	sti();
-	DPRINTF((net_debug, "NET: Installed protocol %d in slot %d (0x%X)\n",
-						family, i, (long)ops));
 	return(i);
   }
   sti();
   return(-ENOMEM);
 }
 
+void proto_init(void)
+{
+	extern struct net_proto protocols[];	/* Network protocols */
+	struct net_proto *pro;
+
+	/* Kick all configured protocols. */
+	pro = protocols;
+	while (pro->name != NULL) 
+	{
+		(*pro->init_func)(pro);
+		pro++;
+	}
+	/* We're all done... */
+}
+
 
 void
 sock_init(void)
 {
   struct socket *sock;
   int i;
-
-  /* Set up our SOCKET VFS major device. */
-  if (register_chrdev(SOCKET_MAJOR, "socket", &net_fops) < 0) {
-	printk("NET: cannot register major device %d!\n", SOCKET_MAJOR);
-	return;
-  }
-
   /* Release all sockets. */
   for (sock = sockets; sock <= last_socket; ++sock) sock->state = SS_FREE;
 
   /* Initialize all address (protocol) families. */
   for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
 
-  /* Initialize the DDI module. */
-  ddi_init();
+  /* Initialize the protocols module. */
+  proto_init();
 
-  /* Initialize the ARP module. */
-#if 0
-  arp_init();
-#endif
+  /* Initialize the DEV module. */
+  dev_init();
+  
+  /* And the bottom half handler */
+  bh_base[NET_BH].routine= net_bh;
+  
 }
diff -u --recursive --new-file v1.1.12/linux/net/unix/proc.c linux/net/unix/proc.c
--- v1.1.12/linux/net/unix/proc.c	Tue Apr 19 10:54:08 1994
+++ linux/net/unix/proc.c	Mon May 23 08:17:54 1994
@@ -17,6 +17,8 @@
  *
  * Fixes:
  *		Dmitry Gorodchanin	:	/proc locking fix
+ *		Mathijs Maassen		:	unbound /proc fix.
+ *		Alan Cox		:	Fix sock=NULL race
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -28,7 +30,6 @@
 #include <linux/string.h>
 #include <linux/socket.h>
 #include <linux/net.h>
-#include <linux/ddi.h>
 #include <linux/un.h>
 #include <linux/param.h>
 #include "unix.h"
@@ -41,19 +42,30 @@
   	off_t begin=0;
   	int len=0;
   	int i;
-
+  	unsigned long flags;
+	socket_state s_state;
+	short s_type;
+	long s_flags;
+	
   	len += sprintf(buffer, "Num RefCount Protocol Flags    Type St Path\n");
 
   	for(i = 0; i < NSOCKETS; i++) 
   	{
-		if (unix_datas[i].refcnt>0) 
+  		save_flags(flags);
+  		cli();
+		if (unix_datas[i].refcnt>0 && unix_datas[i].socket!=NULL)
 		{
+			/* sprintf is slow... lock only for the variable reads */
+			s_type=unix_datas[i].socket->type;
+			s_flags=unix_datas[i].socket->flags;
+			s_state=unix_datas[i].socket->state;
+			restore_flags(flags);
 			len += sprintf(buffer+len, "%2d: %08X %08X %08lX %04X %02X", i,
 				unix_datas[i].refcnt,
 				unix_datas[i].protocol,
-				unix_datas[i].socket->flags,
-				unix_datas[i].socket->type,
-				unix_datas[i].socket->state
+				s_flags,
+				s_type,
+				s_state
 			);
 
 			/* If socket is bound to a filename, we'll print it. */
@@ -65,7 +77,6 @@
 			else 
 			{ /* just add a newline */
 				buffer[len++]='\n';
-				buffer[len++]='\0';
 			}
 			
 			pos=begin+len;
@@ -77,6 +88,8 @@
 			if(pos>offset+length)
 				break;
 		}
+		else
+			restore_flags(flags);
 	}
 	
 	*start=buffer+(offset-begin);
diff -u --recursive --new-file v1.1.12/linux/net/unix/sock.c linux/net/unix/sock.c
--- v1.1.12/linux/net/unix/sock.c	Tue Apr 19 10:54:08 1994
+++ linux/net/unix/sock.c	Mon May 23 08:17:55 1994
@@ -42,7 +42,6 @@
 #include <linux/sockios.h>
 #include <linux/net.h>
 #include <linux/fs.h>
-#include <linux/ddi.h>
 #include <linux/malloc.h>
 
 #include <asm/system.h>
@@ -53,9 +52,7 @@
 #include "unix.h"
 
 struct unix_proto_data unix_datas[NSOCKETS];
-static int unix_debug = 0;
 
-
 static int unix_proto_create(struct socket *sock, int protocol);
 static int unix_proto_dup(struct socket *newsock, struct socket *oldsock);
 static int unix_proto_release(struct socket *sock, struct socket *peer);
@@ -95,26 +92,6 @@
 				  char *optval, int *optlen);
 
 
-static void
-dprintf(int level, char *fmt, ...)
-{
-  va_list args;
-  char *buff;
-  extern int vsprintf(char * buf, const char * fmt, va_list args);
-
-  if (level != unix_debug) return;
-
-  buff = (char *) kmalloc(256, GFP_KERNEL);
-  if (buff != NULL) {
-	va_start(args, fmt);
-	vsprintf(buff, fmt, args);
-	va_end(args);
-	printk(buff);
-	kfree(buff);
-  }
-}
-
-
 static inline int
 min(int a, int b)
 {
@@ -123,25 +100,6 @@
 }
 
 
-void
-sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
-{
-  char buf[sizeof(sockun->sun_path) + 1];
-
-  if (unix_debug == 0) return;
-
-  sockaddr_len -= UN_PATH_OFFSET;
-  if (sockun->sun_family != AF_UNIX)
-	printk("UNIX: Badd addr family %d>\n", sockun->sun_family);
-    else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf))
-	printk("UNIX: Bad addr len %d>\n", sockaddr_len);
-    else {
-	memcpy(buf, sockun->sun_path, sockaddr_len);
-	buf[sockaddr_len] = '\0';
-	printk("\"%s\"[%lu]\n", buf, sockaddr_len + UN_PATH_OFFSET);
-  }
-}
-  
 
 /* Support routines doing anti page fault locking 
  * FvK & Matt Dillon (borrowed From NET2E3)
@@ -152,7 +110,7 @@
  * wait queue because it is allowed to 'go away' outside of our control,
  * whereas unix_proto_data structures stick around.
  */
-void unix_lock(struct unix_proto_data *upd)
+static void unix_lock(struct unix_proto_data *upd)
 {
 	while (upd->lock_flag)
 		sleep_on(&upd->wait);
@@ -160,7 +118,7 @@
 }
 
 
-void unix_unlock(struct unix_proto_data *upd)
+static void unix_unlock(struct unix_proto_data *upd)
 {
 	upd->lock_flag = 0;
 	wake_up(&upd->wait);
@@ -276,11 +234,9 @@
 unix_data_ref(struct unix_proto_data *upd)
 {
   if (!upd) {
-    dprintf(1, "UNIX: data_ref: upd = NULL\n");
     return;
   }
   ++upd->refcnt;
-  dprintf(1, "UNIX: data_ref: refing data 0x%x(%d)\n", upd, upd->refcnt);
 }
 
 
@@ -288,11 +244,9 @@
 unix_data_deref(struct unix_proto_data *upd)
 {
   if (!upd) {
-    dprintf(1, "UNIX: data_deref: upd = NULL\n");
     return;
   }
   if (upd->refcnt == 1) {
-	dprintf(1, "UNIX: data_deref: releasing data 0x%x\n", upd);
 	if (upd->buf) {
 		free_page((unsigned long)upd->buf);
 		upd->buf = NULL;
@@ -312,9 +266,7 @@
 {
   struct unix_proto_data *upd;
 
-  dprintf(1, "UNIX: create: socket 0x%x, proto %d\n", sock, protocol);
   if (protocol != 0) {
-	dprintf(1, "UNIX: create: protocol != 0\n");
 	return(-EINVAL);
   }
   if (!(upd = unix_data_alloc())) {
@@ -330,7 +282,6 @@
   upd->socket = sock;
   UN_DATA(sock) = upd;
   upd->refcnt = 1;	/* Now its complete - bgm */
-  dprintf(1, "UNIX: create: allocated data 0x%x\n", upd);
   return(0);
 }
 
@@ -349,14 +300,12 @@
 {
   struct unix_proto_data *upd = UN_DATA(sock);
 
-  dprintf(1, "UNIX: release: socket 0x%x, unix_data 0x%x\n", sock, upd);
   if (!upd) return(0);
   if (upd->socket != sock) {
 	printk("UNIX: release: socket link mismatch!\n");
 	return(-EINVAL);
   }
   if (upd->inode) {
-	dprintf(1, "UNIX: release: releasing inode 0x%x\n", upd->inode);
 	iput(upd->inode);
 	upd->inode = NULL;
   }
@@ -387,10 +336,8 @@
   int i;
   int er;
 
-  dprintf(1, "UNIX: bind: socket 0x%x, len=%d\n", sock, sockaddr_len);
   if (sockaddr_len <= UN_PATH_OFFSET ||
       sockaddr_len > sizeof(struct sockaddr_un)) {
-	dprintf(1, "UNIX: bind: bad length %d\n", sockaddr_len);
 	return(-EINVAL);
   }
   if (upd->sockaddr_len || upd->inode) {
@@ -403,8 +350,6 @@
   memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
   upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
   if (upd->sockaddr_un.sun_family != AF_UNIX) {
-	dprintf(1, "UNIX: bind: family is %d, not AF_UNIX(%d)\n",
-	       			upd->sockaddr_un.sun_family, AF_UNIX);
 	return(-EINVAL);
   }
 
@@ -421,9 +366,6 @@
   }
   upd->sockaddr_len = sockaddr_len;	/* now its legal */
 
-  dprintf(1, "UNIX: bind: bound socket address: ");
-  sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len);
-  dprintf(1, "to inode 0x%x\n", upd->inode);
   return(0);
 }
 
@@ -445,11 +387,8 @@
   int i;
   int er;
 
-  dprintf(1, "UNIX: connect: socket 0x%x, servlen=%d\n", sock, sockaddr_len);
-
   if (sockaddr_len <= UN_PATH_OFFSET ||
       sockaddr_len > sizeof(struct sockaddr_un)) {
-	dprintf(1, "UNIX: connect: bad length %d\n", sockaddr_len);
 	return(-EINVAL);
   }
   if (sock->state == SS_CONNECTING) return(-EINPROGRESS);
@@ -461,8 +400,6 @@
   memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
   sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
   if (sockun.sun_family != AF_UNIX) {
-	dprintf(1, "UNIX: connect: family is %d, not AF_UNIX(%d)\n",
-	       					sockun.sun_family, AF_UNIX);
 	return(-EINVAL);
   }
 
@@ -479,18 +416,15 @@
   i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
   set_fs(old_fs);
   if (i < 0) {
-	dprintf(1, "UNIX: connect: can't open socket %s\n", fname);
 	return(i);
   }
+	  
   serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode);
   iput(inode);
   if (!serv_upd) {
-	dprintf(1, "UNIX: connect: can't locate peer %s at inode 0x%x\n",
-								fname, inode);
 	return(-EINVAL);
   }
   if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
-	dprintf(1, "UNIX: connect: can't await connection\n");
 	return(i);
   }
   if (sock->conn) {
@@ -526,9 +460,6 @@
 {
   struct socket *clientsock;
 
-  dprintf(1, "UNIX: accept: socket 0x%x accepted via socket 0x%x\n",
-							sock, newsock);
-
   /*
    * If there aren't any sockets awaiting connection,
    * then wait for one, unless nonblocking.
@@ -537,7 +468,6 @@
 	if (flags & O_NONBLOCK) return(-EAGAIN);
 	interruptible_sleep_on(sock->wait);
 	if (current->signal & ~current->blocked) {
-		dprintf(1, "UNIX: accept: sleep was interrupted\n");
 		return(-ERESTARTSYS);
 	}
   }
@@ -570,10 +500,8 @@
   int len;
   int er;
 
-  dprintf(1, "UNIX: getname: socket 0x%x for %s\n", sock, peer?"peer":"self");
   if (peer) {
 	if (sock->state != SS_CONNECTED) {
-		dprintf(1, "UNIX: getname: socket not connected\n");
 		return(-EINVAL);
 	}
 	upd = UN_DATA(sock->conn);
@@ -608,14 +536,11 @@
   upd = UN_DATA(sock);
   while(!(avail = UN_BUF_AVAIL(upd))) {
 	if (sock->state != SS_CONNECTED) {
-		dprintf(1, "UNIX: read: socket not connected\n");
 		return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL);
 	}
-	dprintf(1, "UNIX: read: no data available...\n");
 	if (nonblock) return(-EAGAIN);
 	interruptible_sleep_on(sock->wait);
 	if (current->signal & ~current->blocked) {
-		dprintf(1, "UNIX: read: interrupted\n");
 		return(-ERESTARTSYS);
 	}
   }
@@ -637,8 +562,6 @@
 
 	if ((cando = todo) > avail) cando = avail;
 	if (cando >(part = BUF_SIZE - upd->bp_tail)) cando = part;
-	dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n",
-	       					avail, todo, cando);
 	if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0)
 	{
 		unix_unlock(upd);
@@ -671,7 +594,6 @@
 
   if ((todo = size) <= 0) return(0);
   if (sock->state != SS_CONNECTED) {
-	dprintf(1, "UNIX: write: socket not connected\n");
 	if (sock->state == SS_DISCONNECTING) {
 		send_sig(SIGPIPE, current, 1);
 		return(-EPIPE);
@@ -681,15 +603,12 @@
   pupd = UN_DATA(sock)->peerupd;	/* safer than sock->conn */
 
   while(!(space = UN_BUF_SPACE(pupd))) {
-	dprintf(1, "UNIX: write: no space left...\n");
 	if (nonblock) return(-EAGAIN);
 	interruptible_sleep_on(sock->wait);
 	if (current->signal & ~current->blocked) {
-		dprintf(1, "UNIX: write: interrupted\n");
 		return(-ERESTARTSYS);
 	}
 	if (sock->state == SS_DISCONNECTING) {
-		dprintf(1, "UNIX: write: disconnected(SIGPIPE)\n");
 		send_sig(SIGPIPE, current, 1);
 		return(-EPIPE);
 	}
@@ -722,8 +641,6 @@
 	}
 	if ((cando = todo) > space) cando = space;
 	if (cando >(part = BUF_SIZE - pupd->bp_head)) cando = part;
-	dprintf(1, "UNIX: write: space=%d, todo=%d, cando=%d\n",
-	       					space, todo, cando);
 	er=verify_area(VERIFY_READ, ubuf, cando);
 	if(er)
 	{
@@ -751,25 +668,19 @@
   /* Handle server sockets specially. */
   if (sock->flags & SO_ACCEPTCON) {
 	if (sel_type == SEL_IN) {
-		dprintf(1, "UNIX: select: %sconnections pending\n",
-		       				sock->iconn ? "" : "no ");
 		if (sock->iconn) return(1);
 		select_wait(sock->wait, wait);
 		return(sock->iconn ? 1 : 0);
 	}
-	dprintf(1, "UNIX: select: nothing else for server socket\n");
 	select_wait(sock->wait, wait);
 	return(0);
   }
 
   if (sel_type == SEL_IN) {
 	upd = UN_DATA(sock);
-	dprintf(1, "UNIX: select: there is%s data available\n",
-	       					UN_BUF_AVAIL(upd) ? "" : " no");
 	if (UN_BUF_AVAIL(upd))	/* even if disconnected */
 			return(1);
 	else if (sock->state != SS_CONNECTED) {
-		dprintf(1, "UNIX: select: socket not connected(read EOF)\n");
 		return(1);
 	}
 	select_wait(sock->wait,wait);
@@ -777,19 +688,15 @@
   }
   if (sel_type == SEL_OUT) {
 	if (sock->state != SS_CONNECTED) {
-		dprintf(1, "UNIX: select: socket not connected(write EOF)\n");
 		return(1);
 	}
 	peerupd = UN_DATA(sock->conn);
-	dprintf(1, "UNIX: select: there is%s space available\n",
-	       				UN_BUF_SPACE(peerupd) ? "" : " no");
 	if (UN_BUF_SPACE(peerupd) > 0) return(1);
 	select_wait(sock->wait,wait);
 	return(0);
   }
 
   /* SEL_EX */
-  dprintf(1, "UNIX: select: there are no exceptions here?!\n");
   return(0);
 }
 
@@ -831,74 +738,6 @@
 }
 
 
-static int
-unix_open(struct inode * inode, struct file * file)
-{
-  int minor;
-
-  dprintf(1, "UNIX: open\n");
-  minor = MINOR(inode->i_rdev);
-  if (minor != 0) return(-ENODEV);
-
-  return(0);
-}
-
-
-static void
-unix_close(struct inode * inode, struct file * file)
-{
-  dprintf(1, "UNIX: close\n");
-}
-
-
-static int
-unix_ioctl(struct inode *inode, struct file *file,
-	 unsigned int cmd, unsigned long arg)
-{
-  int minor, ret;
-  int er;
-
-  dprintf(1, "UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg);
-  minor = MINOR(inode->i_rdev);
-  if (minor != 0) return(-ENODEV);
-
-  ret = -EINVAL;
-  switch(cmd) {
-	case DDIOCSDBG:
-		er=verify_area(VERIFY_READ,(void *)arg, sizeof(int));
-		if(er)
-			return er;
-		unix_debug = get_fs_long((int *)arg);
-		if (unix_debug != 0 && unix_debug != 1) {
-			unix_debug = 0;
-			return(-EINVAL);
-		}
-		return(0);
-	case SIOCSIFLINK:
-		printk("UNIX: cannot link streams!\n");
-		break;
-	default:
-		break;
-  }
-  return(ret);
-}
-
-
-
-
-static struct file_operations unix_fops = {
-  NULL,		/* LSEEK	*/
-  NULL,		/* READ		*/
-  NULL,		/* WRITE	*/
-  NULL,		/* READDIR	*/
-  NULL,		/* SELECT	*/
-  unix_ioctl,	/* IOCTL	*/
-  NULL,		/* MMAP		*/
-  unix_open,	/* OPEN		*/
-  unix_close	/* CLOSE	*/
-};
-
-
 static struct proto_ops unix_proto_ops = {
   AF_UNIX,
   unix_proto_create,
@@ -926,16 +765,9 @@
 
 
 void
-unix_proto_init(struct ddi_proto *pro)
+unix_proto_init(struct net_proto *pro)
 {
   struct unix_proto_data *upd;
-
-  dprintf(1, "%s: init: initializing...\n", pro->name);
-  if (register_chrdev(AF_UNIX_MAJOR, "af_unix", &unix_fops) < 0) {
-	printk("%s: cannot register major device %d!\n",
-						pro->name, AF_UNIX_MAJOR);
-	return;
-  }
 
   /* Tell SOCKET that we are alive... */
   (void) sock_register(unix_proto_ops.family, &unix_proto_ops);
diff -u --recursive --new-file v1.1.12/linux/net/unix/unix.h linux/net/unix/unix.h
--- v1.1.12/linux/net/unix/unix.h	Fri Mar  4 09:25:21 1994
+++ linux/net/unix/unix.h	Mon May 23 08:17:55 1994
@@ -66,4 +66,4 @@
 #endif	/* _LINUX_UN_H */
 
 
-extern void	unix_proto_init(struct ddi_proto *pro);
+extern void	unix_proto_init(struct net_proto *pro);
