diff -u --recursive --new-file v2.1.19/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.19/linux/Documentation/Configure.help	Tue Dec 31 21:40:59 1996
+++ linux/Documentation/Configure.help	Tue Dec 31 21:13:09 1996
@@ -1638,9 +1638,10 @@
   of PCI-SCSI controllers. This driver supports parity checking,
   tagged command queuing, fast scsi II transfer up to 10 MB/s with
   narrow scsi devices and 20 MB/s with wide scsi devices.
-  Linux/i386 and Linux/Alpha are supported by this driver.
-  Memory mapped io is currently untested under Linux/Alpha.
+  Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 
+  controllers has been recently added to the driver.
   Please read drivers/scsi/README.ncr53c8xx for more information.
+  Linux/i386 and Linux/Alpha are supported by this driver.
 
 synchronous data transfers frequency
 CONFIG_SCSI_NCR53C8XX_SYNC
@@ -1702,43 +1703,11 @@
   possible.  The default value is 4. Minimum is 2, maximum is 12.  The
   normal answer therefore is the default one.
 
-force asynchronous transfer mode
-CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-  This option allows you to force asynchronous transfer mode for all
-  SCSI devices at linux startup. You can enable synchronous
-  negotiation with the "setsync" control command after boot-up, for
-  example:
-     echo "setsync 2 25" >/proc/scsi/ncr53c8xx/0
-  asks the driver to set the period to 25 ns (10MB/sec) for target 2
-  of controller 0 (please read drivers/scsi/README.ncr53c8xx for more
-  information). The safe answer therefore is Y.  The normal answer
-  therefore is N.
-
-force synchronous negotiation
-CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-  Some scsi-2 devices support synchronous negotiations but do not
-  report this feature in byte 7 of inquiry data.
-  Answer Y only if you suspect some device to be so humble.
-  The normal answer therefore is N.
- 
 IBMMCA SCSI support
 CONFIG_SCSI_IBMMCA
   If your computer sports an MCA bus system architecture (IBM PS/2)
   with an SCSI harddrive, say Y here.
 
-disable master parity checking
-CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-  Some hardware may have problems with parity during master cycles on
-  PCI bus.  Only seen once. Answer Y if you suspect such problem.  The
-  normal answer therefore is N.
-
-disable scsi parity checking
-CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-  Parity on scsi bus is a system option. If one device checks parity,
-  then all devices on the scsi bus must generate parity.  However, the
-  parity can be ignored by the scsi devices.  Answer Y only if you
-  know what you are doing.  The normal answer therefore is N.
-
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
   This is support for an ISA bus SCSI host adaptor. You'll find
@@ -3697,9 +3666,12 @@
 Do you want runtime configuration for QIC-02
 CONFIG_QIC02_DYNCONF
   You can either configure this driver once and for all by editing a
-  header file, in which case you should say N, or you can fetch a
-  program via anonymous ftp which is able to configure this driver
-  during runtime. If you want this, say Y.
+  header file (include/linux/tpqic02.h), in which case you should
+  say N, or you can fetch a program via anonymous ftp which is able
+  to configure this driver during runtime. The program to do this is
+  called 'qic02conf' and it is part of the 'tpqic02-support-X.Y.tar.gz'
+  support package.
+  If you want to use the qic02conf program, say Y.
 
 Ftape (QIC-80/Travan) support
 CONFIG_FTAPE
diff -u --recursive --new-file v2.1.19/linux/Documentation/networking/00-INDEX linux/Documentation/networking/00-INDEX
--- v2.1.19/linux/Documentation/networking/00-INDEX	Thu Dec 12 19:36:57 1996
+++ linux/Documentation/networking/00-INDEX	Thu Jan  2 15:13:24 1997
@@ -14,6 +14,8 @@
 	- info on using AX.25 and NET/ROM code for Linux
 framerelay.txt
 	- info on using Frame Relay/Data Link Connection Identifier (DLCI).
+lapb-module.txt
+	- programming information on the LAPB module.
 ncsa-telnet
 	- notes on how NCSA telnet (DOS) breaks with MTU discovery enabled.
 net-modules.txt
@@ -28,6 +30,10 @@
 	- info on using DEC 21040/21041/21140 based PCI ethernet cards.
 vortex.txt
 	- info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards.
+x25.txt
+	- general info on X.25 development.
+x25-iface.txt
+	- description of the X.25 Packet Layer to LAPB device interface.
 z8530drv.txt
 	- info about Linux driver for Z8530 based HDLC cards for AX.25
 
diff -u --recursive --new-file v2.1.19/linux/Documentation/networking/lapb-module.txt linux/Documentation/networking/lapb-module.txt
--- v2.1.19/linux/Documentation/networking/lapb-module.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/lapb-module.txt	Thu Jan  2 15:13:24 1997
@@ -0,0 +1,257 @@
+		The Linux LAPB Module Interface 1.3
+
+		      Jonathan Naylor 29.12.96
+
+The LAPB module will be a seperately compiled module for use by any parts of
+the Linux operating system that require a LAPB service. This document
+defines the interfaces to, and the services provided by this module. The
+term module in this context does not imply that the LAPB module is a
+seperately loadable module, although it may be. The term module is used in
+its more standard meaning.
+
+The interface to the LAPB module consists of functions to the module,
+callbacks from the module to indicate important state changes, and
+structures for getting and setting information about the module.
+
+Structures
+----------
+
+Probably the most important structure is the skbuff structure for holding
+received and transmitted data, however it is beyond the scope of this
+document.
+
+The two LAPB specific structures are the LAPB initialisation structure and
+the LAPB parameter structure. These will be defined in a standard header
+file, <linux/lapb.h>. The header file <net/lapb.h> is internal to the LAPB
+module and is not for use.
+
+LAPB Initialisation Structure
+-----------------------------
+
+This structure is used only once, in the call to lapb_register (see below).
+It contains information about the device driver that requires the services
+of the LAPB module.
+
+struct lapb_register_struct {
+	void (*connect_confirmation)(int token, int reason);
+	void (*connect_indication)(int token, int reason);
+	void (*disconnect_confirmation)(int token, int reason);
+	void (*disconnect_indication)(int token, int reason);
+	void (*data_indication)(int token, struct sk_buff *skb);
+	void (*data_transmit)(int token, struct sk_buff *skb);
+};
+
+Each member of this structure corresponds to a function in the device driver
+that is called when a particular event in the LAPB module occurs. These will
+be described in detail below. If a callback is not required (!!) then a NULL
+may be substituted.
+
+
+LAPB Parameter Structure
+------------------------
+
+This structure is used with the lapb_getparms and lapb_setparms functions
+(see below). They are used to allow the device driver to get and set the
+operational parameters of the LAPB implementation for a given connection.
+
+struct lapb_parms_struct {
+	unsigned int t1;
+	unsigned int t1timer;
+	unsigned int t2;
+	unsigned int t2timer;
+	unsigned int n2;
+	unsigned int n2count;
+	unsigned int window;
+	unsigned int state;
+	unsigned int mode;
+};
+
+T1 and T2 are protocol timing parameters and are given in units of 100ms. N2
+is the maximum number of tries on the link before it is declared a failure.
+The window size is the maximum number of outstanding data packets allowed to
+be unacknowledged by the remote end, the value of the window is between 1
+and 7 for a standard LAPB link, and between 1 and 127 for an extended LAPB
+link.
+
+The mode variable is a bit field is used for setting (at present) three values.
+The bit fields have the following meanings:
+
+Bit	Meaning
+0	LAPB operation (0=LAPB_STANDARD 1=LAPB_EXTENDED).
+1	[SM]LP operation (0=LAPB_SLP 1=LAPB=MLP).
+2	DTE/DCE operation (0=LAPB_DTE 1=LAPB_DCE)
+3-31	Reserved, must be 0.
+
+Extended LAPB operation indicates the use of extended sequence numbers and
+consequently larger window sizes, the default is standard LAPB operation.
+MLP operation is the same as SLP operation except that the addresses used by
+LAPB are different to indicate the mode of operation, the default is Single
+Link Procedure. The difference between DCE and DTE operation is (i) the
+addresses used for commands and responses, and (ii) when the DCE is not
+connected, it sends DM without polls set, every T1. The upper case constant
+names will be defined in the public LAPB header file.
+
+
+Functions
+---------
+
+The LAPB module provides a number of function entry points.
+
+
+int lapb_register(void *token, struct lapb_register_struct);
+
+This must be called before the LAPB module may be used. If the call is
+successful then LAPB_OK is returned. The token must be a unique identifier
+generated by the device driver to allow for the unique identification of the
+instance of the LAPB link. It is returned by the LAPB module in all of the
+callbacks, and is used by the device driver in all calls to the LAPB module.
+For multiple LAPB links in a single device driver, multiple calls to
+lapb_register must be made. The format of the lapb_register_struct is given
+above. The return values are:
+
+LAPB_OK			LAPB registered successfully.
+LAPB_BADTOKEN		Token is already registered.
+LAPB_NOMEM		Out of memory
+
+
+int lapb_unregister(void *token);
+
+This releases all the resources associated with a LAPB link. Any current
+LAPB link will be abandoned without further messages being passed. After
+this call, the value of token is no longer valid for any calls to the LAPB
+function. The valid return values are:
+
+LAPB_OK			LAPB unregistered successfully.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+
+
+int lapb_getparms(void *token, struct lapb_parms_struct *parms);
+
+This allows the device driver to get the values of the current LAPB
+variables, the lapb_parms_struct is described above. The valid return values
+are:
+
+LAPB_OK			LAPB getparms was successful.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+
+
+int lapb_setparms(void *token, struct lapb_parms_struct *parms);
+
+This allows the device driver to set the values of the current LAPB
+variables, the lapb_parms_struct is described above. The values of t1timer,
+t2timer and n2count are ignored, likewise changing the mode bits when
+connected will be ignored. An error implies that none of the values have
+been changed. The valid return values are:
+
+LAPB_OK			LAPB getparms was successful.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+LAPB_INVALUE		One of the values was out of its allowable range.
+
+
+int lapb_connect_request(void *token);
+
+Initiate a connect using the current parameter settings. The valid return
+values are:
+
+LAPB_OK			LAPB is starting to connect.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+LAPB_CONNECTED		LAPB module is already connected.
+
+
+int lapb_disconnect_request(void *token);
+
+Initiate a disconnect. The valid return values are:
+
+LAPB_OK			LAPB is starting to disconnect.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+LAPB_NOTCONNECTED	LAPB module is not connected.
+
+
+int lapb_data_request(void *token, struct sk_buff *skb);
+
+Queue data with the LAPB module for transmitting over the link. If the call
+is successful then the skbuff is owned by the LAPB module and may not be
+used by the device driver again. The valid return values are:
+
+LAPB_OK			LAPB has accepted the data.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+LAPB_NOTCONNECTED	LAPB module is not connected.
+
+
+int lapb_data_received(void *token, struct sk_buff *skb);
+
+Queue data with the LAPB module which has been received from the device. It
+is expected that the data passed to the LAPB module has skb->data pointing
+to the beginning of the LAPB data. If the call is successful then the skbuff
+is owned by the LAPB module and may not be used by the device driver again.
+The valid return values are:
+
+LAPB_OK			LAPB has accepted the data.
+LAPB_BADTOKEN		Invalid/unknown LAPB token.
+
+
+Callbacks
+---------
+
+These callbacks are functions provided by the device driver for the LAPB
+module to call when an event occurs. They are registered with the LAPB
+module with lapb_register (see above) in the structure lapb_register_struct
+(see above).
+
+
+void (*connect_confirmation)(void *token, int reason);
+
+This is called by the LAPB module when a connection is established after
+being requested by a call to lapb_connect_request (see above). The reason is
+always LAPB_OK.
+
+
+void (*connect_indication)(void *token, int reason);
+
+This is called by the LAPB module when the link is established by the remote
+system. The value of reason is always LAPB_OK.
+
+
+void (*disconnect_confirmation)(void *token, int reason);
+
+This is called by the LAPB module when an event occurs after the device
+driver has called lapb_disconnect_request (see above). The reason indicates
+what has happended. In all cases the LAPB link can be regarded as being
+terminated. The values for reason are:
+
+LAPB_OK			The LAPB link was terminated normally.
+LAPB_NOTCONNECTED	The remote system was not connected.
+LAPB_TIMEDOUT		No response was received in N2 tries from the remote
+			system.
+
+
+void (*disconnect_indication)(void *token, int reason);
+
+This is called by the LAPB module when the link is terminated by the remote
+system or another event has occurred to terminate the link. This may be
+returned in response to a lapb_connect_request (see above) if the remote
+system refused the request. The values for reason are:
+
+LAPB_OK			The LAPB link was terminated normally by the remote
+			system.
+LAPB_REFUSED		The remote system refused the connect request.
+LAPB_NOTCONNECTED	The remote system was not connected.
+LAPB_TIMEDOUT		No response was received in N2 tries from the remote
+			system.
+
+
+void (*data_indication)(void *token, struct sk_buff *skb);
+
+This is called by the LAPB module when data has been received from the
+remote system that should be passed onto the next layer in the protocol
+stack. The skbuff becomes the property of the device driver and the LAPB
+module will not perform any more actions on it. The skb->data pointer will
+be pointing to the first byte of data after the LAPB header.
+
+
+void (*data_transmit)(void *token, struct sk_buff *skb);
+
+This is called by the LAPB module when data is to be transmitted to the
+remote system by the device driver. The skbuff becomes the property of the
+device driver and the LAPB module will not perform any more actions on it.
+The skb->data pointer will be pointing to the first byte of the LAPB header.
diff -u --recursive --new-file v2.1.19/linux/Documentation/networking/x25-iface.txt linux/Documentation/networking/x25-iface.txt
--- v2.1.19/linux/Documentation/networking/x25-iface.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/x25-iface.txt	Thu Jan  2 15:13:24 1997
@@ -0,0 +1,64 @@
+			X.25 Device Driver Interface 1.1
+
+			   Jonathan Naylor 26.12.96
+
+This is a description of the messages to be passed between the X.25 Packet
+Layer and the X.25 device driver. They are designed to allow for the easy
+setting of the LAPB mode from within the Packet Layer.
+
+The X.25 device driver will be coded normally as per the Linux device driver
+standards, most X.25 device drivers will be moderately similar to the
+already existing Eethernet device drivers. However unlike those drivers, the
+X.25 device driver has a state associated with it, and this information
+needs to be passed to and from the Packet Layer for proper operation.
+
+All messages are held in sk_buff's just like real data to be transmitted
+over the LAPB link. The first byte of the skbuff indicates the meaning of
+the rest of the skbuff, if any more information does exist.
+
+
+Packet Layer to Device Driver
+-----------------------------
+
+First Byte = 0x00
+
+This indicates that the rest of the skbuff contains data to be transmitted
+over the LAPB link. The LAPB link should already exist before any data is
+passed down.
+
+First Byte = 0x01
+
+Establish the LAPB link. If the link is already established then the connect
+confirmation message should be returned as soon as possible.
+
+First Byte = 0x02
+
+Terminate the LAPB link. If it is already disconnected then the disconnect
+confirmation message should be returned as soon as possible.
+
+First Byte = 0x03
+
+LAPB parameters. To be defined.
+
+
+Device Driver to Packet Layer
+-----------------------------
+
+First Byte = 0x00
+
+This indicates that the rest of the skbuff contains data that has been
+received over the LAPB link.
+
+First Byte = 0x01
+
+LAPB link has been established. The same message is used for both a LAPB
+link connect_confirmation and a connect_indication.
+
+First Byte = 0x02
+
+LAPB link has been terminated. This same message is used for both a LAPB
+link disconnect_confirmation and a disconnect_indication.
+
+First Byte = 0x03
+
+LAPB parameters. To be defined.
diff -u --recursive --new-file v2.1.19/linux/Documentation/networking/x25.txt linux/Documentation/networking/x25.txt
--- v2.1.19/linux/Documentation/networking/x25.txt	Wed Dec 18 15:58:34 1996
+++ linux/Documentation/networking/x25.txt	Thu Jan  2 15:13:24 1997
@@ -25,12 +25,19 @@
 
 Just when you thought that it could not become more confusing, another
 option appeared, XOT. This allows X.25 Packet Layer frames to operate over
-the Internet using TCP/IP as a reliable link layer. An RFC exists that
-specifies the format and behaviour of the protocol. If time permits this
-option will also be actively considered.
+the Internet using TCP/IP as a reliable link layer. RFC1613 specifies the
+format and behaviour of the protocol. If time permits this option will also
+be actively considered.
 
-It is hoped that a linux-x25 mailing list will be opened soon to allow for
-the discussion of Linux X.25, but it hasn't appeared yet.
+A linux-x25 mailing list has been created at vger.rutgers.edu to support the
+development and use of Linux X.25. It is early days yet, but interested
+parties are welcome to subscribe to it. Just send a message to
+Majordomo@vger.rutgers.edu with the following in the message body:
+
+subscribe linux-x25
+end
+
+The contents of the Subject line are ignored.
 
 Jonathan
 
diff -u --recursive --new-file v2.1.19/linux/Makefile linux/Makefile
--- v2.1.19/linux/Makefile	Tue Dec 31 21:40:59 1996
+++ linux/Makefile	Thu Jan  2 15:03:22 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 19
+SUBLEVEL = 20
 
 ARCH = i386
 
diff -u --recursive --new-file v2.1.19/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.19/linux/arch/i386/defconfig	Sun Dec 22 16:37:20 1996
+++ linux/arch/i386/defconfig	Thu Jan  2 15:19:40 1997
@@ -117,6 +117,7 @@
 # CONFIG_PLIP is not set
 # CONFIG_PPP is not set
 # CONFIG_NET_RADIO is not set
+# CONFIG_LAPBETHER is not set
 # CONFIG_SLIP is not set
 # CONFIG_TR is not set
 
diff -u --recursive --new-file v2.1.19/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
--- v2.1.19/linux/arch/i386/kernel/ldt.c	Thu Dec 12 19:36:57 1996
+++ linux/arch/i386/kernel/ldt.c	Wed Jan  1 16:58:41 1997
@@ -16,7 +16,6 @@
 
 static int read_ldt(void * ptr, unsigned long bytecount)
 {
-	int error;
 	void * address = current->ldt;
 	unsigned long size;
 
@@ -29,11 +28,7 @@
 	}
 	if (size > bytecount)
 		size = bytecount;
-	error = verify_area(VERIFY_WRITE, ptr, size);
-	if (error)
-		return error;
-	copy_to_user(ptr, address, size);
-	return size;
+	return copy_to_user(ptr, address, size) ? -EFAULT : size;
 }
 
 static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info)
@@ -69,11 +64,9 @@
 
 	if (bytecount != sizeof(ldt_info))
 		return -EINVAL;
-	error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info));
+	error = copy_from_user(&ldt_info, ptr, sizeof(ldt_info));
 	if (error)
-		return error;
-
-	copy_from_user(&ldt_info, ptr, sizeof(ldt_info));
+		return -EFAULT; 	
 
 	if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
 		return -EINVAL;
diff -u --recursive --new-file v2.1.19/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.19/linux/arch/i386/kernel/process.c	Sun Dec 22 16:37:20 1996
+++ linux/arch/i386/kernel/process.c	Thu Jan  2 15:13:24 1997
@@ -184,16 +184,90 @@
  */
 static long no_idt[2] = {0, 0};
 static int reboot_mode = 0;
+static int reboot_thru_bios = 0;
 
 void reboot_setup(char *str, int *ints)
 {
-	int mode = 0;
+	while(1) {
+		switch (*str) {
+		case 'w': /* "warm" reboot (no memory testing etc) */
+			reboot_mode = 0x1234;
+			break;
+		case 'c': /* "cold" reboot (with memory testing etc) */
+			reboot_mode = 0x0;
+			break;
+		case 'b': /* "bios" reboot by jumping through the BIOS */
+			reboot_thru_bios = 1;
+			break;
+		case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */
+			reboot_thru_bios = 0;
+			break;
+		}
+		if((str = strchr(str,',')) != NULL)
+			str++;
+		else
+			break;
+	}
+}
+
+
+/* The following code and data reboots the machine by switching to real
+   mode and jumping to the BIOS reset entry point, as if the CPU has
+   really been reset.  The previous version asked the keyboard
+   controller to pulse the CPU reset line, which is more thorough, but
+   doesn't work with at least one type of 486 motherboard.  It is easy
+   to stop this code working; hence the copious comments. */
 
-	/* "w" for "warm" reboot (no memory testing etc) */
-	if (str[0] == 'w')
-		mode = 0x1234;
-	reboot_mode = mode;
+unsigned long long
+real_mode_gdt_entries [3] =
+{
+	0x0000000000000000ULL,	/* Null descriptor */
+	0x00009a000000ffffULL,	/* 16-bit real-mode 64k code at 0x00000000 */
+	0x000092000100ffffULL		/* 16-bit real-mode 64k data at 0x00000100 */
+};
+
+struct
+{
+	unsigned short       size __attribute__ ((packed));
+	unsigned long long * base __attribute__ ((packed));
 }
+real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
+real_mode_idt = { 0x3ff, 0 };
+
+/* This is 16-bit protected mode code to disable paging and the cache,
+   switch to real mode and jump to the BIOS reset code.
+
+   The instruction that switches to real mode by writing to CR0 must be
+   followed immediately by a far jump instruction, which set CS to a
+   valid value for real mode, and flushes the prefetch queue to avoid
+   running instructions that have already been decoded in protected
+   mode.
+
+   Clears all the flags except ET, especially PG (paging), PE
+   (protected-mode enable) and TS (task switch for coprocessor state
+   save).  Flushes the TLB after paging has been disabled.  Sets CD and
+   NW, to disable the cache on a 486, and invalidates the cache.  This
+   is more like the state of a 486 after reset.  I don't know if
+   something else should be done for other chips.
+
+   More could be done here to set up the registers as if a CPU reset had
+   occurred; hopefully real BIOSes don't assume much. */
+
+unsigned char real_mode_switch [] =
+{
+	0x66, 0x0f, 0x20, 0xc0,			/*    movl  %cr0,%eax        */
+	0x66, 0x83, 0xe0, 0x11,			/*    andl  $0x00000011,%eax */
+	0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,		/*    orl   $0x60000000,%eax */
+	0x66, 0x0f, 0x22, 0xc0,			/*    movl  %eax,%cr0        */
+	0x66, 0x0f, 0x22, 0xd8,			/*    movl  %eax,%cr3        */
+	0x66, 0x0f, 0x20, 0xc3,			/*    movl  %cr0,%ebx        */
+	0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60,	/*    andl  $0x60000000,%ebx */
+	0x74, 0x02,					/*    jz    f                */
+	0x0f, 0x08,					/*    invd                   */
+	0x24, 0x10,					/* f: andb  $0x10,al         */
+	0x66, 0x0f, 0x22, 0xc0,			/*    movl  %eax,%cr0        */
+	0xea, 0x00, 0x00, 0xff, 0xff			/*    ljmp  $0xffff,$0x0000  */
+};
 
 static inline void kb_wait(void)
 {
@@ -204,22 +278,107 @@
 			break;
 }
 
-void hard_reset_now(void)
+void hard_reset_now (void)
 {
-	int i, j;
 
-	sti();
-	*((unsigned short *)__va(0x472)) = reboot_mode;
-	for (;;) {
-		for (i=0; i<100; i++) {
-			kb_wait();
-			for(j = 0; j < 100000 ; j++)
-				/* nothing */;
-			outb(0xfe,0x64);	 /* pulse reset low */
-			udelay(100);
+	if(!reboot_thru_bios) {
+		sti();
+		/* rebooting needs to touch the page at absolute addr 0 */
+		pg0[0] = 7;
+		*((unsigned short *)0x472) = reboot_mode;
+		for (;;) {
+			int i;
+			for (i=0; i<100; i++) {
+				int j;
+				kb_wait();
+				for(j = 0; j < 100000 ; j++)
+					/* nothing */;
+				outb(0xfe,0x64);         /* pulse reset low */
+				udelay(10);
+			}
+			__asm__ __volatile__("\tlidt %0": "=m" (no_idt));
 		}
-		__asm__ __volatile__("\tlidt %0": "=m" (no_idt));
 	}
+
+	cli ();
+
+	/* Write zero to CMOS register number 0x0f, which the BIOS POST
+	   routine will recognize as telling it to do a proper reboot.  (Well
+	   that's what this book in front of me says -- it may only apply to
+	   the Phoenix BIOS though, it's not clear).  At the same time,
+	   disable NMIs by setting the top bit in the CMOS address register,
+	   as we're about to do peculiar things to the CPU.  I'm not sure if
+	   `outb_p' is needed instead of just `outb'.  Use it to be on the
+	   safe side. */
+
+	outb_p (0x8f, 0x70);
+	outb_p (0x00, 0x71);
+
+	/* Remap the kernel at virtual address zero, as well as offset zero
+	   from the kernel segment.  This assumes the kernel segment starts at
+	   virtual address 0xc0000000. */
+
+	memcpy (swapper_pg_dir, swapper_pg_dir + 768,
+		sizeof (swapper_pg_dir [0]) * 256);
+
+	/* Make sure the first page is mapped to the start of physical memory.
+	   It is normally not mapped, to trap kernel NULL pointer dereferences. */
+
+	pg0 [0] = 7;
+
+	/* Use `swapper_pg_dir' as our page directory.  Don't bother with
+	   `SET_PAGE_DIR' because interrupts are disabled and we're rebooting.
+	   This instruction flushes the TLB. */
+
+	__asm__ __volatile__ ("movl %0,%%cr3" : : "a" (swapper_pg_dir) : "memory");
+
+	/* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
+	   this on booting to tell it to "Bypass memory test (also warm
+	   boot)".  This seems like a fairly standard thing that gets set by
+	   REBOOT.COM programs, and the previous reset routine did this
+	   too. */
+
+	*((unsigned short *)0x472) = reboot_mode;
+
+	/* For the switch to real mode, copy some code to low memory.  It has
+	   to be in the first 64k because it is running in 16-bit mode, and it
+	   has to have the same physical and virtual address, because it turns
+	   off paging.  Copy it near the end of the first page, out of the way
+	   of BIOS variables. */
+
+	memcpy ((void *) (0x1000 - sizeof (real_mode_switch)),
+		real_mode_switch, sizeof (real_mode_switch));
+
+	/* Set up the IDT for real mode. */
+
+	__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
+
+	/* Set up a GDT from which we can load segment descriptors for real
+	   mode.  The GDT is not used in real mode; it is just needed here to
+	   prepare the descriptors. */
+
+	__asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
+
+	/* Load the data segment registers, and thus the descriptors ready for
+	   real mode.  The base address of each segment is 0x100, 16 times the
+	   selector value being loaded here.  This is so that the segment
+	   registers don't have to be reloaded after switching to real mode:
+	   the values are consistent for real mode operation already. */
+
+	__asm__ __volatile__ ("movw $0x0010,%%ax\n"
+				"\tmovw %%ax,%%ds\n"
+				"\tmovw %%ax,%%es\n"
+				"\tmovw %%ax,%%fs\n"
+				"\tmovw %%ax,%%gs\n"
+				"\tmovw %%ax,%%ss" : : : "eax");
+
+	/* Jump to the 16-bit code that we copied earlier.  It disables paging
+	   and the cache, switches to real mode, and jumps to the BIOS reset
+	   entry point. */
+
+	__asm__ __volatile__ ("ljmp $0x0008,%0"
+				:
+				: "i" ((void *) (0x1000 - sizeof (real_mode_switch))));
 }
 
 void show_regs(struct pt_regs * regs)
diff -u --recursive --new-file v2.1.19/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.1.19/linux/arch/i386/kernel/ptrace.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/kernel/ptrace.c	Thu Jan  2 15:43:29 1997
@@ -421,24 +421,17 @@
 			res = read_long(child, addr, &tmp);
 			if (res < 0)
 				return res;
-			res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
-			if (!res)
-				put_user(tmp,(unsigned long *) data);
-			return res;
+			return put_user(tmp,(unsigned long *) data);
 		}
 
 	/* read the word at location addr in the USER area. */
 		case PTRACE_PEEKUSR: {
 			unsigned long tmp;
-			int res;
 
 			if ((addr & 3) || addr < 0 || 
 			    addr > sizeof(struct user) - 3)
 				return -EIO;
 
-			res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
-			if (res)
-				return res;
 			tmp = 0;  /* Default return condition */
 			if(addr < 17*sizeof(long))
 				tmp = getreg(child, addr);
@@ -448,8 +441,7 @@
 				addr = addr >> 2;
 				tmp = child->debugreg[addr];
 			};
-			put_user(tmp,(unsigned long *) data);
-			return 0;
+			return put_user(tmp,(unsigned long *) data);
 		}
 
       /* when I and D space are separate, this will have to be fixed. */
diff -u --recursive --new-file v2.1.19/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.19/linux/arch/i386/kernel/signal.c	Thu Dec 12 19:36:58 1996
+++ linux/arch/i386/kernel/signal.c	Wed Jan  1 16:38:43 1997
@@ -181,50 +181,56 @@
 	if ((regs->xss & 0xffff) != USER_DS && sa->sa_restorer)
 		frame = (unsigned long *) sa->sa_restorer;
 	frame -= 64;
-	if (verify_area(VERIFY_WRITE,frame,64*4))
+	if (!access_ok(VERIFY_WRITE,frame,64*4))
 		do_exit(SIGSEGV);
 
 /* set up the "normal" stack seen by the signal handler (iBCS2) */
 #define __CODE ((unsigned long)(frame+24))
 #define CODE(x) ((unsigned long *) ((x)+__CODE))
-	if (put_user(__CODE,frame))
+	
+    /* XXX Can possible miss a SIGSEGV when frame crosses a page border
+       and a thread unmaps it while we are accessing it. 
+       So either check all put_user() calls or don't do it at all.  
+       We use __put_user() here because the access_ok() call was already
+       done earlier. */  
+	if (__put_user(__CODE,frame))
 		do_exit(SIGSEGV);
 	if (current->exec_domain && current->exec_domain->signal_invmap)
-		put_user(current->exec_domain->signal_invmap[signr], frame+1);
+		__put_user(current->exec_domain->signal_invmap[signr], frame+1);
 	else
-		put_user(signr, frame+1);
+		__put_user(signr, frame+1);
 	{
 		unsigned int tmp = 0;
 #define PUT_SEG(seg, mem) \
-__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); put_user(tmp,mem);
+__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); __put_user(tmp,mem);
 		PUT_SEG(gs, frame+2);
 		PUT_SEG(fs, frame+3);
 	}
-	put_user(regs->xes, frame+4);
-	put_user(regs->xds, frame+5);
-	put_user(regs->edi, frame+6);
-	put_user(regs->esi, frame+7);
-	put_user(regs->ebp, frame+8);
-	put_user(regs->esp, frame+9);
-	put_user(regs->ebx, frame+10);
-	put_user(regs->edx, frame+11);
-	put_user(regs->ecx, frame+12);
-	put_user(regs->eax, frame+13);
-	put_user(current->tss.trap_no, frame+14);
-	put_user(current->tss.error_code, frame+15);
-	put_user(regs->eip, frame+16);
-	put_user(regs->xcs, frame+17);
-	put_user(regs->eflags, frame+18);
-	put_user(regs->esp, frame+19);
-	put_user(regs->xss, frame+20);
-	put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21);
+	__put_user(regs->xes, frame+4);
+	__put_user(regs->xds, frame+5);
+	__put_user(regs->edi, frame+6);
+	__put_user(regs->esi, frame+7);
+	__put_user(regs->ebp, frame+8);
+	__put_user(regs->esp, frame+9);
+	__put_user(regs->ebx, frame+10);
+	__put_user(regs->edx, frame+11);
+	__put_user(regs->ecx, frame+12);
+	__put_user(regs->eax, frame+13);
+	__put_user(current->tss.trap_no, frame+14);
+	__put_user(current->tss.error_code, frame+15);
+	__put_user(regs->eip, frame+16);
+	__put_user(regs->xcs, frame+17);
+	__put_user(regs->eflags, frame+18);
+	__put_user(regs->esp, frame+19);
+	__put_user(regs->xss, frame+20);
+	__put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21);
 /* non-iBCS2 extensions.. */
-	put_user(oldmask, frame+22);
-	put_user(current->tss.cr2, frame+23);
+	__put_user(oldmask, frame+22);
+	__put_user(current->tss.cr2, frame+23);
 /* set up the return code... */
-	put_user(0x0000b858, CODE(0));	/* popl %eax ; movl $,%eax */
-	put_user(0x80cd0000, CODE(4));	/* int $0x80 */
-	put_user(__NR_sigreturn, CODE(2));
+	__put_user(0x0000b858, CODE(0));	/* popl %eax ; movl $,%eax */
+	__put_user(0x80cd0000, CODE(4));	/* int $0x80 */
+	__put_user(__NR_sigreturn, CODE(2));
 #undef __CODE
 #undef CODE
 
diff -u --recursive --new-file v2.1.19/linux/arch/i386/kernel/sys_i386.c linux/arch/i386/kernel/sys_i386.c
--- v2.1.19/linux/arch/i386/kernel/sys_i386.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/kernel/sys_i386.c	Wed Jan  1 16:36:40 1997
@@ -28,15 +28,12 @@
 	int fd[2];
 	int error;
 
-	error = verify_area(VERIFY_WRITE,fildes,8);
-	if (error)
-		return error;
 	error = do_pipe(fd);
-	if (error)
-		return error;
-	put_user(fd[0],0+fildes);
-	put_user(fd[1],1+fildes);
-	return 0;
+	if (!error) {
+		if (copy_to_user(fildes, fd, 2*sizeof(int)))
+			error = -EFAULT;
+	}
+	return error;
 }
 
 /*
@@ -60,10 +57,8 @@
 	struct file * file = NULL;
 	struct mmap_arg_struct a;
 
-	error = verify_area(VERIFY_READ, arg, sizeof(*arg));
-	if (error)
-		return error;
-	copy_from_user(&a, arg, sizeof(a));
+	if (copy_from_user(&a, arg, sizeof(a)))
+			return -EFAULT; 
 	if (!(a.flags & MAP_ANONYMOUS)) {
 		if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd]))
 			return -EBADF;
@@ -110,12 +105,10 @@
 			return sys_semget (first, second, third);
 		case SEMCTL: {
 			union semun fourth;
-			int err;
 			if (!ptr)
 				return -EINVAL;
-			if ((err = verify_area (VERIFY_READ, ptr, sizeof(long))))
-				return err;
-			get_user(fourth.__pad, (void **) ptr);
+			if (get_user(fourth.__pad, (void **) ptr))
+				return -EFAULT;	
 			return sys_semctl (first, second, third, fourth);
 			}
 		default:
@@ -130,12 +123,11 @@
 			switch (version) {
 			case 0: {
 				struct ipc_kludge tmp;
-				int err;
 				if (!ptr)
 					return -EINVAL;
-				if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
-					return err;
-				copy_from_user(&tmp,(struct ipc_kludge *) ptr, sizeof (tmp));
+				if (copy_from_user(&tmp,(struct ipc_kludge *) ptr, 
+								   sizeof (tmp)))
+					return -EFAULT; 	
 				return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
 				}
 			case 1: default:
@@ -155,14 +147,11 @@
 			case 0: default: {
 				ulong raddr;
 				int err;
-				if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
-					return err;
 				err = sys_shmat (first, (char *) ptr, second, &raddr);
 				if (err)
 					return err;
-				put_user (raddr, (ulong *) third);
-				return 0;
-				}
+				return put_user (raddr, (ulong *) third);
+			}
 			case 1:	/* iBCS2 emulator entry point */
 				if (get_fs() != get_ds())
 					return -EINVAL;
diff -u --recursive --new-file v2.1.19/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.19/linux/arch/sparc/mm/srmmu.c	Tue Dec 31 21:41:00 1996
+++ linux/arch/sparc/mm/srmmu.c	Wed Jan  1 16:24:40 1997
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.122 1996/12/30 06:16:31 davem Exp $
+/* $Id: srmmu.c,v 1.123 1996/12/31 09:53:00 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -53,6 +53,14 @@
 #define smp_release()
 #endif /* !(__SMP__) */
 
+#ifdef __SMP__
+#define FLUSH_BEGIN(mm)
+#define FLUSH_END
+#else
+#define FLUSH_BEGIN(mm) if((mm)->context != NO_CONTEXT) {
+#define FLUSH_END	}
+#endif
+
 /* #define USE_CHUNK_ALLOC 1 */
 
 static unsigned long (*mmu_getpage)(void);
@@ -149,16 +157,12 @@
  */
 static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
 {
-	__asm__ __volatile__("swap [%2], %0\n\t" :
-			     "=&r" (value) :
-			     "0" (value), "r" (addr));
+	__asm__ __volatile__("swap [%2], %0" : "=&r" (value) : "0" (value), "r" (addr));
 	return value;
 }
 
 /* Functions really use this, not srmmu_swap directly. */
-#define srmmu_set_entry(ptr, newentry) \
-        srmmu_swap((unsigned long *) (ptr), (newentry))
-
+#define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry))
 
 /* The very generic SRMMU page table operations. */
 static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
@@ -279,17 +283,15 @@
 static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
 	if(tsk->mm->context != NO_CONTEXT) {
-		flush_cache_mm(current->mm);
+		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
-		flush_tlb_mm(current->mm);
+		flush_tlb_mm(tsk->mm);
 	}
 }
 
 static unsigned long srmmu_getpage(void)
 {
-	unsigned long page = get_free_page(GFP_KERNEL);
-
-	return page;
+	return get_free_page(GFP_KERNEL);
 }
 
 static inline void srmmu_putpage(unsigned long page)
@@ -688,15 +690,23 @@
 
 static void srmmu_set_pte_nocache_hyper(pte_t *ptep, pte_t pteval)
 {
-	volatile unsigned long clear;
-	unsigned long flags;
+	unsigned long page = ((unsigned long)ptep) & PAGE_MASK;
 
-	save_and_cli(flags);
 	srmmu_set_entry(ptep, pte_val(pteval));
-	if(srmmu_hwprobe(((unsigned long)ptep)&PAGE_MASK))
-		hyper_flush_cache_page(((unsigned long)ptep) & PAGE_MASK);
-	clear = srmmu_get_fstatus();
-	restore_flags(flags);
+	__asm__ __volatile__("
+	lda	[%0] %2, %%g4
+	orcc	%%g4, 0x0, %%g0
+	be	2f
+	 sethi	%%hi(%7), %%g5
+1:	subcc	%%g5, %6, %%g5		! hyper_flush_cache_page
+	bne	1b
+	 sta	%%g0, [%1 + %%g5] %3
+	lda	[%4] %5, %%g0
+2:"	: /* no outputs */
+	: "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE),
+	  "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS),
+	  "r" (vac_line_size), "i" (PAGE_SIZE)
+	: "g4", "g5");
 }
 
 static void srmmu_set_pte_nocache_cypress(pte_t *ptep, pte_t pteval)
@@ -867,6 +877,9 @@
 {
 }
 
+/* This is used in many routines below. */
+#define UWINMASK_OFFSET (const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask))
+
 /* On the SRMMU we do not have the problems with limited tlb entries
  * for mapping kernel pages, so we just take things from the free page
  * pool.  As a side effect we are putting a little too much pressure
@@ -916,42 +929,29 @@
 
 static void tsunami_flush_cache_mm(struct mm_struct *mm)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		tsunami_flush_icache();
-		tsunami_flush_dcache();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	tsunami_flush_icache();
+	tsunami_flush_dcache();
+	FLUSH_END
 }
 
 static void tsunami_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		tsunami_flush_icache();
-		tsunami_flush_dcache();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	tsunami_flush_icache();
+	tsunami_flush_dcache();
+	FLUSH_END
 }
 
 static void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
-#ifndef __SMP__
-	struct mm_struct *mm = vma->vm_mm;
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		tsunami_flush_icache();
-		tsunami_flush_dcache();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(vma->vm_mm)
+	flush_user_windows();
+	tsunami_flush_icache();
+	tsunami_flush_dcache();
+	FLUSH_END
 }
 
 static void tsunami_flush_cache_page_to_uncache(unsigned long page)
@@ -982,55 +982,42 @@
 
 static void tsunami_flush_tlb_all(void)
 {
-	module_stats.invall++;
 	srmmu_flush_whole_tlb();
+	module_stats.invall++;
 }
 
 static void tsunami_flush_tlb_mm(struct mm_struct *mm)
 {
+	FLUSH_BEGIN(mm)
+	srmmu_flush_whole_tlb();
 	module_stats.invmm++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		srmmu_flush_whole_tlb();
-#ifndef __SMP__
-        }
-#endif
+	FLUSH_END
 }
 
 static void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
+	FLUSH_BEGIN(mm)
+	srmmu_flush_whole_tlb();
 	module_stats.invrnge++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		srmmu_flush_whole_tlb();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
-	int octx;
 	struct mm_struct *mm = vma->vm_mm;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		unsigned long flags;
-
-		save_and_cli(flags);
-		octx = srmmu_get_context();
-
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_page(page);
-		srmmu_set_context(octx);
-		restore_flags(flags);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__("
+	lda	[%0] %3, %%g5
+	sta	%1, [%0] %3
+	sta	%%g0, [%2] %4
+	sta	%%g5, [%0] %3"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invpg++;
+	FLUSH_END
 }
 
 static void tsunami_flush_tlb_page_for_cbit(unsigned long page)
@@ -1051,41 +1038,28 @@
 
 static void swift_flush_cache_mm(struct mm_struct *mm)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		swift_idflash_clear();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	swift_idflash_clear();
+	FLUSH_END
 }
 
 static void swift_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		swift_idflash_clear();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	swift_idflash_clear();
+	FLUSH_END
 }
 
 static void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
-#ifndef __SMP__
-	struct mm_struct *mm = vma->vm_mm;
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		if(vma->vm_flags & VM_EXEC)
-			swift_flush_icache();
-		swift_flush_dcache();
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(vma->vm_mm)
+	flush_user_windows();
+	if(vma->vm_flags & VM_EXEC)
+		swift_flush_icache();
+	swift_flush_dcache();
+	FLUSH_END
 }
 
 /* Not copy-back on swift. */
@@ -1115,36 +1089,32 @@
 
 static void swift_flush_tlb_all(void)
 {
-	module_stats.invall++;
 	srmmu_flush_whole_tlb();
+	module_stats.invall++;
 }
 
 static void swift_flush_tlb_mm(struct mm_struct *mm)
 {
+	FLUSH_BEGIN(mm)
+	srmmu_flush_whole_tlb();
 	module_stats.invmm++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT)
-#endif
-		srmmu_flush_whole_tlb();
+	FLUSH_END
 }
 
 static void swift_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
+	FLUSH_BEGIN(mm)
+	srmmu_flush_whole_tlb();
 	module_stats.invrnge++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT)
-#endif
-		srmmu_flush_whole_tlb();
+	FLUSH_END
 }
 
 static void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
-#ifndef __SMP__
-	struct mm_struct *mm = vma->vm_mm;
-	if(mm->context != NO_CONTEXT)
-#endif
-		srmmu_flush_whole_tlb();
+	FLUSH_BEGIN(vma->vm_mm)
+	srmmu_flush_whole_tlb();
 	module_stats.invpg++;
+	FLUSH_END
 }
 
 static void swift_flush_tlb_page_for_cbit(unsigned long page)
@@ -1296,82 +1266,114 @@
 
 static void viking_flush_tlb_all(void)
 {
-	module_stats.invall++;
-	flush_user_windows();
+	register int ctr asm("g5");
+
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %2], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%0, 1, %0
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%0, 1, %0
+		bne	2b
+		 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4");
 	srmmu_flush_whole_tlb();
+	module_stats.invall++;
 }
 
 static void viking_flush_tlb_mm(struct mm_struct *mm)
 {
-	int octx;
-	module_stats.invmm++;
+	register int ctr asm("g5");
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_ctx();
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	ctr = 0;
+	__asm__ __volatile__("
+1:	ld	[%%g6 + %7], %%g4	! flush user windows
+	orcc	%%g0, %%g4, %%g0
+	add	%0, 1, %0
+	bne	1b
+	 save	%%sp, -64, %%sp
+2:	subcc	%0, 1, %0
+	bne	2b
+	 restore %%g0, %%g0, %%g0
+	lda	[%1] %4, %0
+	sta	%3, [%1] %4
+	sta	%%g0, [%2] %5
+	sta	%0, [%1] %4"
+	: "=&r" (ctr)
+	: "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE), "0" (ctr),
+	  "i" (UWINMASK_OFFSET)
+	: "g4");
+	module_stats.invmm++;
+	FLUSH_END
 }
 
 static void viking_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	int octx;
-	module_stats.invrnge++;
+	register int ctr asm("g5");
+	unsigned long size;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		if((end - start) < SRMMU_PMD_SIZE) {
-			start &= PAGE_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_page(start);
-				start += PAGE_SIZE;
-			}
-		} else if((end - start) < SRMMU_PGDIR_SIZE) {
-			start &= SRMMU_PMD_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_segment(start);
-				start += SRMMU_PMD_SIZE;
-			}
-		} else {
-			start &= SRMMU_PGDIR_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_region(start);
-				start += SRMMU_PGDIR_SIZE;
-			}
-		}
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %2], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%0, 1, %0
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%0, 1, %0
+		bne	2b
+		 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4");
+	start &= SRMMU_PGDIR_MASK;
+	size = SRMMU_PGDIR_ALIGN(end) - start;
+	__asm__ __volatile__("
+		lda	[%0] %5, %%g5
+		sta	%1, [%0] %5
+	1:	subcc	%3, %4, %3
+		bne	1b
+		 sta	%%g0, [%2 + %3] %6
+		sta	%%g5, [%0] %5"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200),
+	  "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS),
+	  "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
+	module_stats.invrnge++;
+	FLUSH_END
 }
 
 static void viking_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
-	int octx;
 	struct mm_struct *mm = vma->vm_mm;
+	register int ctr asm("g5");
 
+	FLUSH_BEGIN(mm)
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %2], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%0, 1, %0
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%0, 1, %0
+		bne	2b
+		 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4");
+	__asm__ __volatile__("
+	lda	[%0] %3, %%g5
+	sta	%1, [%0] %3
+	sta	%%g0, [%2] %4
+	sta	%%g5, [%0] %3"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invpg++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_page(page);
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void viking_flush_tlb_page_for_cbit(unsigned long page)
@@ -1400,21 +1402,57 @@
 
 static void cypress_flush_cache_mm(struct mm_struct *mm)
 {
+	register unsigned long a, b, c, d, e, f, g;
 	unsigned long flags, faddr;
 	int octx;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		register unsigned long a, b, c, d, e, f, g;
-		flush_user_windows();
-		save_and_cli(flags);
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		a = 0x20; b = 0x40; c = 0x60;
-		d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	save_and_cli(flags);
+	octx = srmmu_get_context();
+	srmmu_set_context(mm->context);
+	a = 0x20; b = 0x40; c = 0x60;
+	d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
 
-		faddr = (0x10000 - 0x100);
+	faddr = (0x10000 - 0x100);
+	goto inside;
+	do {
+		faddr -= 0x100;
+	inside:
+		__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+				     "sta %%g0, [%0 + %2] %1\n\t"
+				     "sta %%g0, [%0 + %3] %1\n\t"
+				     "sta %%g0, [%0 + %4] %1\n\t"
+				     "sta %%g0, [%0 + %5] %1\n\t"
+				     "sta %%g0, [%0 + %6] %1\n\t"
+				     "sta %%g0, [%0 + %7] %1\n\t"
+				     "sta %%g0, [%0 + %8] %1\n\t" : :
+				     "r" (faddr), "i" (ASI_M_FLUSH_CTX),
+				     "r" (a), "r" (b), "r" (c), "r" (d),
+				     "r" (e), "r" (f), "r" (g));
+	} while(faddr);
+	srmmu_set_context(octx);
+	restore_flags(flags);
+	FLUSH_END
+}
+
+static void cypress_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	register unsigned long a, b, c, d, e, f, g;
+	unsigned long flags, faddr;
+	int octx;
+
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	save_and_cli(flags);
+	octx = srmmu_get_context();
+	srmmu_set_context(mm->context);
+	a = 0x20; b = 0x40; c = 0x60;
+	d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+
+	start &= SRMMU_PMD_MASK;
+	while(start < end) {
+		faddr = (start + (0x10000 - 0x100));
 		goto inside;
 		do {
 			faddr -= 0x100;
@@ -1427,103 +1465,55 @@
 					     "sta %%g0, [%0 + %6] %1\n\t"
 					     "sta %%g0, [%0 + %7] %1\n\t"
 					     "sta %%g0, [%0 + %8] %1\n\t" : :
-					     "r" (faddr), "i" (ASI_M_FLUSH_CTX),
+					     "r" (faddr),
+					     "i" (ASI_M_FLUSH_SEG),
 					     "r" (a), "r" (b), "r" (c), "r" (d),
 					     "r" (e), "r" (f), "r" (g));
-		} while(faddr);
-		srmmu_set_context(octx);
-		restore_flags(flags);
-#ifndef __SMP__
-	}
-#endif
-}
-
-static void cypress_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
-{
-	unsigned long flags, faddr;
-	int octx;
-
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		register unsigned long a, b, c, d, e, f, g;
-		flush_user_windows();
-		save_and_cli(flags);
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		a = 0x20; b = 0x40; c = 0x60;
-		d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
-
-		start &= SRMMU_PMD_MASK;
-		while(start < end) {
-			faddr = (start + (0x10000 - 0x100));
-			goto inside;
-			do {
-				faddr -= 0x100;
-			inside:
-				__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
-						     "sta %%g0, [%0 + %2] %1\n\t"
-						     "sta %%g0, [%0 + %3] %1\n\t"
-						     "sta %%g0, [%0 + %4] %1\n\t"
-						     "sta %%g0, [%0 + %5] %1\n\t"
-						     "sta %%g0, [%0 + %6] %1\n\t"
-						     "sta %%g0, [%0 + %7] %1\n\t"
-						     "sta %%g0, [%0 + %8] %1\n\t" : :
-						     "r" (faddr),
-						     "i" (ASI_M_FLUSH_SEG),
-						     "r" (a), "r" (b), "r" (c), "r" (d),
-						     "r" (e), "r" (f), "r" (g));
-			} while (faddr != start);
-			start += SRMMU_PMD_SIZE;
-		}
-		srmmu_set_context(octx);
-		restore_flags(flags);
-#ifndef __SMP__
+		} while (faddr != start);
+		start += SRMMU_PMD_SIZE;
 	}
-#endif
+	srmmu_set_context(octx);
+	restore_flags(flags);
+	FLUSH_END
 }
 
 static void cypress_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
+	register unsigned long a, b, c, d, e, f, g;
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long flags, line;
 	int octx;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		register unsigned long a, b, c, d, e, f, g;
-		flush_user_windows();
-		save_and_cli(flags);
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		a = 0x20; b = 0x40; c = 0x60;
-		d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
+	FLUSH_BEGIN(mm)
+	flush_user_windows();
+	save_and_cli(flags);
+	octx = srmmu_get_context();
+	srmmu_set_context(mm->context);
+	a = 0x20; b = 0x40; c = 0x60;
+	d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0;
 
-		page &= PAGE_MASK;
-		line = (page + PAGE_SIZE) - 0x100;
-		goto inside;
-		do {
-			line -= 0x100;
-		inside:
-				__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
-						     "sta %%g0, [%0 + %2] %1\n\t"
-						     "sta %%g0, [%0 + %3] %1\n\t"
-						     "sta %%g0, [%0 + %4] %1\n\t"
-						     "sta %%g0, [%0 + %5] %1\n\t"
-						     "sta %%g0, [%0 + %6] %1\n\t"
-						     "sta %%g0, [%0 + %7] %1\n\t"
-						     "sta %%g0, [%0 + %8] %1\n\t" : :
-						     "r" (line),
-						     "i" (ASI_M_FLUSH_PAGE),
-						     "r" (a), "r" (b), "r" (c), "r" (d),
-						     "r" (e), "r" (f), "r" (g));
-		} while(line != page);
-		srmmu_set_context(octx);
-		restore_flags(flags);
-#ifndef __SMP__
-	}
-#endif
+	page &= PAGE_MASK;
+	line = (page + PAGE_SIZE) - 0x100;
+	goto inside;
+	do {
+		line -= 0x100;
+	inside:
+			__asm__ __volatile__("sta %%g0, [%0] %1\n\t"
+					     "sta %%g0, [%0 + %2] %1\n\t"
+					     "sta %%g0, [%0 + %3] %1\n\t"
+					     "sta %%g0, [%0 + %4] %1\n\t"
+					     "sta %%g0, [%0 + %5] %1\n\t"
+					     "sta %%g0, [%0 + %6] %1\n\t"
+					     "sta %%g0, [%0 + %7] %1\n\t"
+					     "sta %%g0, [%0 + %8] %1\n\t" : :
+					     "r" (line),
+					     "i" (ASI_M_FLUSH_PAGE),
+					     "r" (a), "r" (b), "r" (c), "r" (d),
+					     "r" (e), "r" (f), "r" (g));
+	} while(line != page);
+	srmmu_set_context(octx);
+	restore_flags(flags);
+	FLUSH_END
 }
 
 /* Cypress is copy-back, at least that is how we configure it. */
@@ -1625,80 +1615,65 @@
 
 static void cypress_flush_tlb_all(void)
 {
-	module_stats.invall++;
 	srmmu_flush_whole_tlb();
+	module_stats.invall++;
 }
 
 static void cypress_flush_tlb_mm(struct mm_struct *mm)
 {
-	int octx;
-
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__("
+	lda	[%0] %3, %%g5
+	sta	%2, [%0] %3
+	sta	%%g0, [%1] %4
+	sta	%%g5, [%0] %3"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invmm++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_ctx();
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void cypress_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	int octx;
-	module_stats.invrnge++;
+	unsigned long size;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		if((end - start) < SRMMU_PMD_SIZE) {
-			start &= PAGE_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_page(start);
-				start += PAGE_SIZE;
-			}
-		} else if((end - start) < SRMMU_PGDIR_SIZE) {
-			start &= SRMMU_PMD_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_segment(start);
-				start += SRMMU_PMD_SIZE;
-			}
-		} else {
-			start &= SRMMU_PGDIR_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_region(start);
-				start += SRMMU_PGDIR_SIZE;
-			}
-		}
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	start &= SRMMU_PGDIR_MASK;
+	size = SRMMU_PGDIR_ALIGN(end) - start;
+	__asm__ __volatile__("
+		lda	[%0] %5, %%g5
+		sta	%1, [%0] %5
+	1:	subcc	%3, %4, %3
+		bne	1b
+		 sta	%%g0, [%2 + %3] %6
+		sta	%%g5, [%0] %5"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200),
+	  "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS),
+	  "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
+	module_stats.invrnge++;
+	FLUSH_END
 }
 
 static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
-	int octx;
 	struct mm_struct *mm = vma->vm_mm;
 
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__("
+	lda	[%0] %3, %%g5
+	sta	%1, [%0] %3
+	sta	%%g0, [%2] %4
+	sta	%%g5, [%0] %3"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invpg++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_page(page);
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void cypress_flush_tlb_page_for_cbit(unsigned long page)
@@ -1709,48 +1684,111 @@
 /* Hypersparc flushes.  Very nice chip... */
 static void hypersparc_flush_cache_all(void)
 {
-	flush_user_windows();
-	hyper_flush_unconditional_combined();
-	hyper_flush_whole_icache();
+	register int ctr asm("g5");
+	unsigned long tmp1;
+
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %6], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%1, 1, %1
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%1, 1, %1
+		bne	2b
+		 restore %%g0, %%g0, %%g0
+	1:	subcc	%0, %3, %0		! hyper_flush_unconditional_combined
+		bne	1b
+		 sta	%%g0, [%0] %4
+		sta	%%g0, [%%g0] %5		! hyper_flush_whole_icache"
+	: "=&r" (tmp1), "=&r" (ctr)
+	: "0" (vac_cache_size), "r" (vac_line_size),
+	  "i" (ASI_M_FLUSH_CTX), "i" (ASI_M_FLUSH_IWHOLE),
+	  "i" (UWINMASK_OFFSET), "1" (ctr)
+	: "g4");
 }
 
 static void hypersparc_flush_cache_mm(struct mm_struct *mm)
 {
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		hyper_flush_cache_user();
-		hyper_flush_whole_icache();
-#ifndef __SMP__
-	}
-#endif
+	register int ctr asm("g5");
+	unsigned long tmp1;
+
+	FLUSH_BEGIN(mm)
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %2], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%0, 1, %0
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%0, 1, %0
+		bne	2b
+		 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr)
+	: "0" (ctr), "i" (UWINMASK_OFFSET)
+	: "g4");
+
+	__asm__ __volatile__("
+	1:	subcc	%0, %2, %0		! hyper_flush_cache_user
+		bne	1b
+		 sta	%%g0, [%0] %3
+		sta	%%g0, [%%g0] %4		! hyper_flush_whole_icache"
+	: "=&r" (tmp1)
+	: "0" (vac_cache_size), "r" (vac_line_size), "i" (ASI_M_FLUSH_USER),
+	  "i" (ASI_M_FLUSH_IWHOLE));
+	FLUSH_END
 }
 
-/* Boy was my older implementation inefficient... */
+/* The things we do for performance... */
 static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	volatile unsigned long clear;
-	int octx;
+	register int ctr asm("g5");
+	unsigned long tmp1;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		flush_user_windows();
-		octx = srmmu_get_context();
-		start &= PAGE_MASK;
-		srmmu_set_context(mm->context);
-		while(start < end) {
-			if(srmmu_hwprobe(start))
-				hyper_flush_cache_page(start);
-			start += PAGE_SIZE;
-		}
-		clear = srmmu_get_fstatus();
-		srmmu_set_context(octx);
-		hyper_flush_whole_icache();
-#ifndef __SMP__
+	FLUSH_BEGIN(mm)
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %2], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%0, 1, %0
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%0, 1, %0
+		bne	2b
+		 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4");
+	tmp1 = vac_cache_size; start &= PAGE_MASK; end = PAGE_ALIGN(end);
+	if((end - start) >= (tmp1 << 2)) {
+		__asm__ __volatile__("
+		1:	subcc	%0, %2, %0	! hyper_flush_cache_user
+			bne	1b
+			 sta	%%g0, [%0] %3
+			sta	%%g0, [%%g0] %4"
+		: "=&r" (tmp1) : "0" (tmp1), "r" (vac_line_size),
+		  "i" (ASI_M_FLUSH_USER), "i" (ASI_M_FLUSH_IWHOLE));
+	} else {
+		tmp1 = srmmu_get_context(); srmmu_set_context(mm->context);
+		__asm__ __volatile__("
+			sub	%0, %3, %0
+		1:	or	%0, 0x400, %%g4
+			lda	[%%g4] %4, %%g4
+			orcc	%%g4, 0x0, %%g0
+			be	3f
+			 sethi	%%hi(0x1000), %%g5
+		2:	subcc	%%g5, %7, %%g5	! hyper_flush_cache_page
+			bne	2b
+			 sta	%%g0, [%0 + %%g5] %5
+		3:	cmp	%0, %2
+			bne	1b
+			 sub	%0, %3, %0
+			sta	%%g0, [%%g0] %6	! hyper_flush_whole_icache"
+		: "=&r" (end)
+		: "0" (end), "r" (start), "r" (PAGE_SIZE), "i" (ASI_M_FLUSH_PROBE),
+		  "i" (ASI_M_FLUSH_PAGE), "i" (ASI_M_FLUSH_IWHOLE), "r" (vac_line_size)
+		: "g4", "g5");
+		(void) srmmu_get_fstatus(); srmmu_set_context(tmp1);
 	}
-#endif
+	FLUSH_END
 }
 
 /* HyperSparc requires a valid mapping where we are about to flush
@@ -1759,35 +1797,64 @@
 static void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
 	struct mm_struct *mm = vma->vm_mm;
-	volatile unsigned long clear;
-	int octx;
+	register int ctr asm("g5");
+	unsigned long tmp1;
 
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-		octx = srmmu_get_context();
-		flush_user_windows();
-		srmmu_set_context(mm->context);
-		hyper_flush_whole_icache();
-		if(!srmmu_hwprobe(page))
-			goto no_mapping;
-		hyper_flush_cache_page(page);
-	no_mapping:
-		clear = srmmu_get_fstatus();
-		srmmu_set_context(octx);
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_BEGIN(mm)
+	ctr = 0;
+	__asm__ __volatile__("
+	1:	ld	[%%g6 + %2], %%g4	! flush user windows
+		orcc	%%g0, %%g4, %%g0
+		add	%0, 1, %0
+		bne	1b
+		 save	%%sp, -64, %%sp
+	2:	subcc	%0, 1, %0
+		bne	2b
+		 restore %%g0, %%g0, %%g0"
+	: "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4");
+	__asm__ __volatile__("
+		mov	0x200, %%g4
+		lda	[%%g4] %6, %1
+		sta	%0, [%%g4] %6
+		or	%3, 0x400, %0
+		lda	[%0] %9, %0
+		orcc	%0, 0x0, %%g0
+		be	2f
+		 sethi	%%hi(0x1000), %0
+	1:	subcc	%0, %5, %0		! hyper_flush_cache_page
+		bne	1b
+		 sta	%%g0, [%3 + %0] %7
+	2:	andcc	%4, 0x4, %%g0
+		sta	%1, [%%g4] %6
+		bne,a	1f
+		 sta	%%g0, [%%g0] %8
+1:"	: "=&r" (tmp1), "=&r" (ctr)
+	: "0" (mm->context), "r" (page & PAGE_MASK), "r" (vma->vm_flags),
+	  "r" (vac_line_size), "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PAGE),
+	  "i" (ASI_M_FLUSH_IWHOLE), "i" (ASI_M_FLUSH_PROBE)
+	: "g4");
+	(void) srmmu_get_fstatus();
+	FLUSH_END
 }
 
 /* HyperSparc is copy-back. */
 static void hypersparc_flush_page_to_ram(unsigned long page)
 {
-	volatile unsigned long clear;
-
-	if(srmmu_hwprobe(page))
-		hyper_flush_cache_page(page);
-	clear = srmmu_get_fstatus();
+	page &= PAGE_MASK;
+	__asm__ __volatile__("
+	lda	[%0] %2, %%g4
+	orcc	%%g4, 0x0, %%g0
+	be	2f
+	 sethi	%%hi(%7), %%g5
+1:	subcc	%%g5, %6, %%g5		! hyper_flush_cache_page
+	bne	1b
+	 sta	%%g0, [%1 + %%g5] %3
+2:	lda	[%4] %5, %%g0"
+	: /* no outputs */
+	: "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE),
+	  "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS),
+	  "r" (vac_line_size), "i" (PAGE_SIZE)
+	: "g4", "g5");
 }
 
 /* HyperSparc is IO cache coherent. */
@@ -1805,108 +1872,106 @@
 
 static void hypersparc_flush_cache_page_to_uncache(unsigned long page)
 {
-	volatile unsigned long clear;
-
-	if(srmmu_hwprobe(page))
-		hyper_flush_cache_page(page);
-	clear = srmmu_get_fstatus();
+	page &= PAGE_MASK;
+	__asm__ __volatile__("
+	lda	[%0] %2, %%g4
+	orcc	%%g4, 0x0, %%g0
+	be	2f
+	 sethi	%%hi(%7), %%g5
+1:	subcc	%%g5, %6, %%g5		! hyper_flush_cache_page
+	bne	1b
+	 sta	%%g0, [%1 + %%g5] %3
+2:	lda	[%4] %5, %%g0"
+	: /* no outputs */
+	: "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE),
+	  "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS),
+	  "r" (vac_line_size), "i" (PAGE_SIZE)
+	: "g4", "g5");
 }
 
 static unsigned long hypersparc_getpage(void)
 {
-	volatile unsigned long clear;
 	unsigned long page = get_free_page(GFP_KERNEL);
-	unsigned long flags;
 
-	save_and_cli(flags);
-	if(srmmu_hwprobe(page))
-		hyper_flush_cache_page(page);
-	clear = srmmu_get_fstatus();
-	restore_flags(flags);
+	__asm__ __volatile__("
+	lda	[%0] %2, %%g4
+	orcc	%%g4, 0x0, %%g0
+	be	2f
+	 sethi	%%hi(%7), %%g5
+1:	subcc	%%g5, %6, %%g5		! hyper_flush_cache_page
+	bne	1b
+	 sta	%%g0, [%1 + %%g5] %3
+2:	lda	[%4] %5, %%g0"
+	: /* no outputs */
+	: "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE),
+	  "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS),
+	  "r" (vac_line_size), "i" (PAGE_SIZE)
+	: "g4", "g5");
 
 	return page;
 }
 
 static void hypersparc_flush_tlb_all(void)
 {
-	module_stats.invall++;
 	srmmu_flush_whole_tlb();
+	module_stats.invall++;
 }
 
 static void hypersparc_flush_tlb_mm(struct mm_struct *mm)
 {
-	int octx;
-
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__("
+	lda	[%0] %3, %%g5
+	sta	%2, [%0] %3
+	sta	%%g0, [%1] %4
+	sta	%%g5, [%0] %3"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invmm++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_ctx();
-		srmmu_set_context(octx);
-
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	int octx;
+	unsigned long size;
 
+	FLUSH_BEGIN(mm)
+	start &= SRMMU_PGDIR_MASK;
+	size = SRMMU_PGDIR_ALIGN(end) - start;
+	__asm__ __volatile__("
+		lda	[%0] %5, %%g5
+		sta	%1, [%0] %5
+	1:	subcc	%3, %4, %3
+		bne	1b
+		 sta	%%g0, [%2 + %3] %6
+		sta	%%g5, [%0] %5"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200),
+	  "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS),
+	  "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invrnge++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		if((end - start) < SRMMU_PMD_SIZE) {
-			start &= PAGE_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_page(start);
-				start += PAGE_SIZE;
-			}
-		} else if((end - start) < SRMMU_PGDIR_SIZE) {
-			start &= SRMMU_PMD_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_segment(start);
-				start += SRMMU_PMD_SIZE;
-			}
-		} else {
-			start &= SRMMU_PGDIR_MASK;
-			while(start < end) {
-				srmmu_flush_tlb_region(start);
-				start += SRMMU_PGDIR_SIZE;
-			}
-		}
-		srmmu_set_context(octx);
-
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
 	struct mm_struct *mm = vma->vm_mm;
-	int octx;
 
+	FLUSH_BEGIN(mm)
+	__asm__ __volatile__("
+	lda	[%0] %3, %%g5
+	sta	%1, [%0] %3
+	sta	%%g0, [%2] %4
+	sta	%%g5, [%0] %3"
+	: /* no outputs */
+	: "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK),
+	  "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE)
+	: "g5");
 	module_stats.invpg++;
-#ifndef __SMP__
-	if(mm->context != NO_CONTEXT) {
-#endif
-
-		octx = srmmu_get_context();
-		srmmu_set_context(mm->context);
-		srmmu_flush_tlb_page(page);
-		srmmu_set_context(octx);
-
-#ifndef __SMP__
-	}
-#endif
+	FLUSH_END
 }
 
 static void hypersparc_flush_tlb_page_for_cbit(unsigned long page)
@@ -1922,21 +1987,27 @@
 
 static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
-	volatile unsigned long clear;
 	unsigned long page = ((unsigned long) pgdp) & PAGE_MASK;
-	unsigned long flags;
 
-	/* Do PGD flush. */
-	save_and_cli(flags);
-	if(srmmu_hwprobe(page))
-		hyper_flush_cache_page(page);
-	clear = srmmu_get_fstatus();
-	restore_flags(flags);
+	__asm__ __volatile__("
+	lda	[%0] %2, %%g4
+	orcc	%%g4, 0x0, %%g0
+	be	2f
+	 sethi	%%hi(%7), %%g5
+1:	subcc	%%g5, %6, %%g5		! hyper_flush_cache_page
+	bne	1b
+	 sta	%%g0, [%1 + %%g5] %3
+2:	lda	[%4] %5, %%g0"
+	: /* no outputs */
+	: "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE),
+	  "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS),
+	  "r" (vac_line_size), "i" (PAGE_SIZE)
+	: "g4", "g5");
 
 	if(tsk->mm->context != NO_CONTEXT) {
-		flush_cache_mm(current->mm);
+		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
-		flush_tlb_mm(current->mm);
+		flush_tlb_mm(tsk->mm);
 	}
 }
 
@@ -2851,17 +2922,14 @@
 static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma,
 				       unsigned long address, pte_t pte)
 {
-	unsigned long offset, vaddr;
-	unsigned long start;
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-
 	if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) {
 		struct vm_area_struct *vmaring;
 		struct inode *inode;
-		unsigned long flags;
+		unsigned long flags, offset, vaddr, start;
 		int alias_found = 0;
+		pgd_t *pgdp;
+		pmd_t *pmdp;
+		pte_t *ptep;
 
 		save_and_cli(flags);
 
@@ -3522,6 +3590,7 @@
 	mmu_getpage = srmmu_getpage;
 	set_pte = srmmu_set_pte_cacheable;
 	init_new_context = srmmu_init_new_context;
+	switch_to_context = srmmu_switch_to_context;
 	pmd_align = srmmu_pmd_align;
 	pgdir_align = srmmu_pgdir_align;
 	vmalloc_start = srmmu_vmalloc_start;
diff -u --recursive --new-file v2.1.19/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.1.19/linux/drivers/block/loop.c	Thu Dec 12 17:02:40 1996
+++ linux/drivers/block/loop.c	Tue Dec 31 16:27:23 1996
@@ -25,11 +25,11 @@
 #include <asm/uaccess.h>
 
 #ifdef CONFIG_BLK_DEV_LOOP_DES
-#include <linux/des.h>
+# /*nodep*/ include <linux/des.h>
 #endif
 
 #ifdef CONFIG_BLK_DEV_LOOP_IDEA
-#include <linux/idea.h>
+# /*nodep*/ include <linux/idea.h>
 #endif
 
 #include <linux/loop.h>		/* must follow des.h */
diff -u --recursive --new-file v2.1.19/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.19/linux/drivers/char/Config.in	Wed Dec 18 15:58:47 1996
+++ linux/drivers/char/Config.in	Tue Dec 31 16:27:23 1996
@@ -37,19 +37,14 @@
 fi
 
 tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE
-if [ "$CONFIG_QIC02_TAPE" = "y" -o "$CONFIG_QIC02_TAPE" = "m" ]; then
-  if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
-    bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF
-    if [ "$CONFIG_QIC02_DYNCONF" = "y" ]; then
-      comment 'Setting runtime QIC-02 configuration is done with qic02conf'
-      comment 'from the tpqic02-support package.  It is available at'
-      comment 'ftp://titus.cfw.com/pub/Linux/util/'
-    else
-      comment 'Edit configuration parameters in ./include/linux/tpqic02.h!'
-    fi
-  fi
-  if [ "$CONFIG_QIC02_TAPE" = "m" ]; then
-    comment 'Edit configuration parameters in ./include/linux/tpqic02.h!'
+if [ "$CONFIG_QIC02_TAPE" != "n" ]; then
+  bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF
+  if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+    comment '   Edit configuration parameters in ./include/linux/tpqic02.h!'
+  else
+    comment '   Setting runtime QIC-02 configuration is done with qic02conf'
+    comment '   from the tpqic02-support package.  It is available at'
+    comment '   sunsite.unc.edu or ftp://titus.cfw.com/pub/Linux/util/'
   fi
 fi
 
diff -u --recursive --new-file v2.1.19/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.19/linux/drivers/char/Makefile	Sun Dec 22 16:37:31 1996
+++ linux/drivers/char/Makefile	Tue Dec 31 16:27:23 1996
@@ -206,11 +206,11 @@
 L_OBJS += rtc.o
 endif
 
-ifdef CONFIG_QIC02_TAPE
+ifeq ($(CONFIG_QIC02_TAPE),y)
+L_OBJS += tpqic02.o
+else
   ifeq ($(CONFIG_QIC02_TAPE),m)
-    M_OBJS += tpqic02.o
-  else
-    L_OBJS += tpqic02.o 
+  M_OBJS += tpqic02.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.19/linux/drivers/char/esp.c linux/drivers/char/esp.c
--- v2.1.19/linux/drivers/char/esp.c	Mon Dec 30 15:39:06 1996
+++ linux/drivers/char/esp.c	Wed Jan  1 22:26:31 1997
@@ -29,13 +29,17 @@
  *     by Chris Faylor.
  *
  * Most recent changes: (Andrew J. Robinson)
- *     Remove tx_flowed_[on|off], since the ESP handles flow itself.
- *     Enabled reinterrupt pacing to ensure all requests are serviced.
- *     Decreased RX timeout to reduce latency.
+ *     Remove all references to tty->hw_stopped.
+ *     Request a single region for multiple ports if possible.
+ *     Stop a DMA transfer on a port when it is closed.
+ *     Rename esp_init() to espserial_init().
+ *     Improve validation of IRQ (only accept those allowed by the ESP card).
+ *     Return if a signal is received while wait for a break to start.
+ *     Split NEED_DMA logic into NEED_DMA_RX and NEED_DMA_TX.
  *
  * This module exports the following rs232 io functions:
  *
- *	int esp_init(void);
+ *	int espserial_init(void);
  */
 
 #include <linux/module.h>
@@ -76,16 +80,22 @@
 static unsigned int dma = CONFIG_ESPSERIAL_DMA_CHANNEL; /* DMA channel */
 static unsigned int trigger = CONFIG_ESPSERIAL_TRIGGER_LEVEL;
 	/* FIFO trigger level */
+
+MODULE_PARM(irq, "1-8i");
+MODULE_PARM(divisor, "1-8i");
+MODULE_PARM(dma, "i");
+MODULE_PARM(trigger, "i");
 /* END */
 
 static char *dma_buffer;
+static int dma_bytes;
 
 #define DMA_BUFFER_SZ 1024
 
 #define WAKEUP_CHARS 1024
 
 static char *serial_name = "ESP serial driver";
-static char *serial_version = "1.2";
+static char *serial_version = "1.3";
 
 DECLARE_TASK_QUEUE(tq_esp);
 
@@ -122,7 +132,6 @@
 
 static struct esp_struct *IRQ_ports[16];
 
-static void autoconfig(struct esp_struct * info);
 static void change_speed(struct esp_struct *info);
 static void rs_wait_until_sent(struct tty_struct *, int);
 	
@@ -288,32 +297,24 @@
 	mark_bh(ESP_BH);
 }
 
-static _INLINE_ void receive_chars_dma(struct esp_struct *info, int *dma_bytes)
+static void receive_chars_dma(struct esp_struct *info)
 {
-	unsigned int num_chars;
-
-        if (*dma_bytes) {
-		info->stat_flags |= ESP_STAT_NEED_DMA;
-        	return;
-	}
-
-	info->stat_flags &= ~(ESP_STAT_RX_TIMEOUT | ESP_STAT_NEED_DMA);
+	info->stat_flags &= ~(ESP_STAT_RX_TIMEOUT | ESP_STAT_NEED_DMA_RX);
 
 	serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
 	serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
-	num_chars = serial_in(info, UART_ESI_STAT1) << 8;
-	num_chars |= serial_in(info, UART_ESI_STAT2);
+	dma_bytes = serial_in(info, UART_ESI_STAT1) << 8;
+	dma_bytes |= serial_in(info, UART_ESI_STAT2);
 
-	if (!num_chars)
+	if (!dma_bytes)
 		return;
         
-        *dma_bytes = num_chars;
 	info->stat_flags |= ESP_STAT_DMA_RX;
         disable_dma(dma);
         clear_dma_ff(dma);
         set_dma_mode(dma, DMA_MODE_READ);
         set_dma_addr(dma, virt_to_bus(dma_buffer));
-        set_dma_count(dma, num_chars);
+        set_dma_count(dma, dma_bytes);
         enable_dma(dma);
         serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);
 }
@@ -368,8 +369,8 @@
 	restore_flags(flags);
 }
 
-static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, 
-	int *dma_bytes, int status)
+static _INLINE_ void receive_chars_dma_done(struct esp_struct *info,
+					    int status)
 {
 	struct tty_struct *tty = info->tty;
 	int num_bytes, bytes_left, x_bytes;
@@ -382,10 +383,8 @@
 	clear_dma_ff(dma);
 
 	info->stat_flags &= ~ESP_STAT_DMA_RX;
-	num_bytes = *dma_bytes - get_dma_residue(dma);
-
+	bytes_left = num_bytes = dma_bytes - get_dma_residue(dma);
 	buffer = &(tty->flip);
-	bytes_left = num_bytes;
 
 	if (info->tty_buf->count && (tty->flip.count < TTY_FLIPBUF_SIZE))
 		do_ttybuf(info);
@@ -415,7 +414,6 @@
 	if (num_bytes > 0) {
 		buffer->flag_buf_ptr--;
 
-		status >>= 8;
 		status &= (0x1c & info->read_status_mask);
 
 		if (status & info->ignore_status_mask) {
@@ -439,26 +437,18 @@
 		queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
 	}
 
-	if (*dma_bytes != num_bytes) {
-		*dma_bytes = 0;
-		receive_chars_dma(info, dma_bytes);
+	if (dma_bytes != num_bytes) {
+		dma_bytes = 0;
+		receive_chars_dma(info);
 	} else
-		*dma_bytes = 0;
+		dma_bytes = 0;
 }
 
-static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int *dma_bytes)
+static void transmit_chars_dma(struct esp_struct *info)
 {
-	int count;
-	
-	if (*dma_bytes) {
-		info->stat_flags |= ESP_STAT_NEED_DMA;
-		return;
-	}
-
-	info->stat_flags &= ~ESP_STAT_NEED_DMA;
+	info->stat_flags &= ~ESP_STAT_NEED_DMA_TX;
 
-	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
-    		info->tty->hw_stopped) {
+	if ((info->xmit_cnt <= 0) || info->tty->stopped) {
 		info->IER &= ~UART_IER_THRI;
 		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
 		serial_out(info, UART_ESI_CMD2, info->IER);
@@ -467,53 +457,53 @@
 	
 	serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
 	serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
-	count = serial_in(info, UART_ESI_STAT1) << 8;
-	count |= serial_in(info, UART_ESI_STAT2);
+	dma_bytes = serial_in(info, UART_ESI_STAT1) << 8;
+	dma_bytes |= serial_in(info, UART_ESI_STAT2);
 
-	if (!count)
+	if (!dma_bytes)
 		return;
 
-	count = ((count < info->xmit_cnt) ? count : info->xmit_cnt);
+	if (dma_bytes > info->xmit_cnt)
+		dma_bytes = info->xmit_cnt;
 
-	if (info->xmit_tail + count <= ESP_XMIT_SIZE) {
+	if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) {
 		memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
-			count);
+		       dma_bytes);
 	} else {
 		int i = ESP_XMIT_SIZE - info->xmit_tail;
 		memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
 			i);
-		memcpy(&(dma_buffer[i]), info->xmit_buf, count - i);
+		memcpy(&(dma_buffer[i]), info->xmit_buf, dma_bytes - i);
 	}
 
-	info->xmit_cnt -= count;
-	info->xmit_tail = (info->xmit_tail + count) & (ESP_XMIT_SIZE - 1);
+	info->xmit_cnt -= dma_bytes;
+	info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1);
 
-	if (info->xmit_cnt < WAKEUP_CHARS)
+	if (info->xmit_cnt < WAKEUP_CHARS) {
 		rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);
 
 #ifdef SERIAL_DEBUG_INTR
 		printk("THRE...");
 #endif
 
-	if (info->xmit_cnt <= 0) {
-		info->IER &= ~UART_IER_THRI;
-		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
-		serial_out(info, UART_ESI_CMD2, info->IER);
+		if (info->xmit_cnt <= 0) {
+			info->IER &= ~UART_IER_THRI;
+			serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+			serial_out(info, UART_ESI_CMD2, info->IER);
+		}
 	}
 
-        *dma_bytes = count;
 	info->stat_flags |= ESP_STAT_DMA_TX;
         disable_dma(dma);
         clear_dma_ff(dma);
         set_dma_mode(dma, DMA_MODE_WRITE);
         set_dma_addr(dma, virt_to_bus(dma_buffer));
-        set_dma_count(dma, count);
+        set_dma_count(dma, dma_bytes);
         enable_dma(dma);
         serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
 }
 
-static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info,
-	int *dma_bytes)
+static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info)
 {
 	int num_bytes;
 
@@ -523,21 +513,20 @@
 	disable_dma(dma);
 	clear_dma_ff(dma);
 
-	num_bytes = *dma_bytes - get_dma_residue(dma);
+	num_bytes = dma_bytes - get_dma_residue(dma);
 
-	if (*dma_bytes != num_bytes)
-	{
-		*dma_bytes -= num_bytes;
-		memmove(dma_buffer, dma_buffer + num_bytes, *dma_bytes);
+	if (dma_bytes != num_bytes) {
+		dma_bytes -= num_bytes;
+		memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes);
         	disable_dma(dma);
         	clear_dma_ff(dma);
         	set_dma_mode(dma, DMA_MODE_WRITE);
         	set_dma_addr(dma, virt_to_bus(dma_buffer));
-        	set_dma_count(dma, *dma_bytes);
+        	set_dma_count(dma, dma_bytes);
         	enable_dma(dma);
         	serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
 	} else {
-		*dma_bytes = 0;
+		dma_bytes = 0;
 		info->stat_flags &= ~ESP_STAT_DMA_TX;
 	}
 }
@@ -585,12 +574,10 @@
  */
 static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
 {
-	struct esp_struct * info, *stop_port;
+	struct esp_struct * info, *stop_port = 0;
 	unsigned err_status;
 	unsigned int scratch;
 	int pre_bytes;
-	int check_dma_only = 0;
-	static int dma_bytes;
 	
 #ifdef SERIAL_DEBUG_INTR
 	printk("rs_interrupt_single(%d)...", irq);
@@ -599,14 +586,13 @@
 	/* This routine will currently check ALL ports when an interrupt */
 	/* is received from ANY port */
 
-	stop_port = info = IRQ_ports[irq];
+	info = IRQ_ports[irq];
 
 	if (!info)
 		return;
 
 	do {
-		if (!info->tty || (check_dma_only &&
-			!(info->stat_flags & ESP_STAT_NEED_DMA))) {
+		if (!info->tty) {
 			info = info->next_port;
 			continue;
 		}
@@ -616,33 +602,38 @@
 		scratch = serial_in(info, UART_ESI_SID);
 		if (scratch & 0x04) { /* error - check for rx timeout */
 			serial_out(info, UART_ESI_CMD1, ESI_GET_ERR_STAT);
-			err_status = serial_in(info, UART_ESI_STAT1) << 8;
-			err_status |= serial_in(info, UART_ESI_STAT2);
+			err_status = serial_in(info, UART_ESI_STAT1);
+			serial_in(info, UART_ESI_STAT2);
 
-			if (err_status & 0x0100)
+			if (err_status & 0x01)
 				info->stat_flags |= ESP_STAT_RX_TIMEOUT;
 
-			if (err_status & 0x2000)	/* UART status */
+			if (err_status & 0x20)	/* UART status */
 				check_modem_status(info);
 
-			if (err_status & 0x8000)	/* Start break */
+			if (err_status & 0x80)	/* Start break */
 				wake_up_interruptible(&info->break_wait);
 		}
 		
 		if ((scratch & 0x88)  || /* DMA completed or timed out */
-			(err_status & 0x1c00) /* receive error */) {
-				receive_chars_dma_done(info, &dma_bytes,
-					err_status);
-				transmit_chars_dma_done(info, &dma_bytes);
+			(err_status & 0x1c) /* receive error */) {
+				receive_chars_dma_done(info, err_status);
+				transmit_chars_dma_done(info);
 		}
 
 		if (((scratch & 0x01) ||
 			(info->stat_flags & ESP_STAT_RX_TIMEOUT)) &&
 			(info->IER & UART_IER_RDI))
-			receive_chars_dma(info, &dma_bytes);
+			if (dma_bytes)
+				info->stat_flags |= ESP_STAT_NEED_DMA_RX;
+			else
+				receive_chars_dma(info);
 
 		if ((scratch & 0x02) && (info->IER & UART_IER_THRI))
-			transmit_chars_dma(info, &dma_bytes);
+			if (dma_bytes)
+				info->stat_flags |= ESP_STAT_NEED_DMA_TX;
+			else
+				transmit_chars_dma(info);
 
 		info->last_active = jiffies;
 
@@ -650,13 +641,21 @@
 			stop_port = info;
 
 		info = info->next_port;
+	} while ((info->irq == irq) && (info != IRQ_ports[irq]));
 
-		if ((info->irq != irq) || (info == IRQ_ports[irq]))
-			check_dma_only = 1;
-
-		if (check_dma_only && dma_bytes)
-			info = stop_port;
-	} while (info != stop_port);
+	if (stop_port) {
+		while ((info != stop_port) && (!dma_bytes)) {
+			if (info->tty) {
+				if (info->stat_flags & ESP_STAT_NEED_DMA_RX)
+					receive_chars_dma(info);
+				if ((info->stat_flags & ESP_STAT_NEED_DMA_TX)
+				    && !dma_bytes)
+					transmit_chars_dma(info);
+			}
+			
+			info = info->next_port;
+		}
+	}
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
@@ -730,7 +729,7 @@
  * ---------------------------------------------------------------
  */
 
-static void esp_basic_init(struct esp_struct * info)
+static _INLINE_ void esp_basic_init(struct esp_struct * info)
 {
 	/* put ESPC in enhanced mode */
 	serial_out(info, UART_ESI_CMD1, ESI_SET_MODE);
@@ -791,13 +790,6 @@
 		return 0;
 	}
 
-	if (!info->port) {
-		if (info->tty)
-			set_bit(TTY_IO_ERROR, &info->tty->flags);
-		restore_flags(flags);
-		return 0;
-	}
-
 	if (!info->xmit_buf) {
 		info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL);
 		if (!info->xmit_buf) {
@@ -857,7 +849,7 @@
 	 */
 	if (!IRQ_ports[info->irq]) {
 		retval = request_irq(info->irq, rs_interrupt_single,
-					SA_INTERRUPT, "esp", NULL);
+					SA_INTERRUPT, "esp serial", NULL);
 
 		if (!retval) {
 			int i = 1;
@@ -872,7 +864,7 @@
 				if (!dma_buffer)
 					retval = -ENOMEM;
 				else
-					retval = request_dma(dma, "esp");
+					retval = request_dma(dma, "esp serial");
 
 				if (retval)
 					free_irq(info->irq, NULL);
@@ -892,10 +884,6 @@
 	}
 
 	info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
-#if defined(__alpha__) && !defined(CONFIG_PCI)
-	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
-#endif
-
 	serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
 	serial_out(info, UART_ESI_CMD2, UART_MCR);
 	serial_out(info, UART_ESI_CMD2, info->MCR);
@@ -979,6 +967,29 @@
 	 */
 	wake_up_interruptible(&info->delta_msr_wait);
 	wake_up_interruptible(&info->break_wait);
+
+	/* stop a DMA transfer on the port being closed, and start the next
+	   one */
+	   
+	if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) {
+		struct esp_struct *curr = info->next_port;
+		
+		disable_dma(dma);
+		clear_dma_ff(dma);
+		dma_bytes = 0;
+
+		while ((curr != info) && (!dma_bytes)) {
+			if (curr->tty) {
+				if (curr->stat_flags & ESP_STAT_NEED_DMA_RX)
+					receive_chars_dma(curr);
+				if ((curr->stat_flags & ESP_STAT_NEED_DMA_TX)
+				    && !dma_bytes)
+					transmit_chars_dma(curr);
+			}
+
+			curr = curr->next_port;
+		}
+	}
 	
 	/*
 	 * First unlink the serial port from the IRQ chain...
@@ -1025,7 +1036,21 @@
 		info->xmit_buf = 0;
 	}
 
-	if (info->tty_buf && !info->tty_buf->tqueue.sync) {
+	if (info->tty_buf) {
+		/* code borrowed from tty_io.c */
+		if (info->tty_buf->tqueue.sync) {
+			struct tq_struct *tq, *prev;
+
+			for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+				if (tq == &info->tty_buf->tqueue) {
+					if (prev)
+						prev->next = tq->next;
+					else
+						tq_timer = tq->next;
+					break;
+				}
+			}
+		}
 		kfree(info->tty_buf);
 		info->tty_buf = 0;
 	}
@@ -1065,8 +1090,7 @@
 	if (!info->tty || !info->tty->termios)
 		return;
 	cflag = info->tty->termios->c_cflag;
-	if (!(port = info->port))
-		return;
+	port = info->port;
 	i = cflag & CBAUD;
 	if (i & CBAUDEX) {
 		i &= ~CBAUDEX;
@@ -1251,8 +1275,7 @@
 	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
 		return;
 
-	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-	    !info->xmit_buf)
+	if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf)
 		return;
 
 	save_flags(flags); cli();
@@ -1303,10 +1326,9 @@
 	}
 	if (from_user)
 		up(&tmp_buf_sem);
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
-	    !(info->IER & UART_IER_THRI)) {
+	if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) {
 		info->IER |= UART_IER_THRI;
-		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); /* set mask */
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
 		serial_out(info, UART_ESI_CMD2, info->IER);
 	}
 	restore_flags(flags);
@@ -1371,8 +1393,8 @@
 	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
 		return;
 	
-	info->IER &= ~UART_IER_RDI;
 	cli();
+	info->IER &= ~UART_IER_RDI;
 	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
 	serial_out(info, UART_ESI_CMD2, info->IER);
 	serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
@@ -1393,8 +1415,8 @@
 	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
 		return;
 	
-	info->IER |= UART_IER_RDI;
 	cli();
+	info->IER |= UART_IER_RDI;
 	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
 	serial_out(info, UART_ESI_CMD2, info->IER);
 	serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
@@ -1438,7 +1460,6 @@
 	unsigned int		change_irq;
 	int 			i, retval = 0;
 	struct esp_struct *current_async;
-	unsigned long flags = 0;
 
 	if (!new_info)
 		return -EFAULT;
@@ -1450,7 +1471,10 @@
 		(info->port != new_serial.port) ||
 		(new_serial.baud_base != BASE_BAUD) ||
 		(new_serial.irq > 15) ||
-		(new_serial.irq < 1))
+		(new_serial.irq < 2) ||
+		(new_serial.irq == 6) ||
+		(new_serial.irq == 8) ||
+		(new_serial.irq == 13))
 		return -EINVAL;
 
 	change_irq = new_serial.irq != info->irq;
@@ -1474,7 +1498,6 @@
 		new_serial.irq = 9;
 
 	if (change_irq) {
-		save_flags(flags); cli();
 		i = 1;
 
 		while ((i < 16) && !IRQ_ports[i])
@@ -1487,14 +1510,10 @@
 				if ((current_async->line >= info->line) &&
 				    (current_async->line < (info->line + 8))) {
 					if (current_async == info) {
-						if (current_async->count > 1) {
-							restore_flags(flags);
+						if (current_async->count > 1)
 							return -EBUSY;
-						}
-					} else {
-						restore_flags(flags);
+					} else
 						return -EBUSY;
-					}
 				}
 
 				current_async = current_async->next_port;
@@ -1534,13 +1553,9 @@
 			serial_out(info, UART_ESI_CMD2, 0x02);
 		else
 			serial_out(info, UART_ESI_CMD2, info->irq);
-
-		restore_flags(flags);
 	}
 	
 check_and_exit:
-	if (!info->port)
-		return 0;
 	if (info->flags & ASYNC_INITIALIZED) {
 		if (((old_info.flags & ASYNC_SPD_MASK) !=
 		     (info->flags & ASYNC_SPD_MASK)) ||
@@ -1639,14 +1654,19 @@
  */
 static void send_break(	struct esp_struct * info, int duration)
 {
-	if (!info->port)
-		return;
 	cli();
 	serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
 	serial_out(info, UART_ESI_CMD2, 0x01);
 
 	interruptible_sleep_on(&info->break_wait);
 
+	if (current->signal & ~current->blocked) {
+		serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+		serial_out(info, UART_ESI_CMD2, 0x00);
+		sti();
+		return;
+	}
+
 	current->state = TASK_INTERRUPTIBLE;
 	current->timeout = jiffies + duration;
 	schedule();
@@ -1832,8 +1852,7 @@
 	if (!(old_termios->c_cflag & CBAUD) &&
 		(tty->termios->c_cflag & CBAUD)) {
 		info->MCR |= UART_MCR_DTR;
-		if (!tty->hw_stopped ||
-			!(tty->termios->c_cflag & CRTSCTS))
+		if (!(tty->termios->c_cflag & CRTSCTS))
 			info->MCR |= UART_MCR_RTS;
 		cli();
 		serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
@@ -1845,7 +1864,6 @@
 	/* Handle turning of CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
-		tty->hw_stopped = 0;
 		rs_start(tty);
 	}
 
@@ -1964,17 +1982,6 @@
 	info->event = 0;
 	info->tty = 0;
 
-	if (info->tty_buf) {
-		while (info->tty_buf->tqueue.sync) {
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + HZ / 10;
-			schedule();
-		}
-
-		kfree(info->tty_buf);
-		info->tty_buf = 0;
-	}
-			
 	if (info->blocked_open) {
 		if (info->close_delay) {
 			current->state = TASK_INTERRUPTIBLE;
@@ -2299,9 +2306,9 @@
 
 /*
  * ---------------------------------------------------------------------
- * esp_init() and friends
+ * espserial_init() and friends
  *
- * esp_init() is called at boot-time to initialize the serial driver.
+ * espserial_init() is called at boot-time to initialize the serial driver.
  * ---------------------------------------------------------------------
  */
 
@@ -2317,45 +2324,47 @@
 }
 
 /*
- * This routine is called by esp_init() to initialize a specific serial
- * port.  It determines what type of UART chip this serial port is
- * using: 8250, 16450, 16550, 16550A.  The important question is
- * whether or not this UART is a 16550A or not, since this will
- * determine whether or not we can use its FIFO features or not.
+ * This routine is called by espserial_init() to initialize a specific serial
+ * port.
  */
-static void autoconfig(struct esp_struct * info)
+static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start)
 {
-	unsigned char status1, status2, scratch;
-	unsigned port = info->port;
+	int port_detected = 0;
 	unsigned long flags;
 
-	if (!port)
-		return;
-
 	save_flags(flags); cli();
 	
 	/*
 	 * Check for ESP card
 	 */
-	scratch = serial_in(info, UART_ESI_BASE);
-	if (scratch == 0xf3) {
+
+	if (!check_region(info->port, 8) && 
+	    serial_in(info, UART_ESI_BASE) == 0xf3) {
 		serial_out(info, UART_ESI_CMD1, 0x00);
 		serial_out(info, UART_ESI_CMD1, 0x01);
-		status1 = serial_in(info, UART_ESI_STAT2);
-		status2 = status1 & 0x70;
-		if (status2 != 0x20) {
-			printk(" Old ESP found at %x\n",info->port);
-			info->port = 0;
-		} else {
-			serial_out(info, UART_ESI_CMD1, 0x02);
-			status1 = serial_in(info, UART_ESI_STAT1) & 0x03;
+
+		if ((serial_in(info, UART_ESI_STAT2) & 0x70) == 0x20) {
+			port_detected = 1;
+
 			if (!(info->irq)) {
-				if ((status1 == 0x00) || (status1 == 0x02))
-					info->irq = 4;
-				else
+				serial_out(info, UART_ESI_CMD1, 0x02);
+
+				if (serial_in(info, UART_ESI_STAT1) & 0x01)
 					info->irq = 3;
+				else
+					info->irq = 4;
 			}
-			request_region(port,8,"esp");
+
+			if (IRQ_ports[0] &&
+			    (IRQ_ports[0]->port == (info->port - 8))) {
+				release_region(*region_start,
+					       info->port - *region_start);
+			} else
+				*region_start = info->port;
+
+			request_region(*region_start,
+				       info->port - *region_start + 8,
+				       "esp serial");
 
 			/* put card in enhanced mode */
 			/* this prevents access through */
@@ -2367,19 +2376,19 @@
 			serial_out(info, UART_ESI_CMD2, UART_MCR);
 			serial_out(info, UART_ESI_CMD2, 0x00);
 		}
-	} else {
-		info->port = 0;
 	}
 
 	restore_flags(flags);
+	return (port_detected);
 }
 
 /*
  * The serial driver boot-time initialization code!
  */
-int esp_init(void)
+int espserial_init(void)
 {
 	int i, offset;
+	int region_start;
 	struct esp_struct * info;
 	int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380};
 	
@@ -2389,6 +2398,14 @@
 		IRQ_ports[i] = 0;
 	}
 
+	for (i = 0; i < NR_PRIMARY; i++)
+		if (irq[i] != 0)
+			if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) ||
+			    (irq[i] == 8) || (irq[i] == 13))
+				irq[i] = 0;
+			else if (irq[i] == 2)
+				irq[i] = 9;
+
 	if ((dma != 1) && (dma != 3))
 		dma = 1;
 
@@ -2458,19 +2475,12 @@
 
 	do {
 		info->port = esp[i] + offset;
+		info->irq = irq[i];
+		info->line = (i * 8) + (offset / 8);
 
-		/* check if i/o region is already in use */
-		if (check_region(info->port, 8)) {
-			/* if it is a primary port, skip secondary ports */
-			if (!offset)
-				i++;
-			else if (offset == 56) {
-				i++;
-				offset = 0;
-			}
-			else
-				offset += 8;
-
+		if (!autoconfig(info, &region_start)) {
+			i++;
+			offset = 0;
 			continue;
 		}
 
@@ -2478,11 +2488,7 @@
 		info->flags = STD_COM_FLAGS;
 		if (info->custom_divisor)
 			info->flags |= ASYNC_SPD_CUST;
-		info->irq = irq[i];
-
 		info->magic = ESP_MAGIC;
-		info->line = (i * 8) + (offset / 8);
-		info->tty = 0;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
 		info->tqueue.routine = do_softint;
@@ -2492,15 +2498,6 @@
 		info->callout_termios = esp_callout_driver.init_termios;
 		info->normal_termios = esp_driver.init_termios;
 
-		if (info->irq == 2)
-			info->irq = 9;
-
-		autoconfig(info);
-		if (!info->port) {
-			i++;
-			offset = 0;
-			continue;
-		}
 		if (IRQ_ports[0])
 			IRQ_ports[0]->prev_port = info;
 		info->next_port = IRQ_ports[0];
@@ -2539,13 +2536,14 @@
 
 int init_module(void)
 {
-	return esp_init();
+	return espserial_init();
 }
 
 void cleanup_module(void) 
 {
 	unsigned long flags;
 	int e1, e2;
+	unsigned int region_start, region_end;
 	struct esp_struct *current_async, *temp_async;
 
 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
@@ -2561,8 +2559,31 @@
 
 	current_async = IRQ_ports[0];
 	while (current_async != 0) {
-		release_region(current_async->port, 8);
-		current_async = current_async->next_port;
+		if (current_async->port != 0) {
+			region_start = region_end = current_async->port;
+			temp_async = current_async;
+
+			while (temp_async != 0) {
+				if ((region_start - temp_async->port) == 8) {
+					region_start = temp_async->port;
+					temp_async->port = 0;
+					temp_async = current_async;
+				} else if ((temp_async->port - region_end)
+					   == 8) {
+					region_end = temp_async->port;
+					temp_async->port = 0;
+					temp_async = current_async;
+				} else
+					temp_async = temp_async->next_port;
+			}
+			
+			release_region(region_start,
+				       region_end - region_start + 8);
+		}
+
+		temp_async = current_async->next_port;
+		kfree(current_async);
+		current_async = temp_async;
 	}
 
 	if (dma_buffer)
@@ -2571,13 +2592,5 @@
 
 	if (tmp_buf)
 		free_page((unsigned long)tmp_buf);
-
-	/* free the port information */
-	current_async = IRQ_ports[0];
-	while (current_async != 0) {
-		temp_async = current_async->next_port;
-		kfree(current_async);
-		current_async = temp_async;
-	}
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.19/linux/drivers/char/esp.h linux/drivers/char/esp.h
--- v2.1.19/linux/drivers/char/esp.h	Wed Dec 18 15:58:47 1996
+++ linux/drivers/char/esp.h	Tue Dec 31 21:30:29 1996
@@ -46,9 +46,10 @@
 #define ESI_NO_COMMAND		0xff
 
 #define ESP_STAT_RX_TIMEOUT	0x01
-#define ESP_STAT_NEED_DMA	0x02
-#define ESP_STAT_DMA_RX		0x04
-#define ESP_STAT_DMA_TX		0x08
+#define ESP_STAT_NEED_DMA_RX	0x02
+#define ESP_STAT_NEED_DMA_TX    0x04
+#define ESP_STAT_DMA_RX		0x08
+#define ESP_STAT_DMA_TX		0x10
 
 #define ESP_EVENT_WRITE_WAKEUP	0
 #define ESP_MAGIC		0x53ee
diff -u --recursive --new-file v2.1.19/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- v2.1.19/linux/drivers/char/tpqic02.c	Thu Dec 12 19:37:02 1996
+++ linux/drivers/char/tpqic02.c	Tue Dec 31 16:27:23 1996
@@ -1,12 +1,9 @@
-/* $Id: tpqic02.c,v 0.4.1.5 1994/10/29 02:46:13 root Exp root $
+/* $Id: tpqic02.c,v 0.7.1.5 1996/12/14 22:58:52 root Exp root $
  *
- * Driver for tape drive support for Linux-i386 1.1.58
+ * Driver for tape drive support for Linux-i386
  *
- * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved.
- * Current e-mail address: hennus@sky.ow.org [This is a UUCP link.]
- * [If you are unable to reach me directly, try the TAPE mailing list
- * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as
- * the first line in your message.]
+ * Copyright (c) 1992--1996 by H. H. Bergman. All rights reserved.
+ * Current e-mail address: hennus@cybercomm.nl
  *
  * Distribution of this program in executable form is only allowed if
  * all of the corresponding source files are made available through the same
@@ -33,154 +30,28 @@
  *
  * You are not allowed to change this line nor the text above.
  *
- * $Log: tpqic02.c,v $
- * Revision 0.4.1.5  1994/10/29  02:46:13  root
- * Minor cleanups.
+ * 1996/10/10   Emerald changes
  *
- * Revision 0.4.1.4  1994/07/21  02:15:45  root
- * ifdef'd DDI. Exception masks.
+ * 1996/05/21	Misc changes+merges+cleanups + I/O reservations
  *
- * Revision 0.4.1.3  1994/05/03  01:49:09  root
- * Initial attempt at Mountain support for the Mountain 7150.
- * Based on patches provided by Erik Jacobson.
+ * 1996/05/20	Module support patches submitted by Brian McCauley.
  *
- * Revision 0.4.1.2  1994/03/18  21:16:50  root
- * Many driver messages can now be turned off (runtime selectable).
+ * 1994/05/03	Initial attempt at Mountain support for the Mountain 7150.
+ * Based on patches provided by Erik Jacobson. Still incomplete, I suppose.
  *
- * Revision 0.4.1.1  1994/02/16  19:47:22  root
- * First stab at runtime debug-variable.
+ * 1994/02/07	Archive changes & some cleanups by Eddy Olk.
  *
- * Revision 0.4  1994/02/15  01:53:16  root
- * DYNCONF mark II.
- * Minor cleanups.
+ * 1994/01/19	Speed measuring stuff moved from aperf.h to delay.h.
+ *		BogoMips (tm) introduced by Linus.
  *
- * Revision 0.3  1994/02/07  01:23:16  root
- * More improved DYNCONF.
- * Archive changes & some cleanups by Eddy Olk.
- * Removed status_open, more cleanups, misc other.
- *
- * Revision 0.2.1.25  1994/01/24  02:01:33  root
- * Changed tape_qic02 to QIC02_TAPE.
- * Changes to prepare for DYNCONF.
- *
- * Revision 0.2.1.24  1994/01/23  07:27:18  root
- * Attempt to remove compilation warnings, G++ bug,
- * Linus changed TAPE_QIC02 to QIC02_TAPE.
- *
- * Revision 0.2.1.23  1994/01/20  23:49:28  root
- * Changed some exception decoding stuff.
- * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global.
- * Attempt to fix cartridge-changed-problem for 2150L.
- * Release irq and dma reservations if initial reset fails.
- *
- * Revision 0.2.1.22  1994/01/19  20:56:55  root
- * Speed measuring stuff moved from aperf.h to delay.h.
- * BogoMips (tm) introduced by Linus.
- *
- * Revision 0.2.1.21  1993/06/18  19:04:33  root
- * minor fixes for 0.99.10.
- *
- * Revision 0.2.1.20  1993/06/11  21:38:51  root
- * Added exception code for status 0x8000 (Cypher weirdness).
- *
- * Revision 0.2.1.19  1993/04/19  23:13:59  root
- * Cleanups. Changed to 0.99.8.
- *
- * Revision 0.2.1.18  1993/03/22  17:39:47  root
- * Moved to 0.99.7. Added Archive MTSEEK and MTTELL support.
- *
- * Revision 0.2.1.17  1993/03/08  18:51:59  root
- * Tried to `fix' write-once bug in previous release.
- *
- * Revision 0.2.1.16  1993/03/01  00:06:16  root
- * Use register_chrdev() for 0.99.6.
- *
- * Revision 0.2.1.15  1993/02/25  00:14:25  root
- * minor cleanups.
- *
- * Revision 0.2.1.14  1993/01/25  00:06:14  root
- * Kernel udelay. Eof fixups.
- * Removed report_ read/write dummies; have strace(1) now.
- *
- * Revision 0.2.1.13  1993/01/10  02:24:43  root
- * Rewrote wait_for_ready() to use newer schedule() features.
- * This improves performance for rewinds etc.
- *
- * Revision 0.2.1.12  1993/01/05  18:44:09  root
- * Changes for 0.99.1. Fixed `restartable reads'.
- *
- * Revision 0.2.1.11  1992/11/28  01:19:10  root
- * Changes to exception handling (significant).
- * Changed returned error codes. Hopefully they're correct now.
- * Changed declarations to please gcc-2.3.1.
- * Patch to deal with bogus interrupts for Archive cards.
- *
- * Revision 0.2.1.10  1992/10/28  00:50:44  root
- * underrun/error counter needed byte swapping.
- *
- * Revision 0.2.1.9  1992/10/15  17:06:01  root
- * Removed online() stuff. Changed EOF handling.
- *
- * Revision 0.2.1.8  1992/10/02  22:25:48  root
- * Removed `no_sleep' parameters (got usleep() now),
- * cleaned up some comments.
- *
- * Revision 0.2.1.7  1992/09/27  01:41:55  root
- * Changed write() to do entire user buffer in one go, rather than just
- * a kernel-buffer sized portion each time.
- *
- * Revision 0.2.1.6  1992/09/21  02:15:30  root
- * Introduced udelay() function for microsecond-delays.
- * Trying to use get_dma_residue rather than TC flags.
- * Patch to fill entire user buffer on reads before
- * returning.
- *
- * Revision 0.2.1.5  1992/09/19  02:31:28  root
- * Some changes based on patches by Eddy Olk to
- * support Archive SC402/SC499R controller cards.
- *
- * Revision 0.2.1.4  1992/09/07  01:37:37  root
- * Minor changes
- *
- * Revision 0.2.1.3  1992/08/13  00:11:02  root
- * Added some support for Archive SC402 and SC499 cards.
- * (Untested.)
- *
- * Revision 0.2.1.2  1992/08/10  02:02:36  root
- * Changed from linux/system.h macros to asm/dma.h inline functions.
- *
- * Revision 0.2.1.1  1992/08/08  01:12:39  root
- * cleaned up a bit. added stuff for selftesting.
- * preparing for asm/dma.h instead of linux/system.h
- *
- * Revision 0.2  1992/08/03  20:11:30  root
- * Changed to use new IRQ allocation. Padding now done at runtime, pads to
- * 512 bytes. Because of this the page regs must be re-programmed every
- * block! Added hooks for selftest commands.
- * Moved to linux-0.97.
- *
- * Revision 0.1.0.5  1992/06/22  22:20:30  root
- * moved to Linux 0.96b
- *
- * Revision 0.1.0.4  1992/06/18  02:00:04  root
- * Use minor bit-7 to enable/disable printing of extra debugging info
- * when do tape access.
- * Added semop stuff for DMA/IRQ allocation checking. Don't think this
- * is the right way to do it though.
- *
- * Revision 0.1.0.3  1992/06/01  01:57:34  root
- * changed DRQ to DMA. added TDEBUG ifdefs to reduce output.
- *
- * Revision 0.1.0.2  1992/05/31  14:02:38  root
- * changed SET_DMA_PAGE handling slightly.
- *
- * Revision 0.1.0.1  1992/05/27  12:12:03  root
- * Can now use multiple files on tape (sort of).
- * First release.
+ * 1993/01/25	Kernel udelay. Eof fixups.
+ * 
+ * 1992/09/19	Some changes based on patches by Eddy Olk to support
+ * 		Archive SC402/SC499R controller cards.
  *
- * Revision 0.1  1992/05/26  01:16:31  root
- * Initial version. Copyright H. H. Bergman 1992
+ * 1992/05/27	First release.
  *
+ * 1992/05/26	Initial version. Copyright H. H. Bergman 1992
  */
 
 /* After the legalese, now the important bits:
@@ -200,14 +71,10 @@
 
 #define REALLY_SLOW_IO		/* it sure is ... */
 
+#include <linux/module.h>
+
 #include <linux/config.h>
 
-#ifdef MODULE
-  #ifdef CONFIG_QIC02_DYNCONF
-    #error dynamic configuration as module not implemented!
-  #endif
-#endif
- 
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -218,6 +85,7 @@
 #include <linux/mtio.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
+#include <linux/ioport.h>
 #include <linux/tpqic02.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
@@ -227,14 +95,9 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
-/* We really shouldn't be using this define.. */
-#define IOCCMD_MASK 0x0000ffff
-
 /* check existence of required configuration parameters */
-#if !defined(QIC02_CMD_PORT) || \
-    !defined(QIC02_TAPE_IRQ) || \
-    !defined(QIC02_TAPE_DMA)
-#error qic02_tape configuration error
+#if !defined(QIC02_CMD_PORT) || !defined(QIC02_TAPE_IRQ) || !defined(QIC02_TAPE_DMA)
+# error qic02_tape configuration error
 #endif
 
 
@@ -248,17 +111,19 @@
 /* This holds the dynamic configuration info for the interface
  * card+drive info if runtime configuration has been selected.
  */
-struct mtconfiginfo qic02_tape_dynconf = { 0, };	/* user settable */
-struct qic02_ccb qic02_tape_ccb = { 0, };		/* private stuff */
+
+static struct mtconfiginfo qic02_tape_dynconf = 	/* user settable */
+    { 0, 0, BOGUS_IRQ, 0, 0, TPQD_DEFAULT_FLAGS, };
+static struct qic02_ccb qic02_tape_ccb = { 0, };	/* private stuff */
 
 #else
 
-unsigned long qic02_tape_debug;
+unsigned long qic02_tape_debug = TPQD_DEFAULT_FLAGS;
 
 # if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN))
 #  error No valid interface card specified
 # endif
-#endif
+#endif /* CONFIG_QIC02_DYNCONF */
 
 static volatile int ctlbits = 0;     /* control reg bits for tape interface */
 
@@ -268,8 +133,8 @@
 
 static volatile struct tpstatus tperror;	/* last drive status */
 
-static char rcs_revision[] = "$Revision: 0.4.1.5 $";
-static char rcs_date[] = "$Date: 1994/10/29 02:46:13 $";
+static char rcs_revision[] = "$Revision: 0.7.1.5 $";
+static char rcs_date[] = "$Date: 1996/12/14 22:58:52 $";
 
 /* Flag bits for status and outstanding requests.
  * (Could all be put in one bit-field-struct.)
@@ -330,27 +195,17 @@
  */
 static int  mode_access;	/* access mode: READ or WRITE */
 
+static int qic02_get_resources(void);
+static void qic02_release_resources(void);
 
-/* This is the actual kernel buffer where the interrupt routines read
- * from/write to. It is needed because the DMA channels 1 and 3 cannot
+/* This is a pointer to the actual kernel buffer where the interrupt routines
+ * read from/write to. It is needed because the DMA channels 1 and 3 cannot
  * always access the user buffers. [The kernel buffer must reside in the
- * lower 16MBytes of system memory because of the DMA controller.]
- * The user must ensure that a large enough buffer is passed to the
- * kernel, in order to reduce tape repositioning.
- *
- * The buffer is 512 bytes larger than expected, because I want to align it
- * at 512 bytes, to prevent problems with 64k boundaries.
+ * lower 16MBytes of system memory because of the DMA controller.] The user
+ * must ensure that a large enough buffer is passed to the kernel, in order
+ * to reduce tape repositioning wear and tear.
  */
-
-#ifdef MODULE
-static char *qic02_tape_buf = NULL;
-#else
-static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
-/* A really good compiler would be able to align this at 512 bytes... :-( */
-#endif /* MODULE */
-
-static unsigned long buffaddr;	/* aligned physical address of buffer */
-
+static unsigned long buffaddr = 0;	/* physical address of buffer */
 
 /* This translates minor numbers to the corresponding recording format: */
 static const char *format_names[] = {
@@ -436,7 +291,15 @@
 };
 #define NR_OF_EXC	(sizeof(exception_list)/sizeof(struct exception_list_type))
 
-
+/* Compare expected struct size and actual struct size. This
+ * is useful to catch programs compiled with old #includes.
+ */
+#define CHECK_IOC_SIZE(structure) \
+	if (_IOC_SIZE(iocmd) != sizeof(struct structure)) { \
+		tpqputs(TPQD_ALWAYS, "sizeof(struct " #structure \
+			") does not match!"); \
+		return -EFAULT; \
+	} \
 
 static void tpqputs(unsigned long flags, const char *s)
 {
@@ -445,8 +308,6 @@
 } /* tpqputs */
 
 
-
-
 /* Perform byte order swapping for a 16-bit word.
  *
  * [FIXME] This should probably be in include/asm/
@@ -468,27 +329,36 @@
  */
 static void ifc_init(void)
 {
-	if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ {
-		ctlbits = WT_CTL_ONLINE;	/* online */
-		outb_p(ctlbits, QIC02_CTL_PORT);
-
-	} else if (QIC02_TAPE_IFC == ARCHIVE) {
-		ctlbits = 0;			/* no interrupts yet */
-		outb_p(ctlbits, QIC02_CTL_PORT);
-		outb_p(0, AR_RESET_DMA_PORT);	/* dummy write to reset DMA */
-
-	} else /* MOUNTAIN */ {
-		ctlbits = MTN_CTL_ONLINE;       /* online, and logic enabled */
-		outb_p(ctlbits, QIC02_CTL_PORT);
-	}
+    if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */
+    {
+	ctlbits = WT_CTL_ONLINE;	/* online */
+	outb_p(ctlbits, QIC02_CTL_PORT);
+    }
+    else if (QIC02_TAPE_IFC == ARCHIVE)
+    {
+	ctlbits = 0;			/* no interrupts yet */
+	outb_p(ctlbits, QIC02_CTL_PORT);
+	outb_p(0, AR_RESET_DMA_PORT);	/* dummy write to reset DMA */
+    }
+    else /* MOUNTAIN */
+    {
+	ctlbits = MTN_CTL_ONLINE;       /* online, and logic enabled */
+	outb_p(ctlbits, QIC02_CTL_PORT);
+    }
 } /* ifc_init */
 
 
 static void report_qic_exception(unsigned n)
 {
-	if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_qic_exception"); n = 0; }
-	if (TPQDBG(SENSE_TEXT) || n==0)
-		printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg);
+    if (n >= NR_OF_EXC)
+    {
+	tpqputs(TPQD_ALWAYS, "Oops -- report_qic_exception");
+	n = 0;
+    }
+    if (TPQDBG(SENSE_TEXT) || n==0)
+    {
+	printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg);
+    }
 } /* report_qic_exception */
 
 
@@ -499,126 +369,63 @@
  */
 static int decode_qic_exception_nr(unsigned s)
 {
-	int i;
+    int i;
 
-	for (i=1; i<NR_OF_EXC; i++) {
-		if ((s & exception_list[i].mask)==exception_list[i].code)
-			return i;
+    for (i=1; i<NR_OF_EXC; i++)
+    {
+	if ((s & exception_list[i].mask)==exception_list[i].code)
+	{
+	    return i;
 	}
-	printk(TPQIC02_NAME ": decode_qic_exception_nr: exception(%x) not recognized\n", s);
-	return 0;
+    }
+    printk(TPQIC02_NAME ": decode_qic_exception_nr: exception(%x) not recognized\n", s);
+    return 0;
 } /* decode_qic_exception_nr */
 
 
-#ifdef OBSOLETE
-/* There are exactly 14 possible exceptions, as defined in QIC-02 rev F.
- * Some are FATAL, some aren't. Currently all exceptions are treated as fatal.
- * Especially 6 and 14 should not abort the transfer. RSN...
- * Should probably let sense() figure out the exception number using the code
- * below, and just report the error based on the number here, returning a code
- * for FATAL/CONTINUABLE.
- */
-static void report_error(int s)
-{
-	short n = -1;
-
-	if (s & TP_ST1) {
-		if (s & TP_ILL)		/* 12: Illegal command. FATAL */
-			n = 12;
-		if (s & TP_POR)		/* 13: Reset occurred. FATAL */
-			n = 13;
-	} 
-	else if (s & TP_ST0) {
-		if (s & TP_EOR) {	/* extra: 15: End of Recorded Media. CONTINUABLE */
-			n = 15;
-			/********** should set flag here **********/
-		}
-		else if (s & TP_EOM)		/* 4: End Of Media. CONTINUABLE */
-			n = 4;
-		else if (s & TP_USL)		/* 2: Drive not online. FATAL */
-			n = 2;
-		else if (s & TP_CNI) {	/* 1: Cartridge not in place. FATAL */
-			n = 1;
-			need_rewind = YES;
-			status_eof_detected = NO;
-			status_eom_detected = NO;
-		}
-		else if (s & TP_UDA) {
-			if (s & TP_BNL) {
-				if (s & TP_NDT) {
-					if (s & TP_BOM)		/* 9: Read error. No data detected & EOM. CONTINUABLE */
-						n = 9;
-					else if (s & TP_EOM)	/* 10: Read error. No data detected & BOM. CONTINUABLE */
-						n = 10;
-					else			/* 8: Read error. No data detected. CONTINUABLE */
-						n = 8;
-				} else { /* 7: Read error. Cannot recover block, filler substituted. CONTINUABLE */
-					tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
-					n = 7;
-				}
-			}
-			else {
-				if (s & TP_EOM)	/* 5: Read or Write error. Rewind tape. FATAL */
-					n = 5;
-				else {		/* 6: Read error. Bad block transferred. CONTINUABLE */
-					/* block is bad, but transfer may continue.
-					 * This is why some people prefer not to
-					 * use compression on backups...
-					 */
-					tpqputs(TPQD_ALWAYS, "[CRC failed!]");
-					n = 6;
-				}
-			}
-		}
-		else if (s & TP_FIL) {
-			if (s & TP_MBD) {	/* 14: Marginal block detected. CONTINUABLE */
-				tpqputs(TPQD_ALWAYS, "[Marginal block]");
-				n = 14;
-			} else			/* 11: File mark detected. CONTINUABLE */
-				n = 11;
-		}
-		else if (s & TP_WRP)		/* 3: Write protected cartridge. FATAL */
-			n = 3;
-	}
-	if (n >= 0)
- 		sensemsg(n);
-} /* report_error */
-#endif
-
 
 /* Perform appropriate action for certain exceptions.
  * should return a value to indicate stop/continue (in case of bad blocks)
  */
 static void handle_qic_exception(int exnr, int exbits)
 {
-	if (exnr==EXC_NCART) {
-		/* Cartridge was changed. Redo sense().
-		 * EXC_NCART should be handled in open().
-		 * It is not permitted to remove the tape while
-		 * the tape driver has open files. 
-		 */
-		need_rewind = YES;
-		status_eof_detected = NO;
-		status_eom_detected = NO;
-	}
-	else if (exnr==EXC_XFILLER)
-		tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
-	else if (exnr==EXC_XBAD)
-		tpqputs(TPQD_ALWAYS, "[CRC failed!]");
-	else if (exnr==EXC_MARGINAL) {
-		/* A marginal block behaves much like a FM.
-		 * User may continue reading, if desired.
-		 */
-		tpqputs(TPQD_ALWAYS, "[Marginal block]");
-		doing_read = NO;
-	} else if (exnr==EXC_FM)
-		doing_read = NO;
+    if (exnr==EXC_NCART)
+    {
+	/* Cartridge was changed. Redo sense().
+	 * EXC_NCART should be handled in open().
+	 * It is not permitted to remove the tape while
+	 * the tape driver has open files. 
+	 */
+	need_rewind = YES;
+	status_eof_detected = NO;
+	status_eom_detected = NO;
+    }
+    else if (exnr==EXC_XFILLER)
+    {
+	tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
+    }
+    else if (exnr==EXC_XBAD)
+    {
+	tpqputs(TPQD_ALWAYS, "[CRC failed!]");
+    }
+    else if (exnr==EXC_MARGINAL)
+    {
+	/* A marginal block behaves much like a FM.
+	 * User may continue reading, if desired.
+	 */
+	tpqputs(TPQD_ALWAYS, "[Marginal block]");
+	doing_read = NO;
+    }
+    else if (exnr==EXC_FM)
+    {
+	doing_read = NO;
+    }
 } /* handle_qic_exception */
 
 
 static inline int is_exception(void)
 {
-	return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0;
+    return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0;
 } /* is_exception */
 
 
@@ -628,41 +435,53 @@
  */
 static int tape_reset(int verbose)
 {
-	ifc_init();				/* reset interface card */
-
-	/* assert reset */
-	if (QIC02_TAPE_IFC == MOUNTAIN)
-		outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
-	else /* WANGTEK, ARCHIVE */
- 		outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT);
-
-	/* Next, we need to wait >=25 usec. */
-	udelay(30);
+    ifc_init();				/* reset interface card */
 
-	/* after reset, we will be at BOT (modulo an automatic rewind) */
-	status_eof_detected = NO;
-	status_eom_detected = NO;
-	status_cmd_pending = 0;
-	need_rewind = YES;
-	doing_read = doing_write = NO;
-	ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0;
-
-	/* de-assert reset */
-	if (QIC02_TAPE_IFC == MOUNTAIN)
-		outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
-	else
-		outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT);
-
-	/* KLUDGE FOR G++ BUG */
-	{ int stat = inb_p(QIC02_STAT_PORT);
-	  status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); }
-	/* if successful, inb(STAT) returned RESETVAL */
-	if (status_dead == YES)
-		printk(TPQIC02_NAME ": reset failed!\n");
-	else if (verbose)
-		printk(TPQIC02_NAME ": reset successful\n");
-
-	return (status_dead == YES)? TE_DEAD : TE_OK;
+    /* assert reset */
+    if (QIC02_TAPE_IFC == MOUNTAIN)
+    {
+	outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
+    }
+    else /* WANGTEK, ARCHIVE */
+    {
+	outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT);
+    }
+    
+    /* Next, we need to wait >=25 usec. */
+    udelay(30);
+
+    /* after reset, we will be at BOT (modulo an automatic rewind) */
+    status_eof_detected = NO;
+    status_eom_detected = NO;
+    status_cmd_pending = 0;
+    need_rewind = YES;
+    doing_read = doing_write = NO;
+    ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0;
+    
+    /* de-assert reset */
+    if (QIC02_TAPE_IFC == MOUNTAIN)
+    {
+	outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
+    }
+    else
+    {
+	outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT);
+    }
+    
+    /* KLUDGE FOR G++ BUG */
+    { int stat = inb_p(QIC02_STAT_PORT);
+	status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); }
+    /* if successful, inb(STAT) returned RESETVAL */
+    if (status_dead == YES)
+    {
+	printk(TPQIC02_NAME ": reset failed!\n");
+    }
+    else if (verbose)
+    {
+	printk(TPQIC02_NAME ": reset successful\n");
+    }
+    
+    return (status_dead == YES)? TE_DEAD : TE_OK;
 } /* tape_reset */
 
 
@@ -1852,9 +1671,10 @@
 		 * if dma channel hasn't finished last byte yet.
 		 */
 		r = 0;
-/* Skip next ready check for Archive controller because
- * it may be busy reading ahead. Weird. --hhb
- */
+
+	    /* Skip next ready check for Archive controller because
+	     * it may be busy reading ahead. Weird. --hhb
+	     */
 		if (QIC02_TAPE_IFC == WANGTEK)	/* I think this is a drive-dependency, not IFC -- hhb */
 			if (stat & QIC02_STAT_READY) {		/* not ready */
 				tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready");
@@ -1892,7 +1712,7 @@
 
 
 static long long qic02_tape_lseek(struct inode * inode, struct file * file,
-	long long offset, int origin)
+				  long long offset, int origin)
 {
 	return -EINVAL;	/* not supported */
 } /* qic02_tape_lseek */
@@ -1931,145 +1751,187 @@
  */
 
 static long qic02_tape_read(struct inode * inode, struct file * filp,
-	char * buf, unsigned long count)
+			    char * buf, unsigned long count)
 {
-	kdev_t dev = inode->i_rdev;
-	unsigned short flags = filp->f_flags;
-	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
-	int stat;
-
-	if (status_zombie==YES) {
-		tpqputs(TPQD_ALWAYS, "configs not set");		
-		return -ENXIO;
-	}
+    int err;
+    kdev_t dev = inode->i_rdev;
+    unsigned short flags = filp->f_flags;
+    unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
+    int stat;
+
+    if (status_zombie==YES)
+    {
+	tpqputs(TPQD_ALWAYS, "configs not set");		
+	return -ENXIO;
+    }
+
+    if (TP_DIAGS(current_tape_dev))
+      /* can't print a ``long long'' (for filp->f_pos), so chop it */
+      printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n",
+	     MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags);
+    
+    if (count % TAPE_BLKSIZE)	/* Only allow mod 512 bytes at a time. */
+    {
+	tpqputs(TPQD_BLKSZ, "Wrong block size");
+	return -EINVAL;
+    }
 
-	if (TP_DIAGS(current_tape_dev))
-		/* can't print a ``long long'' (for filp->f_pos), so chop it */
-		printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n",
-			MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags);
+    /* Just assume everything is ok. Controller will scream if not. */
 
-	if (count % TAPE_BLKSIZE) {	/* Only allow mod 512 bytes at a time. */
-		tpqputs(TPQD_BLKSZ, "Wrong block size");
-		return -EINVAL;
+    if (status_bytes_wr)	/* Once written, no more reads, 'till after WFM. */
+    {
+	return -EACCES;
+    }
+    
+    /* This is rather ugly because it has to implement a finite state
+     * machine in order to handle the EOF situations properly.
+     */
+    while ((signed)count>=0)
+    {
+	bytes_done = 0;
+	/* see how much fits in the kernel buffer */
+	bytes_todo = TPQBUF_SIZE;
+	if (bytes_todo>count)
+	{
+	    bytes_todo = count;
 	}
-
-	/* Just assume everything is ok. Controller will scream if not. */
-
-	if (status_bytes_wr)	/* Once written, no more reads, 'till after WFM. */
-		return -EACCES;
-
-	/* This is rather ugly because it has to implement a finite state
-	 * machine in order to handle the EOF situations properly.
-	 */
-	while ((signed)count>=0) {
-		bytes_done = 0;
-		/* see how much fits in the kernel buffer */
-		bytes_todo = TPQBUF_SIZE;
-		if (bytes_todo>count)
-			bytes_todo = count;
-
-		/* Must ensure that user program sees exactly one EOF token (==0) */
-		if (return_read_eof==YES) {
-			if (TPQDBG(DEBUG))
-				printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done);
-
-			if (reported_read_eof==NO) {
-				/* have not yet returned EOF to user program */
-				if (total_bytes_done>0) {
-					return total_bytes_done; /* next time return EOF */
-				} else {
-					reported_read_eof = YES; /* move on next time */
-					return 0;		 /* return EOF */
-				}				
-			} else {
-				/* Application program has already received EOF
-				 * (above), now continue with next file on tape,
-				 * if possible.
-				 * When the FM is reached, EXCEPTION is set,
-				 * causing a sense(). Subsequent read/writes will
-				 * continue after the FM.
-				 */
-/*********** ?????????? this should check for (EOD|NDT), not EOM, 'cause we can read past EW: ************/
-				if (status_eom_detected)
-					/* If EOM, nothing left to read, so keep returning EOFs.
-					 *** should probably set some flag to avoid clearing
-					 *** status_eom_detected through ioctls or something
-					 */
-					return 0;
-				else {
-					/* just eof, there may be more files ahead... */
-					return_read_eof = NO;
-					reported_read_eof = NO;
-					status_eof_detected = NO; /* reset this too */
-					/*fall through*/
-				}
-			}
+	
+	/* Must ensure that user program sees exactly one EOF token (==0) */
+	if (return_read_eof==YES)
+	{
+	    if (TPQDBG(DEBUG))
+	    {
+		printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done);
+	    }
+	    
+	    if (reported_read_eof==NO)
+	    {
+		/* have not yet returned EOF to user program */
+		if (total_bytes_done>0)
+		{
+		    return total_bytes_done; /* next time return EOF */
 		}
-
-/*****************************/
-		if (bytes_todo==0)
-			return total_bytes_done;
-
-		if (bytes_todo>0) {
-			/* start reading data */
-			if (is_exception())	/****************************************/
-				tpqputs(TPQD_DMAX, "is_exception() before start_dma()!");
+		else
+		{
+		    reported_read_eof = YES; /* move on next time */
+		    return 0;		 /* return EOF */
+		}				
+	    }
+	    else
+	    {
+		/* Application program has already received EOF
+		 * (above), now continue with next file on tape,
+		 * if possible.
+		 * When the FM is reached, EXCEPTION is set,
+		 * causing a sense(). Subsequent read/writes will
+		 * continue after the FM.
+		 */
+		/*********** ?????????? this should check for (EOD|NDT), not EOM, 'cause we can read past EW: ************/
+		if (status_eom_detected)
+		{
+		    /* If EOM, nothing left to read, so keep returning EOFs.
+		     *** should probably set some flag to avoid clearing
+		     *** status_eom_detected through ioctls or something
+		     */
+		    return 0;
+		}
+		else
+		{
+		    /* just eof, there may be more files ahead... */
+		    return_read_eof = NO;
+		    reported_read_eof = NO;
+		    status_eof_detected = NO; /* reset this too */
+		    /*fall through*/
+		}
+	    }
+	}
+	
+	/*****************************/
+	if (bytes_todo==0)
+	{
+	    return total_bytes_done;
+	}
+	
+	if (bytes_todo>0)
+	{
+	    /* start reading data */
+	    if (is_exception())	/****************************************/
+	    {
+		tpqputs(TPQD_DMAX, "is_exception() before start_dma()!");
+	    }
+	    
 /******************************************************************
  ***** if start_dma() fails because the head is positioned 0 bytes
  ***** before the FM, (causing EXCEPTION to be set) return_read_eof should
  ***** be set to YES, and we should return total_bytes_done, rather than -ENXIO.
  ***** The app should recognize this as an EOF condition.
  ***************************************************************************/
-			stat = start_dma(READ, bytes_todo);
-			if (stat == TE_OK) {
-				/* Wait for transfer to complete, interrupt should wake us */
-				while (dma_mode != 0) {
-					sleep_on(&qic02_tape_transfer);
-				}
-				if (status_error)
-					return_read_eof = YES;
-			} else if (stat != TE_END) {
-				/* should do sense() on error here */
+	    stat = start_dma(READ, bytes_todo);
+	    if (stat == TE_OK)
+	    {
+		/* Wait for transfer to complete, interrupt should wake us */
+		while (dma_mode != 0)
+		{
+		    sleep_on(&qic02_tape_transfer);
+		}
+		if (status_error)
+		{
+		    return_read_eof = YES;
+		}
+		
+	    }
+	    else if (stat != TE_END)
+	    {
+		/* should do sense() on error here */
 #if 0
-				return -ENXIO;
+		return -ENXIO;
 #else
-				printk("Trouble: stat==%02x\n", stat);
-				return_read_eof = YES;
-				/*************** check EOF/EOT handling!!!!!! **/
-#endif
-			}
-			end_dma(&bytes_done);
-			if (bytes_done>bytes_todo) {
-				tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested");
-				return -EIO;
-			}
-			/* copy buffer to user-space in one go */
-			if (bytes_done>0)
-				if (copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), 
-						bytes_done))
-					return -EFAULT;
-
-#if 1
-			/* Checks Ton's patch below */
-			if ((return_read_eof == NO) && (status_eof_detected == YES)) {
-				printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
-			}
-#endif
-			if ((bytes_todo != bytes_done) || (status_eof_detected == YES))
-				/* EOF or EOM detected. return EOF next time. */
-				return_read_eof = YES;
-		} /* else: ignore read request for 0 bytes */
-
-		if (bytes_done>0) {
-			status_bytes_rd = YES;
-			buf += bytes_done;
-			filp->f_pos += bytes_done;
-			total_bytes_done += bytes_done;
-			count -= bytes_done;
+		printk("Trouble: stat==%02x\n", stat);
+		return_read_eof = YES;
+		/*************** check EOF/EOT handling!!!!!! **/
+#endif
+	    }
+	    end_dma(&bytes_done);
+	    if (bytes_done>bytes_todo)
+	    {
+		tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested");
+		return -EIO;
+	    }
+	    /* copy buffer to user-space in one go */
+	    if (bytes_done>0)
+	    {
+		err = copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), bytes_done);
+		if (err)
+		{
+		    return -EFAULT;
 		}
-	}
-	tpqputs(TPQD_ALWAYS, "read request for <0 bytes");
-	return -EINVAL;
+	    }
+#if 1
+	    /* Checks Ton's patch below */
+	    if ((return_read_eof == NO) && (status_eof_detected == YES))
+	    {
+		printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
+	    }
+#endif
+	    if ((bytes_todo != bytes_done) || (status_eof_detected == YES))
+	    {
+		/* EOF or EOM detected. return EOF next time. */
+		return_read_eof = YES;
+	    }
+	    
+	} /* else: ignore read request for 0 bytes */
+
+	if (bytes_done>0)
+	{
+	    status_bytes_rd = YES;
+	    buf += bytes_done;
+	    filp->f_pos += bytes_done;
+	    total_bytes_done += bytes_done;
+	    count -= bytes_done;
+	}
+    }
+    tpqputs(TPQD_ALWAYS, "read request for <0 bytes");
+    return -EINVAL;
 } /* qic02_tape_read */
 
 
@@ -2102,77 +1964,98 @@
  * tape device again. The driver will detect an exception status in (No Cartridge)
  * and force a rewind. After that tar may continue writing.
  */
-static long qic02_tape_write(struct inode * inode, struct file * filp,
-	const char * buf, unsigned long count)
+static long qic02_tape_write(struct inode * inode, struct file * filp, 
+			     const char * buf, unsigned long count)
 {
-	kdev_t dev = inode->i_rdev;
-	unsigned short flags = filp->f_flags;
-	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
-
-	if (status_zombie==YES) {
-		tpqputs(TPQD_ALWAYS, "configs not set");		
-		return -ENXIO;
-	}
-
-	if (TP_DIAGS(current_tape_dev))
-		/* can't print a ``long long'' (for filp->f_pos), so chop it */
-		printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n",
-			MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags);
+    int err;
+    kdev_t dev = inode->i_rdev;
+    unsigned short flags = filp->f_flags;
+    unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
+    
+    if (status_zombie==YES)
+    {
+	tpqputs(TPQD_ALWAYS, "configs not set");		
+	return -ENXIO;
+    }
+
+    if (TP_DIAGS(current_tape_dev))
+    {
+	/* can't print a ``long long'' (for filp->f_pos), so chop it */
+	printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n",
+	       MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags);
+    }
+    
+    if (count % TAPE_BLKSIZE) 	/* only allow mod 512 bytes at a time */
+    {
+	tpqputs(TPQD_BLKSZ, "Wrong block size");
+	return -EINVAL;
+    }
 
-	if (count % TAPE_BLKSIZE) {	/* only allow mod 512 bytes at a time */
-		tpqputs(TPQD_BLKSZ, "Wrong block size");
-		return -EINVAL;
+    if (mode_access==READ)
+    {
+	tpqputs(TPQD_ALWAYS, "Not in write mode");
+	return -EACCES;
+    }
+
+    /* open() does a sense() and we can assume the tape isn't changed
+     * between open() and release(), so the tperror.exs bits will still
+     * be valid.
+     */
+    if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP))
+    {
+	tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
+	return -EACCES;	/* don't even try when write protected */
+    }
+      
+    if (doing_read == YES)
+    {
+	terminate_read(0);
+    }
+    
+    while ((signed)count>=0)
+    {
+	/* see how much fits in the kernel buffer */
+	bytes_done = 0;
+	bytes_todo = TPQBUF_SIZE;
+	if (bytes_todo>count)
+	{
+	    bytes_todo = count;
 	}
-
-	if (mode_access==READ) {
-		tpqputs(TPQD_ALWAYS, "Not in write mode");
-		return -EACCES;
+	
+	if (return_write_eof == YES)
+	{
+	    /* return_write_eof should be reset on reverse tape movements. */
+	    
+	    if (reported_write_eof==NO)
+	    {
+		if (bytes_todo>0)
+		{
+		    tpqputs(TPQD_ALWAYS, "partial write");
+		    /* partial write signals EOF to user program */
+		}
+		reported_write_eof = YES;
+		return total_bytes_done;
+	    }
+	    else
+	    {
+		return -ENOSPC;		 /* return error */
+	    }	
 	}
-
-	/* open() does a sense() and we can assume the tape isn't changed
-	 * between open() and release(), so the tperror.exs bits will still
-	 * be valid.
-	 */
-	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
-		tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
-		return -EACCES;	/* don't even try when write protected */
+	
+	/* Quit when done. */
+	if (bytes_todo==0)
+	{
+	    return total_bytes_done;
 	}
-
-	if (doing_read == YES)
-		terminate_read(0);
-
-	while ((signed)count>=0) {
-		/* see how much fits in the kernel buffer */
-		bytes_done = 0;
-		bytes_todo = TPQBUF_SIZE;
-		if (bytes_todo>count)
-			bytes_todo = count;
 	
-		if (return_write_eof == YES) {
-			/* return_write_eof should be reset on reverse tape movements. */
-
-			if (reported_write_eof==NO) {
-				if (bytes_todo>0) {
-					tpqputs(TPQD_ALWAYS, "partial write");
-					/* partial write signals EOF to user program */
-				}
-				reported_write_eof = YES;
-				return total_bytes_done;
-			} else {
-				return -ENOSPC;		 /* return error */
-			}	
-		}
-
-		/* Quit when done. */
-		if (bytes_todo==0)
-			return total_bytes_done;
-
-
-		/* copy from user to DMA buffer and initiate transfer. */
-		if (bytes_todo>0) {
-			if (copy_from_user( (void *) bus_to_virt(buffaddr), 
-					(const void *) buf, bytes_todo))
-				return -EFAULT;
+	/* copy from user to DMA buffer and initiate transfer. */
+	if (bytes_todo>0)
+	{
+	    err = copy_from_user( (void *) bus_to_virt(buffaddr), (const void *) buf, bytes_todo);
+	    if (err)
+	    {
+		return -EFAULT;
+	    }
 
 /****************** similar problem with read() at FM could happen here at EOT.
  ******************/
@@ -2180,62 +2063,75 @@
 /***** if at EOT, 0 bytes can be written. start_dma() will
  ***** fail and write() will return ENXIO error
  *****/
-			if (start_dma(WRITE, bytes_todo) != TE_OK) {
-				tpqputs(TPQD_ALWAYS, "write: start_dma() failed");
-				/* should do sense() on error here */
-				return -ENXIO;	/*********** FIXTHIS **************/
-			}
-
-			/* Wait for write to complete, interrupt should wake us. */
-			while ((status_error == 0) && (dma_mode != 0)) {
-				sleep_on(&qic02_tape_transfer);
-			}
-
-			end_dma(&bytes_done);
-			if (bytes_done>bytes_todo) {
-				tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested");
-				return -EIO;
-			}
-			/* If the dma-transfer was aborted because of an exception,
-			 * status_error will have been set in the interrupt handler.
-			 * Then end_dma() will do a sense().
-			 * If the exception was EXC_EOM, the EW-hole was encountered
-			 * and two more blocks could be written. For the time being we'll
-			 * just consider this to be the EOT.
-			 * Otherwise, something Bad happened, such as the maximum number
-			 * of block-rewrites was exceeded. [e.g. A very bad spot on tape was
-			 * encountered. Normally short dropouts are compensated for by
-			 * rewriting the block in error, up to 16 times. I'm not sure
-			 * QIC-24 drives can do this.]
-			 */
-			if (status_error) {
-				if (status_eom_detected == YES)	{
-					tpqputs(TPQD_ALWAYS, "write: EW detected");
-					return_write_eof = YES;
-				} else {
-					/* probably EXC_RWA */
-					tpqputs(TPQD_ALWAYS, "write: dma: error in writing");
-					return -EIO;
-				}
-			}
-			if (bytes_todo != bytes_done)
-				/* EOF or EOM detected. return EOT next time. */
-				return_write_eof = YES;
-		}
-		/* else: ignore write request for 0 bytes. */
-
-		if (bytes_done>0) {
-			status_bytes_wr = YES;
-			buf += bytes_done;
-			filp->f_pos += bytes_done;
-			total_bytes_done += bytes_done;
-			count -= bytes_done;
-		}
-	}
-	tpqputs(TPQD_ALWAYS, "write request for <0 bytes");
-	if (TPQDBG(DEBUG))
-		printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %lx\n", status_bytes_wr, buf, total_bytes_done, count);
-	return -EINVAL;
+	    if (start_dma(WRITE, bytes_todo) != TE_OK)
+	    {
+		tpqputs(TPQD_ALWAYS, "write: start_dma() failed");
+		/* should do sense() on error here */
+		return -ENXIO;	/*********** FIXTHIS **************/
+	    }
+
+	    /* Wait for write to complete, interrupt should wake us. */
+	    while ((status_error == 0) && (dma_mode != 0))
+	    {
+		sleep_on(&qic02_tape_transfer);
+	    }
+
+	    end_dma(&bytes_done);
+	    if (bytes_done>bytes_todo)
+	    {
+		tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested");
+		return -EIO;
+	    }
+	    /* If the dma-transfer was aborted because of an exception,
+	     * status_error will have been set in the interrupt handler.
+	     * Then end_dma() will do a sense().
+	     * If the exception was EXC_EOM, the EW-hole was encountered
+	     * and two more blocks could be written. For the time being we'll
+	     * just consider this to be the EOT.
+	     * Otherwise, something Bad happened, such as the maximum number
+	     * of block-rewrites was exceeded. [e.g. A very bad spot on tape was
+	     * encountered. Normally short dropouts are compensated for by
+	     * rewriting the block in error, up to 16 times. I'm not sure
+	     * QIC-24 drives can do this.]
+	     */
+	    if (status_error)
+	    {
+		if (status_eom_detected == YES)
+		{
+		    tpqputs(TPQD_ALWAYS, "write: EW detected");
+		    return_write_eof = YES;
+		}
+		else
+		{
+		    /* probably EXC_RWA */
+		    tpqputs(TPQD_ALWAYS, "write: dma: error in writing");
+		    return -EIO;
+		}
+	    }
+	    if (bytes_todo != bytes_done)
+	    {
+		/* EOF or EOM detected. return EOT next time. */
+		return_write_eof = YES;
+	    }
+	}
+	/* else: ignore write request for 0 bytes. */
+	
+	if (bytes_done>0)
+	{
+	    status_bytes_wr = YES;
+	    buf += bytes_done;
+	    filp->f_pos += bytes_done;
+	    total_bytes_done += bytes_done;
+	    count -= bytes_done;
+	}
+    }
+    
+    tpqputs(TPQD_ALWAYS, "write request for <0 bytes");
+    if (TPQDBG(DEBUG))
+    {
+	printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %lx\n", status_bytes_wr, buf, total_bytes_done, count);
+    }
+    return -EINVAL;
 } /* qic02_tape_write */
 
 
@@ -2251,344 +2147,388 @@
  * remembered values, rewind the tape and set the required density.
  * Don't rewind if the minor bits specify density 0.
  */
+
 static int qic02_tape_open(struct inode * inode, struct file * filp)
 {
-	kdev_t dev = inode->i_rdev;
-	unsigned short flags = filp->f_flags;
-	unsigned short dens = 0;
-	int s;
+    static int qic02_tape_open_no_use_count(struct inode *, struct file *);
+    int open_error;
 
+    open_error = qic02_tape_open_no_use_count(inode, filp);
+    if (!open_error)
+    {
+	MOD_INC_USE_COUNT;
+    }
+    return open_error;
+}
 
-	if (TP_DIAGS(dev)) {
-		printk("qic02_tape_open: dev=%s, flags=%x     ",
-		       kdevname(dev), flags);
+static int qic02_tape_open_no_use_count(struct inode * inode, struct file * filp)
+{
+    kdev_t dev = inode->i_rdev;
+    unsigned short flags = filp->f_flags;
+    unsigned short dens = 0;
+    int s;
+
+
+    if (TP_DIAGS(dev))
+    {
+	printk("qic02_tape_open: dev=%s, flags=%x     ",
+	       kdevname(dev), flags);
+    }
+
+    if (MINOR(dev)==255)	/* special case for resetting */
+    {
+	if (suser())
+	{
+	    return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO;
 	}
-
-#ifdef MODULE
-       MOD_INC_USE_COUNT;
-#endif
-
-	if (MINOR(dev)==255) {	/* special case for resetting */
-#ifdef MODULE
-                MOD_DEC_USE_COUNT;
-#endif
-		if (suser())
-			return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO;
-		else
-			return -EPERM;
+	else
+	{
+	    return -EPERM;
 	}
-
-	if (status_dead==YES)
-		/* Allow `mt reset' ioctl() even when already open()ed. */
-		return 0;
-
+    }
+    
+    if (status_dead==YES)
+    {
+	/* Allow `mt reset' ioctl() even when already open()ed. */
+	return 0;
+    }
+    
 	/* Only one at a time from here on... */
-	if (filp->f_count>1) {	/* filp->f_count==1 for the first open() */
-#ifdef MODULE
-                MOD_DEC_USE_COUNT;
-#endif
-		return -EBUSY;
-	}
-
-	if (status_zombie==YES)
-		/* no irq/dma/port stuff allocated yet, no reset done
-		 * yet, so return until MTSETCONFIG has been done.
-		 */
-		return 0;
-
-	status_bytes_rd = NO;
-	status_bytes_wr = NO;
-
-	return_read_eof = NO;	/********????????????????*****/
-	return_write_eof = (status_eot_detected)? YES : NO;
-
-	/* Clear this in case user app close()d before reading EOF token */
-	status_eof_detected = NO;
-
-	reported_read_eof = NO;
-	reported_write_eof = NO;
-
-
-	switch (flags & O_ACCMODE) {
-		case O_RDONLY:
-			mode_access = READ;
-			break;
-		case O_WRONLY:	/* Fallthru... Strictly speaking this is not correct... */
-		case O_RDWR:	/* Reads are allowed as long as nothing is written */
-			mode_access = WRITE;
-			break;
-	}
-
-	/* This is to avoid tape-changed problems (TP_CNI exception).
-	 *
-	 * Since removing the cartridge will not raise an exception,
-	 * we always do a tp_sense() to make sure we have the proper
-	 * CNI status, the 2150L may need an additional sense.... - Eddy
-	 */
-	s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
-
-	if (s == TE_OK)
-		/* Try to clear cartridge-changed status for Archive-2150L */
-		if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI))
-			s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
-
-	if (s != TE_OK) {
-		tpqputs(TPQD_ALWAYS, "open: sense() failed");
-#ifdef MODULE
-                MOD_DEC_USE_COUNT;
-#endif
-		return -EIO;
-	}
-
-	/* exception bits should be up-to-date now, so check for
-	 * tape presence and exit if absent.
-	 * Even `mt stat' will fail without a tape.
+    if (filp->f_count>1) 	/* filp->f_count==1 for the first open() */
+    {
+	return -EBUSY;
+    }
+
+    if (status_zombie==YES)
+    {
+	/* no irq/dma/port stuff allocated yet, no reset done
+	 * yet, so return until MTSETCONFIG has been done.
 	 */
-	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) {
-		tpqputs(TPQD_ALWAYS, "No tape present.");
-#ifdef MODULE
-                MOD_DEC_USE_COUNT;
-#endif
-		return -EIO;
+	return 0;
+    }
+    
+    status_bytes_rd = NO;
+    status_bytes_wr = NO;
+    
+    return_read_eof = NO;	/********????????????????*****/
+    return_write_eof = (status_eot_detected)? YES : NO;
+
+    /* Clear this in case user app close()d before reading EOF token */
+    status_eof_detected = NO;
+
+    reported_read_eof = NO;
+    reported_write_eof = NO;
+
+
+    switch (flags & O_ACCMODE)
+    {
+     case O_RDONLY:
+	mode_access = READ;
+	break;
+     case O_WRONLY:	/* Fallthru... Strictly speaking this is not correct... */
+     case O_RDWR:	/* Reads are allowed as long as nothing is written */
+	mode_access = WRITE;
+	break;
+    }
+    
+    /* This is to avoid tape-changed problems (TP_CNI exception).
+     *
+     * Since removing the cartridge will not raise an exception,
+     * we always do a tp_sense() to make sure we have the proper
+     * CNI status, the 2150L may need an additional sense.... - Eddy
+     */
+    s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
+    
+    if (s == TE_OK)
+    {
+	/* Try to clear cartridge-changed status for Archive-2150L */
+	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI))
+	{
+	    s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
 	}
-
-	/* At this point we can assume that a tape is present and
-	 * that it will remain present until release() is called.
+    }
+    
+    if (s != TE_OK)
+    {
+	tpqputs(TPQD_ALWAYS, "open: sense() failed");
+	return -EIO;
+    }
+
+    /* exception bits should be up-to-date now, so check for
+     * tape presence and exit if absent.
+     * Even `mt stat' will fail without a tape.
+     */
+    if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI))
+    {
+	tpqputs(TPQD_ALWAYS, "No tape present.");
+	return -EIO;
+    }
+
+    /* At this point we can assume that a tape is present and
+     * that it will remain present until release() is called.
+     */
+
+    /* not allowed to do QCMD_DENS_* unless tape is rewound */
+    if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev)))
+    {
+	/* force rewind if minor bits have changed,
+	 * i.e. user wants to use tape in different format.
+	 * [assuming single drive operation]
 	 */
-
-	/* not allowed to do QCMD_DENS_* unless tape is rewound */
-	if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) {
-		/* force rewind if minor bits have changed,
-		 * i.e. user wants to use tape in different format.
-		 * [assuming single drive operation]
-		 */
-		if (TP_HAVE_DENS) {
-			tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind.");
-			need_rewind = YES;
-		}
-	} else {
-		/* density bits still the same, but TP_DIAGS bit 
-		 * may have changed.
-		 */
-		current_tape_dev = dev;
+	if (TP_HAVE_DENS)
+	{
+	    tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind.");
+	    need_rewind = YES;
 	}
-
-	if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/
-		s = do_qic_cmd(QCMD_REWIND, TIM_R);
-		if (s != 0) {
-			tpqputs(TPQD_ALWAYS, "open: rewind failed");
-#ifdef MODULE
-			MOD_DEC_USE_COUNT;
-#endif
-			return -EIO;
-		}
+    }
+    else
+    {
+	/* density bits still the same, but TP_DIAGS bit 
+	 * may have changed.
+	 */
+	current_tape_dev = dev;
+    }
+    
+    if (need_rewind == YES)  /***************** CHECK THIS!!!!!!!! **********/
+    {
+	s = do_qic_cmd(QCMD_REWIND, TIM_R);
+	if (s != 0)
+	{
+	    tpqputs(TPQD_ALWAYS, "open: rewind failed");
+	    return -EIO;
 	}
+    }
 
 
 /* Note: After a reset command, the controller will rewind the tape
  *	 just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!!
  */ 
-	if (status_dead==YES) {
-		tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset");
-		if (tape_reset(1)!=TE_OK) {
-#ifdef MODULE
-			MOD_DEC_USE_COUNT;
-#endif
-			return -ENXIO;
-		} else {
-			status_dead = NO;
-			if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) {
-				tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n");
-				status_dead = YES;	/* try reset next time */
-#ifdef MODULE
-				MOD_DEC_USE_COUNT;
-#endif
-				return -EIO;
-			}
-		}
+    if (status_dead==YES)
+    {
+	tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset");
+	if (tape_reset(1)!=TE_OK)
+	{
+	    return -ENXIO;
 	}
-
-	/* things should be ok, once we get here */
-
-
-	/* set density: only allowed when TP_BOM status bit is set,
-	 * so we must have done a rewind by now. If not, just skip over.
-	 * Only give set density command when minor bits have changed.
-	 */
-	if (TP_DENS(current_tape_dev) == TP_DENS(dev) )
-		return 0;
-
-	current_tape_dev = dev;
-	need_rewind = NO;
-	if (TP_HAVE_DENS)
-		dens = TP_DENS(dev);
-
-	if (dens < sizeof(format_names)/sizeof(char *))
-		printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
 	else
-		tpqputs(TPQD_REWIND, "Wait for retensioning...");
-
-	switch (TP_DENS(dev)) {
-		case 0: /* Minor 0 is for drives without set-density support */
-			s = 0;
-			break;
-		case 1:
-			s = do_qic_cmd(QCMD_DENS_11, TIM_S);
-			break;
-		case 2:
-			s = do_qic_cmd(QCMD_DENS_24, TIM_S);
-			break;
-		case 3:
-			s = do_qic_cmd(QCMD_DENS_120, TIM_S);
-			break;
-		case 4:
-			s = do_qic_cmd(QCMD_DENS_150, TIM_S);
-			break;
-		case 5:
-			s = do_qic_cmd(QCMD_DENS_300, TIM_S);
-			break;
-		case 6:
-			s = do_qic_cmd(QCMD_DENS_600, TIM_S);
-			break;
-		default:  /* otherwise do a retension before anything else */
-			s = do_qic_cmd(QCMD_RETEN, TIM_R);
-	}
-	if (s != 0) {
-		status_dead = YES;	/* force reset */
-		current_tape_dev = 0; /* earlier 0xff80 */
-#ifdef MODULE
-		MOD_DEC_USE_COUNT;
-#endif
+	{
+	    status_dead = NO;
+	    if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK)
+	    {
+		tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n");
+		status_dead = YES;	/* try reset next time */
 		return -EIO;
+	    }
 	}
-
+    }
+    
+    /* things should be ok, once we get here */
+    
+
+    /* set density: only allowed when TP_BOM status bit is set,
+     * so we must have done a rewind by now. If not, just skip over.
+     * Only give set density command when minor bits have changed.
+     */
+    if (TP_DENS(current_tape_dev) == TP_DENS(dev) )
+    {
 	return 0;
+    }
+    
+    current_tape_dev = dev;
+    need_rewind = NO;
+    if (TP_HAVE_DENS)
+    {
+	dens = TP_DENS(dev);
+    }
+    
+    if (dens < sizeof(format_names)/sizeof(char *))
+    {
+	printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
+    }
+    else
+    {
+	tpqputs(TPQD_REWIND, "Wait for retensioning...");
+    }
+    
+    switch (TP_DENS(dev))
+    {
+     case 0: /* Minor 0 is for drives without set-density support */
+	s = 0;
+	break;
+     case 1:
+	s = do_qic_cmd(QCMD_DENS_11, TIM_S);
+	break;
+     case 2:
+	s = do_qic_cmd(QCMD_DENS_24, TIM_S);
+	break;
+     case 3:
+	s = do_qic_cmd(QCMD_DENS_120, TIM_S);
+	break;
+     case 4:
+	s = do_qic_cmd(QCMD_DENS_150, TIM_S);
+	break;
+     case 5:
+	s = do_qic_cmd(QCMD_DENS_300, TIM_S);
+	break;
+     case 6:
+	s = do_qic_cmd(QCMD_DENS_600, TIM_S);
+	break;
+     default:  /* otherwise do a retension before anything else */
+	s = do_qic_cmd(QCMD_RETEN, TIM_R);
+    }
+    if (s != 0)
+    {
+	status_dead = YES;	/* force reset */
+	current_tape_dev = 0; /* earlier 0xff80 */
+	return -EIO;
+    }
+    
+    return 0;
 } /* qic02_tape_open */
 
 
 static void qic02_tape_release(struct inode * inode, struct file * filp)
 {
-	kdev_t dev = inode->i_rdev;
-
-	if (TP_DIAGS(dev))
-		printk("qic02_tape_release: dev=%s\n",
-		       kdevname(dev));
-
-	if (status_zombie==YES)		/* don't rewind in zombie mode */
-#ifdef MODULE
-		MOD_DEC_USE_COUNT;
-#endif
-		return;
+    kdev_t dev = inode->i_rdev;
 
+    if (TP_DIAGS(dev))
+    {
+	printk("qic02_tape_release: dev=%s\n",  kdevname(dev));
+    }
+    
+    if (status_zombie==NO)		/* don't rewind in zombie mode */
+    {
 	/* Terminate any pending write cycle. Terminating the read-cycle
 	 * is delayed until it is required to do so for a new command.
 	 */
 	terminate_write(-1);
-
+	
 	if (status_dead==YES)
-		tpqputs(TPQD_ALWAYS, "release: device dead!?");
-
- 	/* Rewind only if minor number requires it AND 
+	{
+	    tpqputs(TPQD_ALWAYS, "release: device dead!?");
+	}
+	
+	/* Rewind only if minor number requires it AND 
 	 * read/writes have been done. ************* IS THIS CORRECT??????????
 	 */
-	if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) {
-		tpqputs(TPQD_REWIND, "release: Doing rewind...");
-		(void) do_qic_cmd(QCMD_REWIND, TIM_R);
+	if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr))
+	{
+	    tpqputs(TPQD_REWIND, "release: Doing rewind...");
+	    (void) do_qic_cmd(QCMD_REWIND, TIM_R);
 	}
+    }
 #ifdef MODULE
-	MOD_DEC_USE_COUNT;
+    MOD_DEC_USE_COUNT;
 #endif
-	return;
+    return;
 } /* qic02_tape_release */
 
 
 #ifdef CONFIG_QIC02_DYNCONF
 /* Set masks etc. based on the interface card type. */
-int update_ifc_masks(int ifc)
+static int update_ifc_masks(int ifc)
 {
-	QIC02_TAPE_IFC = ifc;
-
-	if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) {
-		QIC02_STAT_PORT = QIC02_TAPE_PORT;
-		QIC02_CTL_PORT = QIC02_TAPE_PORT;
-		QIC02_CMD_PORT = QIC02_TAPE_PORT+1;
-		QIC02_DATA_PORT = QIC02_TAPE_PORT+1;
-		QIC02_STAT_READY = WT_QIC02_STAT_READY;
-		QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION;
-		QIC02_STAT_MASK = WT_QIC02_STAT_MASK;
-
-		QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK;
-		QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL;
-
-		QIC02_CTL_RESET = WT_QIC02_CTL_RESET;
-		QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST;
- 
-		if (QIC02_TAPE_DMA == 3)
-			WT_CTL_DMA = WT_CTL_DMA3;
-		else if (QIC02_TAPE_DMA == 1)
-			WT_CTL_DMA = WT_CTL_DMA1;
-		else {
-			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
-			return -EIO;
-		} 
+    QIC02_TAPE_IFC = ifc;
 
-		if (QIC02_TAPE_IFC == EVEREX) {
-			/* Everex is a special case for Wangtek (actually
-			 * it's the other way 'round, but I saw Wangtek first)
-			 */
-			if (QIC02_TAPE_DMA==3)
-				WT_CTL_DMA = WT_CTL_DMA1;
-			/* Fixup the kernel copy of the IFC type to that
-			 * we don't have to distinguish between Wangtek and
-			 * and Everex at runtime.
-			 */
-			QIC02_TAPE_IFC = WANGTEK;
-		}
-	} else if (QIC02_TAPE_IFC == ARCHIVE) {
-		QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
-		QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
-		QIC02_CMD_PORT = QIC02_TAPE_PORT;
-		QIC02_DATA_PORT = QIC02_TAPE_PORT;
-		QIC02_STAT_READY = AR_QIC02_STAT_READY;
-		QIC02_STAT_EXCEPTION  = AR_QIC02_STAT_EXCEPTION;
-		QIC02_STAT_MASK = AR_QIC02_STAT_MASK;
-
-		QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK;
-		QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL;
-
-		QIC02_CTL_RESET = AR_QIC02_CTL_RESET;
-		QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST;
- 
-		if (QIC02_TAPE_DMA > 3) {
-			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
-			return -EIO;
-		} 
-	} else if (QIC02_TAPE_IFC == MOUNTAIN) {
-		QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
-		QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
-		QIC02_CMD_PORT = QIC02_TAPE_PORT;
-		QIC02_DATA_PORT = QIC02_TAPE_PORT;
-
-		QIC02_STAT_READY = MTN_QIC02_STAT_READY;
-		QIC02_STAT_EXCEPTION  = MTN_QIC02_STAT_EXCEPTION;
-		QIC02_STAT_MASK = MTN_QIC02_STAT_MASK;
-
-		QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK;
-		QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL;
-
-		QIC02_CTL_RESET = MTN_QIC02_CTL_RESET;
-		QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST;
+    if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX))
+    {
+	QIC02_STAT_PORT = QIC02_TAPE_PORT;
+	QIC02_CTL_PORT = QIC02_TAPE_PORT;
+	QIC02_CMD_PORT = QIC02_TAPE_PORT+1;
+	QIC02_DATA_PORT = QIC02_TAPE_PORT+1;
+	QIC02_STAT_READY = WT_QIC02_STAT_READY;
+	QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION;
+	QIC02_STAT_MASK = WT_QIC02_STAT_MASK;
+	
+	QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK;
+	QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL;
+	
+	QIC02_CTL_RESET = WT_QIC02_CTL_RESET;
+	QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST;
  
-		if (QIC02_TAPE_DMA > 3) {
-			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
-			return -EIO;
-		} 
-	} else {
-		tpqputs(TPQD_ALWAYS, "Invalid interface type");
-		return -ENXIO;
+	if (QIC02_TAPE_DMA == 3)
+	{
+	    WT_CTL_DMA = WT_CTL_DMA3;
 	}
-	return 0;
-} /* update_ifc-masks */
+	else if (QIC02_TAPE_DMA == 1)
+	{
+	    WT_CTL_DMA = WT_CTL_DMA1;
+	}
+	else
+	{
+	    tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+	    return -EIO;
+	} 
+
+	if (QIC02_TAPE_IFC == EVEREX)
+	{
+	    /* Everex is a special case for Wangtek (actually
+	     * it's the other way 'round, but I saw Wangtek first)
+	     */
+	    if (QIC02_TAPE_DMA==3)
+	    {
+		WT_CTL_DMA = WT_CTL_DMA1;
+	    }
+	    
+	    /* Fixup the kernel copy of the IFC type to that
+	     * we don't have to distinguish between Wangtek and
+	     * and Everex at runtime.
+	     */
+	    QIC02_TAPE_IFC = WANGTEK;
+	}
+    }
+    else if (QIC02_TAPE_IFC == ARCHIVE)
+    {
+	QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
+	QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
+	QIC02_CMD_PORT = QIC02_TAPE_PORT;
+	QIC02_DATA_PORT = QIC02_TAPE_PORT;
+	QIC02_STAT_READY = AR_QIC02_STAT_READY;
+	QIC02_STAT_EXCEPTION  = AR_QIC02_STAT_EXCEPTION;
+	QIC02_STAT_MASK = AR_QIC02_STAT_MASK;
+	
+	QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK;
+	QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL;
+	
+	QIC02_CTL_RESET = AR_QIC02_CTL_RESET;
+	QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST;
+	
+	if (QIC02_TAPE_DMA > 3)
+	{
+	    tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+	    return -EIO;
+	} 
+    }
+    else if (QIC02_TAPE_IFC == MOUNTAIN)
+    {
+	QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
+	QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
+	QIC02_CMD_PORT = QIC02_TAPE_PORT;
+	QIC02_DATA_PORT = QIC02_TAPE_PORT;
+	
+	QIC02_STAT_READY = MTN_QIC02_STAT_READY;
+	QIC02_STAT_EXCEPTION  = MTN_QIC02_STAT_EXCEPTION;
+	QIC02_STAT_MASK = MTN_QIC02_STAT_MASK;
+	
+	QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK;
+	QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL;
+	
+	QIC02_CTL_RESET = MTN_QIC02_CTL_RESET;
+	QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST;
+	
+	if (QIC02_TAPE_DMA > 3)
+	{
+	    tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+	    return -EIO;
+	} 
+    }
+    else
+    {
+	tpqputs(TPQD_ALWAYS, "Invalid interface type");
+	return -ENXIO;
+    }
+    return qic02_get_resources();
+} /* update_ifc_masks */
 #endif
 
 
@@ -2596,209 +2536,207 @@
 static int qic02_tape_ioctl(struct inode * inode, struct file * filp, 
 		     unsigned int iocmd, unsigned long ioarg)
 {
-	int error;
-	int dev_maj = MAJOR(inode->i_rdev);
-	int c;
-	struct mtop operation;
-	char *stp, *argp;
-	unsigned char blk_addr[6];
-	struct mtpos ioctl_tell;
+    int error;
+    int dev_maj = MAJOR(inode->i_rdev);
+    int c;
+    struct mtop operation;
+    unsigned char blk_addr[6];
+    struct mtpos ioctl_tell;
+    
+
+    if (TP_DIAGS(current_tape_dev))
+    {
+	printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
+    }
+    
+    if (!inode || !ioarg)
+    {
+	return -EINVAL;
+    }
+    
+    /* check iocmd first */
+
+    if (dev_maj != QIC02_TAPE_MAJOR)
+    {
+	printk(TPQIC02_NAME ": Oops! Wrong device?\n");
+	/* A panic() would be appropriate here */
+	return -ENODEV;
+    }
 
+    c = _IOC_NR(iocmd);
 
-	if (TP_DIAGS(current_tape_dev))
-		printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
+#ifdef CONFIG_QIC02_DYNCONF
+    if (c == _IOC_NR(MTIOCGETCONFIG))
+    {
+	CHECK_IOC_SIZE(mtconfiginfo);
 
-	if (!inode || !ioarg)
-		return -EINVAL;
+	if (copy_to_user((char *) ioarg, (char *) &qic02_tape_dynconf, sizeof(qic02_tape_dynconf)))
+	{
+	    return -EFAULT;
+	}
+	return 0;
 
-	/* check iocmd first */
+    }
+    else if (c == _IOC_NR(MTIOCSETCONFIG))
+    {
+	/* One should always do a MTIOCGETCONFIG first, then update
+	 * user-settings, then write back with MTIOCSETCONFIG.
+	 * The qic02conf program should re-open() the device before actual
+	 * use, to make sure everything is initialized.
+	 */
+	
+	CHECK_IOC_SIZE(mtconfiginfo);
+	
+	if (!suser())
+	{
+	    return -EPERM;
+	}
+	
+	if ((doing_read!=NO) || (doing_write!=NO))
+	{
+	    return -EBUSY;
+	}
+	
+	if (status_zombie==NO)
+	{
+	    qic02_release_resources();	/* and go zombie */
+	}
+	
+	/* copy struct from user space to kernel space */
+	if (copy_from_user((char *) &qic02_tape_dynconf, (char *) ioarg, sizeof(qic02_tape_dynconf)))
+	{
+	    return -EFAULT;
+	}
+	return update_ifc_masks(qic02_tape_dynconf.ifc_type);
+    }
+    if (status_zombie==YES)
+    {
+	tpqputs(TPQD_ALWAYS, "Configs not set");
+	return -ENXIO;
+    }
+#endif
+    if (c == _IOC_NR(MTIOCTOP))
+    {
+	CHECK_IOC_SIZE(mtop);
 
-	if (dev_maj != QIC02_TAPE_MAJOR) {
-		printk(TPQIC02_NAME ": Oops! Wrong device?\n");
-		/* A panic() would be appropriate here */
-		return -ENODEV;
-	}
-
-	c = iocmd & IOCCMD_MASK;
-
-#ifdef DDIOCSDBG
-	/* Check for DDI Debug Control, contributed by FvK, edited by HHB. */
-	if (c == DDIOCSDBG) {
-		if (!suser())
-			return -EPERM;
-		error = get_user(c, (int *) ioarg);
-		if (error)
-			return error;
-		if (c==0) {
-			QIC02_TAPE_DEBUG = 0;
-			return 0;
-		}
-		if ((c>=1) && (c<=32)) {
-			QIC02_TAPE_DEBUG |= (1 << (c-1));
-			return 0;
-		}
-		if (c >= 128) {
-			QIC02_TAPE_DEBUG &= ~(1 << (c - 128));
-			return 0;
-		}
-		return -EINVAL;
+	/* copy mtop struct from user space to kernel space */
+	if (copy_from_user((char *) &operation, (char *) ioarg, sizeof(operation)))
+	{
+	    return -EFAULT;
 	}
-#endif
 
-#ifdef CONFIG_QIC02_DYNCONF
-	if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) {
-		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
-			tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
-			return -EFAULT;
-		}
-
-		/* check for valid user address and copy current settings to user space */
-		stp = (char *) &qic02_tape_dynconf;
-		argp = (char *) ioarg;
-		if (copy_to_user(stp, argp, sizeof(qic02_tape_dynconf)))
-			return -EFAULT;
-		return 0;
-
-	} else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) {
-		static int qic02_get_resources(void), qic02_release_resources(void);
-
-		/* One should always do a MTIOCGETCONFIG first, then update
-		 * user-settings, then write back with MTIOCSETCONFIG.
-		 * Re-open() the device before actual use to make sure
-		 * everything is initialized.
-		 */
-		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
-			tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
-			return -EFAULT;
-		}
-		if (!suser())
-			return -EPERM;
-		if ((doing_read!=NO) || (doing_write!=NO))
-			return -EBUSY;
-
-		/* copy struct from user space to kernel space */
-		stp = (char *) &qic02_tape_dynconf;
-		argp = (char *) ioarg;
-		if (copy_from_user(stp, argp, sizeof(qic02_tape_dynconf)))
-			return -EFAULT;
-		if (status_zombie==NO)
-			qic02_release_resources();	/* and go zombie */
-		if (update_ifc_masks(qic02_tape_dynconf.ifc_type))
-			return -EIO;
-		if (qic02_get_resources())
-			return -ENXIO;
-		return 0;
+	/* ---note: mt_count is signed, negative seeks must be
+	 * ---	    translated to seeks in opposite direction!
+	 * (only needed for Sun-programs, I think.)
+	 */
+	/* ---note: MTFSF with count 0 should position the
+	 * ---	    tape at the beginning of the current file.
+	 */
 
+	if (TP_DIAGS(current_tape_dev))
+	{
+	    printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count);
 	}
-	if (status_zombie==YES) {
-		tpqputs(TPQD_ALWAYS, "Configs not set");
-		return -ENXIO;
+	
+	if (operation.mt_count < 0)
+	{
+	    tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored");
 	}
-#endif
-	if (c == (MTIOCTOP & IOCCMD_MASK)) {
-
-		/* Compare expected struct size and actual struct size. This
-		 * is useful to catch programs compiled with old #includes.
-		 */
-		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtop)) {
-			tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!");
-			return -EFAULT;
+	
+	ioctl_status.mt_resid = operation.mt_count;
+	if (operation.mt_op == MTSEEK)
+	{
+	    if (!TP_HAVE_SEEK)
+	    {
+		return -ENOTTY;
+	    }
+	    
+	    seek_addr_buf[0] = (operation.mt_count>>16)&0xff;
+	    seek_addr_buf[1] = (operation.mt_count>>8)&0xff;
+	    seek_addr_buf[2] = (operation.mt_count)&0xff;
+	    if (operation.mt_count>>24)
+	    {
+		return -EINVAL;
+	    }
+	    if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
+	    {
+		return error;
+	    }
+	    
+	    ioctl_status.mt_resid = 0;
+	}
+	else
+	{
+	    while (operation.mt_count > 0)
+	    {
+		operation.mt_count--;
+		if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
+		{
+		    return error;
 		}
-
-		/* copy mtop struct from user space to kernel space */
-		stp = (char *) &operation;
-		argp = (char *) ioarg;
-		if (copy_from_user(stp, argp, sizeof(operation)))
-			return -EFAULT;
-
-		/* ---note: mt_count is signed, negative seeks must be
-		 * ---	    translated to seeks in opposite direction!
-		 * (only needed for Sun-programs, I think.)
-		 */
-		/* ---note: MTFSF with count 0 should position the
-		 * ---	    tape at the beginning of the current file.
-		 */
-
-		if (TP_DIAGS(current_tape_dev))
-			printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count);
-
-		if (operation.mt_count < 0)
-			tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored");
-
+		
 		ioctl_status.mt_resid = operation.mt_count;
-		if (operation.mt_op == MTSEEK) {
-			if (!TP_HAVE_SEEK)
-				return -ENOTTY;
-			seek_addr_buf[0] = (operation.mt_count>>16)&0xff;
-			seek_addr_buf[1] = (operation.mt_count>>8)&0xff;
-			seek_addr_buf[2] = (operation.mt_count)&0xff;
-			if (operation.mt_count>>24)
-				return -EINVAL;
-			if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
-				return error;
-			ioctl_status.mt_resid = 0;
-		} else {
-			while (operation.mt_count > 0) {
-				operation.mt_count--;
-				if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
-					return error;
-				ioctl_status.mt_resid = operation.mt_count;
-			}
-		}
-		return 0;
-
-	} else if (c == (MTIOCGET & IOCCMD_MASK)) {
-		if (TP_DIAGS(current_tape_dev))
-			printk("GET ");
-
-		/* compare expected struct size and actual struct size */
-		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) {
-			tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!");
-			return -EFAULT;
-		}
-
-		/* It appears (gmt(1)) that it is normal behaviour to
-		 * first set the status with MTNOP, and then to read
-		 * it out with MTIOCGET
-		 */
-
-		/* copy results to user space */
-		stp = (char *) &ioctl_status;
-		argp = (char *) ioarg;
-		if (copy_to_user(stp, argp, sizeof(ioctl_status)))
-			return -EFAULT;
-		return 0;
-
-
-	} else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) {
-		if (TP_DIAGS(current_tape_dev))
-			printk("POS ");
-
-		/* compare expected struct size and actual struct size */
-		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) {
-			tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!");
-			return -EFAULT;
-		}
-
-		tpqputs(TPQD_IOCTLS, "MTTELL reading block address");
-		if ((doing_read==YES) || (doing_write==YES))
-			finish_rw(AR_QCMDV_TELL_BLK);
+	    }
+	}
+	return 0;
+	
+    }
+    else if (c == _IOC_NR(MTIOCGET))
+    {
+	if (TP_DIAGS(current_tape_dev))
+	{
+	    printk("GET ");
+	}
+	
+	CHECK_IOC_SIZE(mtget);
 
-		c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK);
-		if (c!=TE_OK)
-			return -EIO;
+	/* It appears (gmt(1)) that it is normal behaviour to
+	 * first set the status with MTNOP, and then to read
+	 * it out with MTIOCGET
+	 */
 
-		ioctl_tell.mt_blkno = (blk_addr[3] << 16) | (blk_addr[4] << 8) | blk_addr[5];
+	/* copy results to user space */
+	if (copy_to_user((char *) &ioctl_status, (char *) ioarg, sizeof(ioctl_status)))
+	{
+	    return -EFAULT;
+	}	
+	return 0;
+    }
+    else if (TP_HAVE_TELL && (c == _IOC_NR(MTIOCPOS)))
+    {
+	if (TP_DIAGS(current_tape_dev))
+	{
+	    printk("POS ");
+	}
+	
+	CHECK_IOC_SIZE(mtpos);
+	
+	tpqputs(TPQD_IOCTLS, "MTTELL reading block address");
+	if ((doing_read==YES) || (doing_write==YES))
+	{
+	    finish_rw(AR_QCMDV_TELL_BLK);
+	}
+	
+	c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK);
+	if (c!=TE_OK)
+	{
+	    return -EIO;
+	}
+	
+	ioctl_tell.mt_blkno = (blk_addr[3] << 16) | (blk_addr[4] << 8) | blk_addr[5];
 
-		/* copy results to user space */
-		stp = (char *) &ioctl_tell;
-		argp = (char *) ioarg;
-		if (copy_to_user(stp, argp, sizeof(ioctl_status)))
-			return -EFAULT;
-		return 0;
+	/* copy results to user space */
+	if (copy_to_user((char *) ioarg, (char *) &ioctl_tell, sizeof(ioctl_tell)))
+	{
+	    return -EFAULT;
+	}
+	return 0;
 
-	} else
-		return -ENOTTY;	/* Other cmds not supported. */
+    }
+    else
+    {
+	return -ENOTTY;	/* Other cmds not supported. */
+    }
 } /* qic02_tape_ioctl */
 
 
@@ -2820,215 +2758,229 @@
 	NULL				/* revalidate */
 };
 
-/* align `a' at `size' bytes. `size' must be a power of 2 */
-static inline unsigned long const align_buffer(unsigned long a, unsigned size)
-{
-	if (a & (size-1))			/* if not aligned */
-		return (a | (size-1)) + 1;
-	else					/* else is aligned */
-		return a;
-}
 
+/* Why is this not is one place? */
+/* Pure 2^n version of get_order */
+static inline int __get_order(unsigned long size)
+{
+    int order;
+
+    size = (size-1) >> (PAGE_SHIFT-1);
+    order = -1;
+    do
+    {
+	size >>= 1;
+	order++;
+    } while (size);
+    return order;
+}
 
 
 static void qic02_release_resources(void)
 {
-	free_irq(QIC02_TAPE_IRQ, NULL);
-	free_dma(QIC02_TAPE_DMA);
-	status_zombie = YES;
+    free_irq(QIC02_TAPE_IRQ, NULL);
+    free_dma(QIC02_TAPE_DMA);
+    release_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE);
+    if (buffaddr)
+    {
+	free_pages(buffaddr, __get_order(TPQBUF_SIZE));
+    }
+    buffaddr = 0; /* Better to cause a panic than overwite someone else */
+    status_zombie = YES;
 } /* qic02_release_resources */
 
 
-
-
 static int qic02_get_resources(void)
 {
-	/* First perform some checks. If one of them fails,
-	 * the tape driver will not be registered to the system.
-	 */
-	if (QIC02_TAPE_IRQ>16) {
-		tpqputs(TPQD_ALWAYS, "Bogus interrupt number.");
-		return -1;
-	}
-
-	/* for DYNCONF, allocating DMA & IRQ should not be done until 
-         * the config parameters have been set using MTSETCONFIG.
-	 */
-
-	/* get IRQ */
-	if (request_irq(QIC02_TAPE_IRQ, qic02_tape_interrupt, SA_INTERRUPT, "QIC-02", NULL)) {
-		printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
-			QIC02_TAPE_IRQ);
-		status_zombie = YES;
-		return -1;
-	}
-
-	/* After IRQ, allocate DMA channel */
-	if (request_dma(QIC02_TAPE_DMA,"QIC-02")) {
-		printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n",
-			QIC02_TAPE_DMA);
-		free_irq(QIC02_TAPE_IRQ, NULL);
-		status_zombie = YES;
-		return -1;
-	}
-
-	printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n",
-		QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
-		((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))?
-			QIC02_CMD_PORT : QIC02_STAT_PORT,
-		(QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" :
-			((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek"));
+    /* First perform some checks. If one of them fails,
+     * the tape driver will not be registered to the system.
+     */
+    if (QIC02_TAPE_IRQ>16)
+    {
+	tpqputs(TPQD_ALWAYS, "Bogus interrupt number.");
+	return -ENXIO;
+    }
+
+    /* for DYNCONF, allocating IO, DMA and IRQ should not be done until 
+     * the config parameters have been set using MTSETCONFIG.
+     */
+
+    if (check_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE))
+    {
+	printk(TPQIC02_NAME ": IO space at 0x%x [%d ports] already reserved\n",
+	       QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE);
+	return -ENXIO;
+    }
+
+    /* get IRQ */
+    if (request_irq(QIC02_TAPE_IRQ, qic02_tape_interrupt, SA_INTERRUPT, "QIC-02", NULL))
+    {
+	printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
+	       QIC02_TAPE_IRQ);
+	return -EBUSY;
+    }
+
+    /* After IRQ, allocate DMA channel */
+    if (request_dma(QIC02_TAPE_DMA,"QIC-02"))
+    {
+	printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n",
+	       QIC02_TAPE_DMA);
+	free_irq(QIC02_TAPE_IRQ, NULL);
+	return -EBUSY;
+    }
 
-	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
-		/* No drive detected, so vanish */
-		tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma.");
-		status_dead = YES;
-		qic02_release_resources();
-		return -1;
-	}
+    /* Grab the IO region. We already made sure it's available. */
+    request_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE, TPQIC02_NAME);
+    
+    /* Setup the page-address for the dma transfer. */
+
+    /*** TODO: does _get_dma_pages() really return the physical address?? ****/
+    buffaddr = __get_dma_pages(GFP_KERNEL,__get_order(TPQBUF_SIZE));
+    
+    if (!buffaddr)
+    {
+	qic02_release_resources();
+	return -EBUSY; /* Not ideal, EAGAIN perhaps? */
+    }
+    
+    memset( (void*) buffaddr, 0, TPQBUF_SIZE );
+    
+    printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n",
+	   QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
+	   ((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))?
+	   QIC02_CMD_PORT : QIC02_STAT_PORT,
+	   (QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" :
+	   ((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek"));
+    
+    if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK)
+    {
+	/* No drive detected, so vanish */
+	tpqputs(TPQD_ALWAYS, "No drive detected -- releasing IO/IRQ/DMA.");
+	status_dead = YES;
+	qic02_release_resources();
+	return -EIO;
+    }
 
-	/* All should be ok now */
-	status_zombie = NO;
-	return 0;
+    /* All should be ok now */
+    status_zombie = NO;
+    return 0;
 } /* qic02_get_resources */
 
-
+#ifdef MODULE
+static
+#endif
 int qic02_tape_init(void)
-	/* Shouldn't this be a caddr_t ? */
 {
+    if (TPSTATSIZE != 6)
+    {
+	printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n");
+	return -ENODEV;
+    }
+    if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000))
+    {
+	printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n");
+	return -ENODEV;
+    }
 
-	if (TPSTATSIZE != 6) {
-		printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n");
-		return -ENODEV;
-	}
-	if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) {
-		printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n");
-		return -ENODEV;
-	}
-
-	QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS;
-
-	current_tape_dev = MKDEV(QIC02_TAPE_MAJOR, 0);
+    current_tape_dev = MKDEV(QIC02_TAPE_MAJOR, 0);
 
 #ifndef CONFIG_QIC02_DYNCONF
-	printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n",
-		 QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
+    printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n",
+	   QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
 # if QIC02_TAPE_IFC == WANGTEK
-		 QIC02_STAT_PORT, "Wangtek",
+	   QIC02_STAT_PORT, "Wangtek",
 # elif QIC02_TAPE_IFC == ARCHIVE
-		 QIC02_CMD_PORT, "Archive",
+	   QIC02_CMD_PORT, "Archive",
 # elif QIC02_TAPE_IFC == MOUNTAIN
-		 QIC02_CMD_PORT, "Mountain",
+	   QIC02_CMD_PORT, "Mountain",
 # else
 #  error
 # endif
-		 rcs_revision, rcs_date);
-	if (qic02_get_resources())
-		return -ENODEV;
+	   rcs_revision, rcs_date);
+    if (qic02_get_resources())
+    {
+	return -ENODEV;
+    }
 #else
-	printk(TPQIC02_NAME ": Runtime config, %s, %s\n", 
-		 rcs_revision, rcs_date);
-	
-	QIC02_TAPE_IRQ = BOGUS_IRQ;	/* invalid value */
+    printk(TPQIC02_NAME ": Runtime config, %s, %s\n", 
+	   rcs_revision, rcs_date);
 #endif
-
-	printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
-
-	/* 
-	 * Setup the page-address for the dma transfer.
-	 */
-#ifdef MODULE
-        qic02_tape_buf = kmalloc(TPQBUF_SIZE+TAPE_BLKSIZE, GFP_DMA);
-        if (qic02_tape_buf == NULL) {
+    printk(TPQIC02_NAME ": DMA buffers: %u blocks\n", NR_BLK_BUF);
+    /* If we got this far, install driver functions */
+    if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops))
+    {
+	printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
 #ifndef CONFIG_QIC02_DYNCONF
-        /*
-         * irq and dma were requested by qic_get_resources, so
-         * relase them only when _not_ using DYNCONF
-         */
-		qic02_release_resources();
-#endif
-		return -ENODEV;
-       }
-       buffaddr = align_buffer(virt_to_bus((unsigned long *)qic02_tape_buf),
-                               TAPE_BLKSIZE);
-       printk(", at address 0x%lx (0x%lx)\n", buffaddr,
-              virt_to_bus((unsigned long *)qic02_tape_buf));
-#else /* no MODULE */
-	buffaddr = align_buffer(virt_to_bus(qic02_tape_buf), TAPE_BLKSIZE);
-	printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf);
-#endif /* MODULE */
-
-
-#ifndef CONFIG_MAX_16M
-	if (buffaddr+TPQBUF_SIZE>=0x1000000) {
-		printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n");
-#ifdef MODULE
-		qic02_release_resources();
-                kfree(qic02_tape_buf);
-#endif
-
-		return -ENODEV;
-	}
-#endif
-
-	/* If we got this far, install driver functions */
-	if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) {
-		printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
-#ifndef CONFIG_QIC02_DYNCONF
-		qic02_release_resources();
+	qic02_release_resources();
 #endif
-		return -ENODEV;
-	}
-
-	/* prepare timer */
-	TIMEROFF;
-	timer_table[QIC02_TAPE_TIMER].expires = 0;
-	timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out;
+	return -ENODEV;
+    }
 
+    /* prepare timer */
+    TIMEROFF;
+    timer_table[QIC02_TAPE_TIMER].expires = 0;
+    timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out;
+    
 #ifndef CONFIG_QIC02_DYNCONF
-	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
-		/* No drive detected, so vanish */
-		tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation...");
-		unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
-		qic02_release_resources();
-		return -ENODEV;
-	} else {
-		if (is_exception()) {
-			tpqputs(TPQD_ALWAYS, "exception detected\n");
-			(void) tp_sense(TP_WRP|TP_POR|TP_CNI);
-		}
+    if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK)
+    {
+	/* No drive detected, so vanish */
+	tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation...");
+	qic02_release_resources();
+	status_dead = YES;
+	return -ENODEV;
+    }
+    else
+    {
+	if (is_exception())
+	{
+	    tpqputs(TPQD_ALWAYS, "exception detected\n");
+	    (void) tp_sense(TP_WRP|TP_POR|TP_CNI);
 	}
+    }
 #endif
 
-	/* initialize generic status for ioctl requests */
-
-	ioctl_status.mt_type	= QIC02_TAPE_DRIVE;	/* MT_IS* id nr */
-
-	ioctl_status.mt_resid	= 0;	/* ---residual count */
-	ioctl_status.mt_gstat	= 0;	/* ---generic status */
-	ioctl_status.mt_erreg	= 0;	/* not used */
-	ioctl_status.mt_fileno	= 0;	/* number of current file on tape */
-	ioctl_status.mt_blkno	= 0;	/* number of current (logical) block */
+    /* initialize generic status for ioctl requests */
+    
+    ioctl_status.mt_type	= QIC02_TAPE_DRIVE;	/* MT_IS* id nr */
+    
+    ioctl_status.mt_resid	= 0;	/* ---residual count */
+    ioctl_status.mt_gstat	= 0;	/* ---generic status */
+    ioctl_status.mt_erreg	= 0;	/* not used */
+    ioctl_status.mt_fileno	= 0;	/* number of current file on tape */
+    ioctl_status.mt_blkno	= 0;	/* number of current (logical) block */
 
-	return 0;
+    return 0;
 } /* qic02_tape_init */
 
 #ifdef MODULE
 
-int init_module(void) {
-	return qic02_tape_init();
-}
-
-void cleanup_module(void) {
-	unsigned long flags;
-       
-	save_flags(flags); 
-	cli(); 
-	unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); 
+void cleanup_module(void)
+{
+    if (status_zombie == NO)
+    {
 	qic02_release_resources();
-	if (qic02_tape_buf) kfree(qic02_tape_buf); 
-	sti(); 
-	restore_flags(flags); 
+    }
+    unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
 }
 
-#endif /* MODULE */
+int init_module(void)
+{
+    int retval;
+    retval=qic02_tape_init();
+# ifdef CONFIG_QIC02_DYNCONF
+    /* This allows the dynamic config program to setup the card
+     * by presetting qic02_tape_dynconf via insmod
+     */
+    if (!retval && qic02_tape_dynconf.ifc_type)
+    {
+	retval=update_ifc_masks(qic02_tape_dynconf.ifc_type);
+	if (retval)
+	{
+	  cleanup_module();
+	}
+    }
+# endif
+    return retval;
+}
+#endif
diff -u --recursive --new-file v2.1.19/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.19/linux/drivers/char/tty_io.c	Mon Dec 30 15:39:08 1996
+++ linux/drivers/char/tty_io.c	Tue Dec 31 21:30:29 1996
@@ -1924,7 +1924,7 @@
 	
 	kbd_init();
 #ifdef CONFIG_ESPSERIAL  /* init ESP before rs, so rs doesn't see the port */
-	esp_init();
+	espserial_init();
 #endif
 #ifdef CONFIG_SERIAL
 	rs_init();
diff -u --recursive --new-file v2.1.19/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v2.1.19/linux/drivers/char/wdt.c	Thu Dec 12 19:37:02 1996
+++ linux/drivers/char/wdt.c	Thu Jan  2 15:13:24 1997
@@ -22,6 +22,7 @@
  *		Alan Cox	:	Added the watchdog ioctl() stuff
  *		Alan Cox	:	Fixed the reboot problem (as noted by
  *					Matt Crocker).
+ *		Alan Cox	:	Added wdt= boot option
  */
 
 #include <linux/config.h>
@@ -46,6 +47,7 @@
 
 /*
  *	You must set these - there is no sane way to probe for this board.
+ *	You can use wdt=x,y to set these now.
  */
  
 int io=0x240;
@@ -53,6 +55,20 @@
 
 #define WD_TIMO (100*60)		/* 1 minute */
 
+/*
+ *	Setup options
+ */
+ 
+void wdt_setup(char *str, int *ints)
+{
+	if(ints[0]>0)
+	{
+		io=ints[1];
+		if(ints[0]>1)
+			irq=ints[2];
+	}
+}
+ 
 /*
  *	Programming support
  */
diff -u --recursive --new-file v2.1.19/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.19/linux/drivers/net/Config.in	Sun Dec 22 16:37:31 1996
+++ linux/drivers/net/Config.in	Thu Jan  2 15:13:24 1997
@@ -140,6 +140,10 @@
   tristate 'WIC Radio IP bridge' CONFIG_WIC
 fi
 
+if [ "$CONFIG_X25" != "n" ]; then
+  tristate 'LAPB over Ethernet driver' CONFIG_LAPBETHER
+fi
+
 tristate 'SLIP (serial line) support' CONFIG_SLIP
 if [ "$CONFIG_SLIP" != "n" ]; then
   bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
diff -u --recursive --new-file v2.1.19/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.19/linux/drivers/net/Makefile	Sun Dec 22 16:37:31 1996
+++ linux/drivers/net/Makefile	Thu Jan  2 15:13:24 1997
@@ -524,6 +524,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_LAPBETHER),y)
+L_OBJS += lapbether.o
+else
+  ifeq ($(CONFIG_LAPBETHER),m)
+  M_OBJS += lapbether.o
+  endif
+endif
+
 # If anything built-in uses slhc, then build it into the kernel also.
 # If not, but a module uses it, build as a module.
 ifdef CONFIG_SLHC_BUILTIN
diff -u --recursive --new-file v2.1.19/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.19/linux/drivers/net/Space.c	Sun Dec 22 16:37:31 1996
+++ linux/drivers/net/Space.c	Thu Jan  2 15:13:25 1997
@@ -254,25 +254,6 @@
 #   define NEXT_DEV	(&sdla0_dev)
 #endif
 
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
-	extern int nr_init(struct device *);
-	static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, };
-	static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, };
-	static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, };
-	static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, };
-#   undef NEXT_DEV
-#   define	NEXT_DEV	(&nr0_dev)
-#endif
-#ifdef CONFIG_ROSE
-	extern int rose_init(struct device *);
-	static struct device rose1_dev = { "rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV,   rose_init, };
-	static struct device rose0_dev = { "rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &rose1_dev, rose_init, };
-#   undef NEXT_DEV
-#   define	NEXT_DEV	(&rose0_dev)
-#endif
-#endif
-
 /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
 #ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */
 static struct device atp_dev = {
diff -u --recursive --new-file v2.1.19/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.1.19/linux/drivers/net/arcnet.c	Tue Dec 31 21:41:02 1996
+++ linux/drivers/net/arcnet.c	Thu Jan  2 15:13:25 1997
@@ -1308,11 +1308,10 @@
  *
  * Intelligent defaults?!  Nah.
  */
+
 void arcnet_setup(struct device *dev)
 {
-	int i;
-	for (i=0; i<DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
+	dev_init_buffers(dev);
 
 	dev->broadcast[0]	= 0x00;	/* for us, broadcasts are address 0 */
 	dev->addr_len		= 1;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- v2.1.19/linux/drivers/net/bpqether.c	Mon Dec 30 15:39:08 1996
+++ linux/drivers/net/bpqether.c	Thu Jan  2 15:13:25 1997
@@ -543,8 +543,7 @@
                 return -EIO;
         }
 
-	for (k=0; k < DEV_NUMBUFFS; k++)
-		skb_queue_head_init(&dev->buffs[k]);
+	dev_init_buffers(dev);
 
 	dev->hard_start_xmit = bpq_xmit;
 	dev->open	     = bpq_open;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/bsd_comp.c linux/drivers/net/bsd_comp.c
--- v2.1.19/linux/drivers/net/bsd_comp.c	Tue Oct 29 19:58:11 1996
+++ linux/drivers/net/bsd_comp.c	Tue Dec 31 16:27:23 1996
@@ -90,7 +90,7 @@
 #include <linux/ppp_defs.h>
 
 #ifdef NEW_SKBUFF
-#include <linux/netprotocol.h>
+# /*nodep*/ include <linux/netprotocol.h>
 #endif
 
 #include <linux/ip.h>
diff -u --recursive --new-file v2.1.19/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.19/linux/drivers/net/de4x5.c	Tue Dec 31 21:41:03 1996
+++ linux/drivers/net/de4x5.c	Thu Jan  2 09:52:02 1997
@@ -46,20 +46,14 @@
     to the differences in the EISA and PCI CSR address offsets from the base
     address.
 
-    The ability to load  this driver as a  loadable module has been included
-    and used  extensively during the  driver development (to save those long
-    reboot sequences).  Loadable module support  under PCI and EISA has been
+    The ability to load this driver  as a loadable  module has been included
+    and used extensively during the  driver development (to save those  long
+    reboot sequences).  Loadable module support under  PCI and EISA has been
     achieved by letting the driver autoprobe as if it were compiled into the
     kernel, except that there is no autoprobing of the IRQ lines. This is of
-    no great  consequence except do make sure  you're not sharing interrupts
-    with  anything that cannot  accommodate  interrupt sharing! The existing
-    register_netdevice() code will only allow one device to be registered at
-    a time.
-
-    ************************************************************************
-    For now, please only use the 'io=??' assignment (see  2. below, ?? != 0)
-    when loading a module.
-    ************************************************************************
+    no great consequence except do  make sure you're not sharing  interrupts
+    with anything that cannot   accommodate interrupt sharing!  By  default,
+    the driver will autoprobe for the next available card.
 
     Essentially, the I/O address and IRQ information  are ignored and filled
     in later by  the PCI BIOS   during the PCI  probe.  Note  that the board
@@ -71,13 +65,20 @@
     0) have a copy of the loadable modules code installed on your system.
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
     temporary directory.
-    2) edit the source code near line 3779 to reflect the I/O address you're
+    2) edit the source code near line 4146 to reflect the I/O address you're
     using (only  if you want to manually  load the module),  or assign these
     when loading by:
 
                    insmod de4x5.o io=0xghh         where g = bus number
 		                                        hh = device number
 
+       NB: autoprobing for modules is now supported by default. You may just
+           use:
+
+                   insmod de4x5.o
+
+           to load the next available board. For a specific board, still use
+	   the 'io=?' above.
     3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
     that the correct bits are compiled (see end of source code).
     4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
@@ -215,11 +216,13 @@
                            by <bhat@mundook.cs.mu.OZ.AU>
       0.45    8-Dec-96    Include endian functions for PPC use, from work
                            by <cort@cs.nmt.edu>.
+      0.451  28-Dec-96    Added fix to allow autoprobe for modules after
+                           suggestion from <mjacob@feral.com>
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.45 96/12/8 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:v0.451 96/12/28 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -1666,7 +1669,9 @@
 	maxSlots = i + 1;
     }
 
-    for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+    for (status = -ENODEV;
+	 (i<maxSlots) && (dev!=NULL) && (!loading_module || (num_de4x5s == 0));
+	 i++, iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) {
 	    cfid = inl(PCI_CFID);
 	    cfrv = inl(PCI_CFRV);
@@ -1740,7 +1745,8 @@
     }
 
     for (index=0;
-	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND) &&
+	 (!loading_module || (num_de4x5s == 0));
 	 index++) {
 	dev_num = PCI_SLOT(dev_fn);
 
@@ -4130,10 +4136,8 @@
 ** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes
 ** to "do the right thing".
 **
-** NB: Current register_netdevice() code does not permit assigning io=0 as
-** this driver will autoprobe all instances of acceptable DECchips. The
-** cleanup_module() code needs work still....(just to unload modules owned
-** by this driver).
+** The module autoprobe will only load one instance of the driver and
+** hardware.
 */
 static char devicename[9] = { 0, };
 static struct device thisDE4X5 = {
@@ -4142,7 +4146,7 @@
     0, 0,         /* I/O address, IRQ                                       */
     0, 0, 0, NULL, de4x5_probe };
 
-static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION              */
+static int io=0x0; /* EDIT THESE LINES FOR YOUR CONFIGURATION              */
 MODULE_PARM(io, "i");
 
 int
diff -u --recursive --new-file v2.1.19/linux/drivers/net/defxx.c linux/drivers/net/defxx.c
--- v2.1.19/linux/drivers/net/defxx.c	Tue Nov 19 15:53:55 1996
+++ linux/drivers/net/defxx.c	Thu Jan  2 15:13:25 1997
@@ -687,11 +687,11 @@
 	tmp_dev->rebuild_header			= NULL;		/* set in fddi_setup() */
 	tmp_dev->set_multicast_list		= &dfx_ctl_set_multicast_list;
 	tmp_dev->set_mac_address		= &dfx_ctl_set_mac_address;
-	tmp_dev->do_ioctl				= NULL;		/* not supported for now &&& */
-	tmp_dev->set_config				= NULL;		/* not supported for now &&& */
-	tmp_dev->header_cache_bind		= NULL;		/* not supported */
-	tmp_dev->header_cache_update	= NULL;		/* not supported */
-	tmp_dev->change_mtu				= NULL;		/* set in fddi_setup() */
+	tmp_dev->do_ioctl			= NULL;		/* not supported for now &&& */
+	tmp_dev->set_config			= NULL;		/* not supported for now &&& */
+	tmp_dev->hard_header_cache		= NULL;		/* not supported */
+	tmp_dev->header_cache_update		= NULL;		/* not supported */
+	tmp_dev->change_mtu			= NULL;		/* set in fddi_setup() */
 
 	/* Initialize remaining device structure information */
 
@@ -3134,13 +3134,13 @@
 	 */
 
 	if (!IN_RANGE(skb->len, FDDI_K_LLC_ZLEN, FDDI_K_LLC_LEN))
-		{
-		printk("%s: Invalid packet length - %lu bytes\n", dev->name, skb->len);
-		bp->xmt_length_errors++;	/* bump error counter */
+	{
+		printk("%s: Invalid packet length - %u bytes\n", 
+			dev->name, skb->len);
+		bp->xmt_length_errors++;		/* bump error counter */
 		dev_tint(dev);				/* dequeue packets from xmt queue and send them */
-		return(0);					/* return "success" */
-		}
-
+		return(0);				/* return "success" */
+	}
 	/*
 	 * See if adapter link is available, if not, free buffer
 	 *
diff -u --recursive --new-file v2.1.19/linux/drivers/net/dlci.c linux/drivers/net/dlci.c
--- v2.1.19/linux/drivers/net/dlci.c	Tue Nov 19 15:53:55 1996
+++ linux/drivers/net/dlci.c	Thu Jan  2 15:13:25 1997
@@ -619,9 +619,8 @@
    dev->pa_brdaddr      = 0;
    dev->pa_mask         = 0;
 
-   for (i = 0; i < DEV_NUMBUFFS; i++) 
-      skb_queue_head_init(&dev->buffs[i]);
-
+   dev_init_buffers(dev);
+   
    return(0);
 }
 
diff -u --recursive --new-file v2.1.19/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v2.1.19/linux/drivers/net/eql.c	Wed Dec 18 15:58:47 1996
+++ linux/drivers/net/eql.c	Thu Jan  2 15:13:25 1997
@@ -249,13 +249,11 @@
   
   	/*
   	 *	Fill in the fields of the device structure with 
-	 *	eql-generic values. This should be in a common 
-	 *	file instead of per-driver.  
+	 *	eql-generic values. 
 	 */
 
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
-
+	dev_init_buffers(dev);
+	
 	/*
 	 *	Now we undo some of the things that eth_setup does
 	 * 	that we don't like 
diff -u --recursive --new-file v2.1.19/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.1.19/linux/drivers/net/hdlcdrv.c	Mon Dec 30 15:39:09 1996
+++ linux/drivers/net/hdlcdrv.c	Thu Jan  2 15:13:25 1997
@@ -790,8 +790,8 @@
 	dev->get_stats = hdlcdrv_get_stats;
 
 	/* Fill in the fields of the device structure */
-	for (i=0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
+
+	dev_init_buffers(dev);
 
 	skb_queue_head_init(&s->send_queue);
 	
diff -u --recursive --new-file v2.1.19/linux/drivers/net/lapbether.c linux/drivers/net/lapbether.c
--- v2.1.19/linux/drivers/net/lapbether.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/lapbether.c	Thu Jan  2 15:13:25 1997
@@ -0,0 +1,629 @@
+/*
+ *	"LAPB via ethernet" driver release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly 
+ *	fail to work with new releases, misbehave and/or generally screw up. 
+ *	It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	This is a "pseudo" network driver to allow LAPB over Ethernet.
+ *
+ *	This driver can use any ethernet destination address, and can be 
+ *	limited to accept frames from one dedicated ethernet card only.
+ *
+ *	History
+ *	LAPBETH 001	Jonathan Naylor		Cloned from bpqether.c
+ */
+
+#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/string.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/module.h>
+#include <linux/net_alias.h>
+#include <linux/lapb.h>
+
+#include <linux/lapbether.h>
+
+static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+
+static char lapbeth_eth_addr[6];
+
+static int lapbeth_rcv(struct sk_buff *, struct device *, struct packet_type *);
+static int lapbeth_device_event(struct notifier_block *, unsigned long, void *);
+
+static struct packet_type lapbeth_packet_type = {
+	0,		/* ntohs(ETH_P_DEC),*/
+	0,		/* copy */
+	lapbeth_rcv,
+	NULL,
+	NULL,
+};
+
+static struct notifier_block lapbeth_dev_notifier = {
+	lapbeth_device_event,
+	0
+};
+
+
+#define MAXLAPBDEV 100
+
+static struct lapbethdev {
+	struct lapbethdev *next;
+	char   ethname[14];		/* ether device name */
+	struct device *ethdev;		/* link to ethernet device */
+	struct device axdev;		/* lapbeth device (lapb#) */
+	struct enet_statistics stats;	/* some statistics */
+	char   dest_addr[6];		/* ether destination address */
+	char   acpt_addr[6];		/* accept ether frames from this address only */
+} *lapbeth_devices = NULL;
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Get the ethernet device for a LAPB device
+ */
+static __inline__ struct device *lapbeth_get_ether_dev(struct device *dev)
+{
+	struct lapbethdev *lapbeth;
+	
+	lapbeth = (struct lapbethdev *)dev->priv;
+
+	return (lapbeth != NULL) ? lapbeth->ethdev : NULL;
+}
+
+/*
+ *	Get the LAPB device for the ethernet device
+ */
+static __inline__ struct device *lapbeth_get_x25_dev(struct device *dev)
+{
+	struct lapbethdev *lapbeth;
+
+	for (lapbeth = lapbeth_devices; lapbeth != NULL; lapbeth = lapbeth->next)
+		if (lapbeth->ethdev == dev)
+			return &lapbeth->axdev;
+
+	return NULL;
+}
+
+static __inline__ int dev_is_ethdev(struct device *dev)
+{
+	return (
+			dev->type == ARPHRD_ETHER
+			&& strncmp(dev->name, "dummy", 5)
+#ifdef CONFIG_NET_ALIAS
+			&& !net_alias_is(dev)
+#endif
+	);
+}
+
+/*
+ *	Sanity check: remove all devices that ceased to exists and
+ *	return '1' if the given LAPB device was affected.
+ */
+static int lapbeth_check_devices(struct device *dev)
+{
+	struct lapbethdev *lapbeth, *lapbeth_prev;
+	int result = 0;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	lapbeth_prev = NULL;
+
+	for (lapbeth = lapbeth_devices; lapbeth != NULL; lapbeth = lapbeth->next) {
+		if (!dev_get(lapbeth->ethname)) {
+			if (lapbeth_prev)
+				lapbeth_prev->next = lapbeth->next;
+			else
+				lapbeth_devices = lapbeth->next;
+				
+			if (&lapbeth->axdev == dev)
+				result = 1;
+
+			unregister_netdev(&lapbeth->axdev);
+			kfree(lapbeth);
+		}
+
+		lapbeth_prev = lapbeth;
+	}
+	
+	restore_flags(flags);
+	
+	return result;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Receive a LAPB frame via an ethernet interface.
+ */
+static int lapbeth_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+	int len, err;
+	struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
+	struct lapbethdev *lapbeth;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+	
+	dev = lapbeth_get_x25_dev(dev);
+
+	if (dev == NULL || dev->start == 0) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	/*
+	 * if we want to accept frames from just one ethernet device
+	 * we check the source address of the sender.
+	 */
+
+	lapbeth = (struct lapbethdev *)dev->priv;
+
+	if (!(lapbeth->acpt_addr[0] & 0x01) && memcmp(eth->h_source, lapbeth->acpt_addr, ETH_ALEN)) {
+		printk(KERN_DEBUG "lapbeth: wrong dest address\n");
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	((struct lapbethdev *)dev->priv)->stats.rx_packets++;
+
+	len = skb->data[0] + skb->data[1] * 256;
+
+	skb_pull(skb, 2);	/* Remove the length bytes */
+	skb_trim(skb, len);	/* Set the length of the data */
+
+	if ((err = lapb_data_received(lapbeth, skb)) != LAPB_OK) {
+		kfree_skb(skb, FREE_READ);
+		printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n", err);
+	}
+
+	return 0;
+}
+
+static void lapbeth_data_indication(void *token, struct sk_buff *skb)
+{
+	struct lapbethdev *lapbeth = (struct lapbethdev *)token;
+	unsigned char *ptr;
+
+	ptr  = skb_push(skb, 1);
+	*ptr = 0x00;
+
+	skb->dev      = &lapbeth->axdev;
+	skb->protocol = htons(ETH_P_X25);
+	skb->mac.raw  = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+}
+
+/*
+ * 	Send a LAPB frame via an ethernet interface
+ */
+static int lapbeth_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct lapbethdev *lapbeth;
+	int err;
+	
+	lapbeth = (struct lapbethdev *)dev->priv;
+
+	/*
+	 * Just to be *really* sure not to send anything if the interface
+	 * is down, the ethernet device may have gone.
+	 */
+	if (!dev->start) {
+		lapbeth_check_devices(dev);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	switch (skb->data[0]) {
+		case 0x00:
+			break;
+		case 0x01:
+			if ((err = lapb_connect_request(lapbeth)) != LAPB_OK)
+				printk(KERN_ERR "lapbeth: lapb_connect_request error - %d\n", err);
+			kfree_skb(skb, FREE_WRITE);
+			return 0;
+		case 0x02:
+			if ((err = lapb_disconnect_request(lapbeth)) != LAPB_OK)
+				printk(KERN_ERR "lapbeth: lapb_disconnect_request err - %d\n", err);
+			kfree_skb(skb, FREE_WRITE);
+			return 0;
+		default:
+			kfree_skb(skb, FREE_WRITE);
+			return 0;
+	}
+
+	skb_pull(skb, 1);
+
+	if ((err = lapb_data_request(lapbeth, skb)) != LAPB_OK) {
+		printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err);
+		kfree_skb(skb, FREE_WRITE);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+	
+static void lapbeth_data_transmit(void *token, struct sk_buff *skb)
+{
+	struct lapbethdev *lapbeth = (struct lapbethdev *)token;
+	unsigned char *ptr;
+	struct device *dev;
+	int size;
+
+	skb->protocol = htons(ETH_P_X25);
+
+	size = skb->len;
+
+	ptr = skb_push(skb, 2);
+
+	*ptr++ = size % 256;
+	*ptr++ = size / 256;
+
+	lapbeth->stats.tx_packets++;
+
+	skb->dev = dev = lapbeth->ethdev;
+
+	dev->hard_header(skb, dev, ETH_P_DEC, lapbeth->dest_addr, NULL, 0);
+
+	dev->hard_start_xmit(skb, dev);
+}
+
+static void lapbeth_connected(void *token, int reason)
+{
+	struct lapbethdev *lapbeth = (struct lapbethdev *)token;
+	struct sk_buff *skb;
+	unsigned char *ptr;
+
+	if ((skb = dev_alloc_skb(1)) == NULL) {
+		printk(KERN_ERR "lapbeth: out of memory\n");
+		return;
+	}
+
+	ptr  = skb_put(skb, 1);
+	*ptr = 0x01;
+
+	skb->dev      = &lapbeth->axdev;
+	skb->protocol = htons(ETH_P_X25);
+	skb->mac.raw  = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+}
+
+static void lapbeth_disconnected(void *token, int reason)
+{
+	struct lapbethdev *lapbeth = (struct lapbethdev *)token;
+	struct sk_buff *skb;
+	unsigned char *ptr;
+
+	if ((skb = dev_alloc_skb(1)) == NULL) {
+		printk(KERN_ERR "lapbeth: out of memory\n");
+		return;
+	}
+
+	ptr  = skb_put(skb, 1);
+	*ptr = 0x02;
+
+	skb->dev      = &lapbeth->axdev;
+	skb->protocol = htons(ETH_P_X25);
+	skb->mac.raw  = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+}
+
+/*
+ *	Statistics
+ */
+static struct enet_statistics *lapbeth_get_stats(struct device *dev)
+{
+	struct lapbethdev *lapbeth;
+
+	lapbeth = (struct lapbethdev *)dev->priv;
+
+	return &lapbeth->stats;
+}
+
+/*
+ *	Set AX.25 callsign
+ */
+static int lapbeth_set_mac_address(struct device *dev, void *addr)
+{
+    struct sockaddr *sa = (struct sockaddr *)addr;
+
+    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+    return 0;
+}
+
+/*	Ioctl commands
+ *
+ *		SIOCSLAPBETHADDR	set the destination and accepted
+ *					source ethernet address (broadcast
+ *					or multicast: accept all)
+ */
+static int lapbeth_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	int err;
+	struct lapbeth_ethaddr *ethaddr = (struct lapbeth_ethaddr *)ifr->ifr_data;
+	struct lapbethdev *lapbeth = dev->priv;
+
+	if (!suser())
+		return -EPERM;
+	
+	if (lapbeth == NULL)		/* woops! */
+		return -ENODEV;
+	
+	switch (cmd) {
+		case SIOCSLAPBETHADDR:
+			if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct lapbeth_ethaddr))) != 0)
+				return err;
+			copy_from_user(lapbeth->dest_addr, ethaddr->destination, ETH_ALEN);
+			copy_from_user(lapbeth->acpt_addr, ethaddr->accept, ETH_ALEN);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * open/close a device
+ */
+static int lapbeth_open(struct device *dev)
+{
+	struct lapb_register_struct lapbeth_callbacks;
+	struct lapbethdev *lapbeth;
+	int err;
+
+	if (lapbeth_check_devices(dev))
+		return -ENODEV;		/* oops, it's gone */
+	
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	lapbeth = (struct lapbethdev *)dev->priv;
+
+	lapbeth_callbacks.connect_confirmation    = lapbeth_connected;
+	lapbeth_callbacks.connect_indication      = lapbeth_connected;
+	lapbeth_callbacks.disconnect_confirmation = lapbeth_disconnected;
+	lapbeth_callbacks.disconnect_indication   = lapbeth_disconnected;
+	lapbeth_callbacks.data_indication         = lapbeth_data_indication;
+	lapbeth_callbacks.data_transmit           = lapbeth_data_transmit;
+
+	if ((err = lapb_register(lapbeth, &lapbeth_callbacks)) != LAPB_OK) {
+		printk(KERN_ERR "lapbeth: lapb_register error - %d\n", err);
+		dev->tbusy = 1;
+		dev->start = 0;
+		return -ENODEV;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int lapbeth_close(struct device *dev)
+{
+	struct lapbethdev *lapbeth;
+	int err;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	lapbeth = (struct lapbethdev *)dev->priv;
+
+	if ((err = lapb_unregister(lapbeth)) != LAPB_OK)
+		printk(KERN_ERR "lapbeth: lapb_unregister error - %d\n", err);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static int lapbeth_dev_init(struct device *dev)
+{
+	return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ *	Setup a new device.
+ */
+static int lapbeth_new_device(struct device *dev)
+{
+	int k;
+	unsigned char *buf;
+	struct lapbethdev *lapbeth, *lapbeth2;
+	
+	if ((lapbeth = (struct lapbethdev *)kmalloc(sizeof(struct lapbethdev), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+		
+	memset(lapbeth, 0, sizeof(struct lapbethdev));
+	
+	lapbeth->ethdev = dev;
+
+	lapbeth->ethname[sizeof(lapbeth->ethname)-1] = '\0';
+	strncpy(lapbeth->ethname, dev->name, sizeof(lapbeth->ethname)-1);
+
+	memcpy(lapbeth->dest_addr, bcast_addr, sizeof(lapbeth_eth_addr));
+	memcpy(lapbeth->acpt_addr, bcast_addr, sizeof(lapbeth_eth_addr));
+
+	dev = &lapbeth->axdev;
+	buf = (unsigned char *)kmalloc(14, GFP_KERNEL);
+
+	for (k = 0; k < MAXLAPBDEV; k++) {
+		struct device *odev;
+
+		sprintf(buf, "lapb%d", k);
+
+		if ((odev = dev_get(buf)) == NULL || lapbeth_check_devices(odev))
+			break;
+	}
+
+	if (k == MAXLAPBDEV) {
+		kfree(lapbeth);
+		return -ENODEV;
+	}
+	
+	dev->priv = (void *)lapbeth;	/* pointer back */
+	dev->name = buf;
+	dev->init = lapbeth_dev_init;
+
+	if (register_netdev(dev) != 0) {
+		kfree(lapbeth);
+                return -EIO;
+        }
+
+	dev_init_buffers(dev);
+
+	dev->hard_start_xmit = lapbeth_xmit;
+	dev->open	     = lapbeth_open;
+	dev->stop	     = lapbeth_close;
+	dev->set_mac_address = lapbeth_set_mac_address;
+	dev->get_stats	     = lapbeth_get_stats;
+	dev->do_ioctl	     = lapbeth_ioctl;
+
+	/* preset with reasonable values */
+
+#if CONFIG_INET
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+	dev->pa_addr    = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask    = 0;
+	dev->pa_alen    = 4;
+#endif
+
+	dev->type            = ARPHRD_X25;
+	dev->hard_header_len = 3;
+	dev->mtu             = 1000;
+	dev->addr_len        = 0;
+
+	cli();
+
+	if (lapbeth_devices == NULL) {
+		lapbeth_devices = lapbeth;
+	} else {
+		for (lapbeth2 = lapbeth_devices; lapbeth2->next != NULL; lapbeth2 = lapbeth2->next);
+		lapbeth2->next = lapbeth;
+	}
+	
+	sti();
+
+	return 0;
+}
+
+
+/*
+ *	Handle device status changes.
+ */
+static int lapbeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+	
+	if (!dev_is_ethdev(dev))
+		return NOTIFY_DONE;
+
+	lapbeth_check_devices(NULL);
+
+	switch (event) {
+		case NETDEV_UP:		/* new ethernet device -> new LAPB interface */
+			if (lapbeth_get_x25_dev(dev) == NULL)
+				lapbeth_new_device(dev);
+			break;
+
+		case NETDEV_DOWN:	/* ethernet device closed -> close LAPB interface */
+			if ((dev = lapbeth_get_x25_dev(dev)) != NULL)
+				dev_close(dev);
+			break;
+
+		default:
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Initialize driver. To be called from af_ax25 if not compiled as a
+ * module
+ */
+int lapbeth_init(void)
+{
+	struct device *dev;
+
+	lapbeth_packet_type.type  = htons(ETH_P_DEC);
+	dev_add_pack(&lapbeth_packet_type);
+
+	register_netdevice_notifier(&lapbeth_dev_notifier);
+
+	printk(KERN_INFO "LAPB Ethernet driver version 0.01\n");
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev_is_ethdev(dev))
+			lapbeth_new_device(dev);
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	return lapbeth_init();
+}
+
+void cleanup_module(void)
+{
+	struct lapbethdev *lapbeth;
+
+	dev_remove_pack(&lapbeth_packet_type);
+
+	unregister_netdevice_notifier(&lapbeth_dev_notifier);
+
+	for (lapbeth = lapbeth_devices; lapbeth != NULL; lapbeth = lapbeth->next)
+		unregister_netdev(&lapbeth->axdev);
+}
+#endif
diff -u --recursive --new-file v2.1.19/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v2.1.19/linux/drivers/net/loopback.c	Thu Dec 12 19:37:06 1996
+++ linux/drivers/net/loopback.c	Thu Jan  2 15:13:25 1997
@@ -110,8 +110,6 @@
 /* Initialize the rest of the LOOPBACK device. */
 int loopback_init(struct device *dev)
 {
-	int i;
-
 	dev->mtu		= LOOPBACK_MTU;
 	dev->tbusy		= 0;
 	dev->hard_start_xmit	= loopback_xmit;
@@ -142,8 +140,7 @@
 	 *	Fill in the generic fields of the device structure. 
 	 */
    
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
+	dev_init_buffers(dev);
   
 	return(0);
 };
diff -u --recursive --new-file v2.1.19/linux/drivers/net/mkiss.c linux/drivers/net/mkiss.c
--- v2.1.19/linux/drivers/net/mkiss.c	Tue Dec 31 21:41:05 1996
+++ linux/drivers/net/mkiss.c	Thu Jan  2 15:13:25 1997
@@ -862,11 +862,11 @@
 }
 
 
-/* Initialize the driver.  Called by DDI. */
+/* Initialize the driver.  Called by network startup. */
+
 static int ax25_init(struct device *dev)
 {
 	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-	int i;
 
 	static char ax25_bcast[AX25_ADDR_LEN] =
 		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
@@ -903,8 +903,7 @@
 	dev->rebuild_header  = ax_rebuild_header;
 #endif
 
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
+	dev_init_buffers(dev);
 
 	/* New-style flags. */
 	dev->flags      = 0;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v2.1.19/linux/drivers/net/net_init.c	Sun Dec 22 16:37:34 1996
+++ linux/drivers/net/net_init.c	Thu Jan  2 15:13:25 1997
@@ -174,9 +174,9 @@
 	int i;
 	/* Fill in the fields of the device structure with ethernet-generic values.
 	   This should be in a common file instead of per-driver.  */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
 
+	dev_init_buffers(dev);
+	
 	/* register boot-defined "eth" devices */
 	if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {
 		i = simple_strtoul(dev->name + 3, NULL, 0);
@@ -217,16 +217,14 @@
 #ifdef CONFIG_FDDI
 
 void fddi_setup(struct device *dev)
-	{
-	int i;
-
+{
 	/*
 	 * Fill in the fields of the device structure with FDDI-generic values.
 	 * This should be in a common file instead of per-driver.
 	 */
-	for (i=0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
-
+	
+	dev_init_buffers(dev);
+	
 	dev->change_mtu			= fddi_change_mtu;
 	dev->hard_header		= fddi_header;
 	dev->rebuild_header		= fddi_rebuild_header;
@@ -247,7 +245,7 @@
 	dev->pa_mask	= 0;
 	dev->pa_alen	= 4;
 	return;
-	}
+}
 
 #endif
 
@@ -398,8 +396,7 @@
 #define MAX_TR_CARDS 16 /* same as the number of irq's in irq2dev[] */
 static struct device *trdev_index[MAX_TR_CARDS];
 
-struct device *
-init_trdev(struct device *dev, int sizeof_priv)
+struct device *init_trdev(struct device *dev, int sizeof_priv)
 {
 	int new_device = 0;
 	int i;
@@ -436,7 +433,7 @@
 		new_device = 1;
 	}
 
-	trfound:						/* From the double loop above. */
+trfound:						/* From the double loop above. */
 
 	for (i = 0; i < MAX_TR_CARDS; ++i)
 		if (trdev_index[i] == NULL) {
@@ -511,11 +508,9 @@
 int register_trdev(struct device *dev)
 {
 	unsigned long flags;
-	int i;
 	
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
-
+	dev_init_buffers(dev);
+	
 	save_flags(flags);
 
 	if (dev && dev->init) {
diff -u --recursive --new-file v2.1.19/linux/drivers/net/pi2.c linux/drivers/net/pi2.c
--- v2.1.19/linux/drivers/net/pi2.c	Mon Dec 30 15:39:09 1996
+++ linux/drivers/net/pi2.c	Thu Jan  2 15:13:25 1997
@@ -1400,9 +1400,9 @@
     dev->get_stats = pi_get_stats;
 
     /* Fill in the fields of the device structure */
-    for (i = 0; i < DEV_NUMBUFFS; i++)
-	skb_queue_head_init(&dev->buffs[i]);
 
+    dev_init_buffers(dev):
+    
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
     dev->hard_header    = ax25_encapsulate;
     dev->rebuild_header = ax25_rebuild_header;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.19/linux/drivers/net/ppp.c	Tue Dec 31 21:41:05 1996
+++ linux/drivers/net/ppp.c	Thu Jan  2 15:13:26 1997
@@ -402,9 +402,8 @@
 	dev->tx_queue_len     = 10;
 	dev->type	      = ARPHRD_PPP;
 
-	for (indx = 0; indx < DEV_NUMBUFFS; indx++)
-		skb_queue_head_init (&dev->buffs[indx]);
-
+	dev_init_buffers(dev);
+	
 	/* New-style flags */
 	dev->flags	= IFF_POINTOPOINT;
 	dev->family	= AF_INET;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/pt.c linux/drivers/net/pt.c
--- v2.1.19/linux/drivers/net/pt.c	Mon Dec 30 15:39:10 1996
+++ linux/drivers/net/pt.c	Thu Jan  2 15:13:26 1997
@@ -733,7 +733,6 @@
 {
     short ioaddr;
     struct pt_local *lp;
-    int i;
     unsigned long flags;
     unsigned long mem_ptr;
 
@@ -851,8 +850,7 @@
     dev->get_stats = pt_get_stats;
 
     /* Fill in the fields of the device structure */
-    for (i=0; i < DEV_NUMBUFFS; i++)
-        skb_queue_head_init(&dev->buffs[i]);
+    dev_init_buffers(dev);
 
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
     dev->hard_header    = ax25_encapsulate;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/scc.c linux/drivers/net/scc.c
--- v2.1.19/linux/drivers/net/scc.c	Wed Dec 18 15:58:47 1996
+++ linux/drivers/net/scc.c	Thu Jan  2 15:13:26 1997
@@ -229,8 +229,7 @@
 
 /* These provide interrupt save 2-step access to the Z8530 registers */
 
-static __inline__ unsigned char
-InReg(io_port port, unsigned char reg)
+extern __inline__ unsigned char InReg(io_port port, unsigned char reg)
 {
 	unsigned long flags;
 	unsigned char r;
@@ -250,8 +249,7 @@
 	return r;
 }
 
-static __inline__ void
-OutReg(io_port port, unsigned char reg, unsigned char val)
+extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val)
 {
 	unsigned long flags;
 	
@@ -267,28 +265,26 @@
 	restore_flags(flags);
 }
 
-static __inline__ void
-wr(struct scc_channel *scc, unsigned char reg, unsigned char val)
+extern __inline__ void wr(struct scc_channel *scc, unsigned char reg,
+	unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
 }
 
-static __inline__ void
-or(struct scc_channel *scc, unsigned char reg, unsigned char val)
+extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
 }
 
-static __inline__ void
-cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
 }
 
 #ifdef DISABLE_ALL_INTS
-static __inline__ void scc_cli(int irq)
+extern __inline__ void scc_cli(int irq)
 { cli(); }
-static __inline__ void scc_sti(int irq)
+extern __inline__ void scc_sti(int irq)
 { sti(); }
 #else
 static __inline__ void scc_cli(int irq)
@@ -302,20 +298,17 @@
 /* ******************************************************************** */
 
 
-static __inline__ void
-scc_lock_dev(struct scc_channel *scc)
+extern __inline__ void scc_lock_dev(struct scc_channel *scc)
 {
 	scc->dev->tbusy = 1;
 }
 
-static __inline__ void
-scc_unlock_dev(struct scc_channel *scc)
+extern __inline__ void scc_unlock_dev(struct scc_channel *scc)
 {
 	scc->dev->tbusy = 0;
 }
 
-static __inline__ void
-scc_discard_buffers(struct scc_channel *scc)
+extern __inline__ void scc_discard_buffers(struct scc_channel *scc)
 {
 	unsigned long flags;
 	
@@ -343,8 +336,7 @@
 
 /* ----> subroutines for the interrupt handlers <---- */
 
-static __inline__ void
-scc_notify(struct scc_channel *scc, int event)
+extern __inline__ void scc_notify(struct scc_channel *scc, int event)
 {
 	struct sk_buff *skb;
 	char *bp;
@@ -364,8 +356,7 @@
 		scc->stat.nospace++;
 }
 
-static __inline__ void
-flush_rx_FIFO(struct scc_channel *scc)
+extern __inline__ void flush_rx_FIFO(struct scc_channel *scc)
 {
 	int k;
 	
@@ -385,8 +376,7 @@
 /*       DCD/CTS and Rx/Tx errors					*/
 
 /* Transmitter interrupt handler */
-static __inline__ void
-scc_txint(struct scc_channel *scc)
+extern __inline__ void scc_txint(struct scc_channel *scc)
 {
 	struct sk_buff *skb;
 
@@ -450,8 +440,7 @@
 
 
 /* External/Status interrupt handler */
-static __inline__ void
-scc_exint(struct scc_channel *scc)
+extern __inline__ void scc_exint(struct scc_channel *scc)
 {
 	unsigned char status,changes,chg_and_stat;
 
@@ -532,8 +521,7 @@
 
 
 /* Receiver interrupt handler */
-static __inline__ void
-scc_rxint(struct scc_channel *scc)
+extern __inline__ void scc_rxint(struct scc_channel *scc)
 {
 	struct sk_buff *skb;
 
@@ -583,8 +571,7 @@
 
 
 /* Receive Special Condition interrupt handler */
-static __inline__ void
-scc_spint(struct scc_channel *scc)
+extern __inline__ void scc_spint(struct scc_channel *scc)
 {
 	unsigned char status;
 	struct sk_buff *skb;
@@ -630,8 +617,7 @@
 
 /* ----> interrupt service routine for the Z8530 <---- */
 
-static void
-scc_isr_dispatch(struct scc_channel *scc, int vector)
+static void scc_isr_dispatch(struct scc_channel *scc, int vector)
 {
 	switch (vector & VECTOR_MASK)
 	{
@@ -649,8 +635,7 @@
 
 #define SCC_IRQTIMEOUT 30000
 
-static void
-scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs)
 {
 	unsigned char vector;	
 	struct scc_channel *scc;
@@ -742,8 +727,7 @@
 
 /* ----> set SCC channel speed <---- */
 
-static __inline__ void 
-set_brg(struct scc_channel *scc, unsigned int tc)
+extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc)
 {
 	cl(scc,R14,BRENABL);		/* disable baudrate generator */
 	wr(scc,R12,tc & 255);		/* brg rate LOW */
@@ -751,8 +735,7 @@
 	or(scc,R14,BRENABL);		/* enable baudrate generator */
 }
 
-static __inline__ void 
-set_speed(struct scc_channel *scc)
+extern __inline__ void set_speed(struct scc_channel *scc)
 {
 	disable_irq(scc->irq);
 
@@ -765,8 +748,7 @@
 
 /* ----> initialize a SCC channel <---- */
 
-static __inline__ void 
-init_brg(struct scc_channel *scc)
+extern __inline__ void init_brg(struct scc_channel *scc)
 {
 	wr(scc, R14, BRSRC);				/* BRG source = PCLK */
 	OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]);	/* DPLL source = BRG */
@@ -818,8 +800,7 @@
  *
  */
  
-static void
-init_channel(struct scc_channel *scc)
+static void init_channel(struct scc_channel *scc)
 {
 	disable_irq(scc->irq);
 	
@@ -929,8 +910,7 @@
 /* ----> scc_key_trx sets the time constant for the baudrate 
          generator and keys the transmitter		     <---- */
 
-static void
-scc_key_trx(struct scc_channel *scc, char tx)
+static void scc_key_trx(struct scc_channel *scc, char tx)
 {
 	unsigned int time_const;
 		
@@ -1001,8 +981,7 @@
 
 /* ----> SCC timer interrupt handler and friends. <---- */
 
-static void 
-scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
 {
 	unsigned long flags;
 	
@@ -1027,8 +1006,7 @@
 	restore_flags(flags);	
 }
 
-static void
-scc_start_defer(struct scc_channel *scc)
+static void scc_start_defer(struct scc_channel *scc)
 {
 	unsigned long flags;
 	
@@ -1048,8 +1026,7 @@
 	restore_flags(flags);
 }
 
-static void
-scc_start_maxkeyup(struct scc_channel *scc)
+static void scc_start_maxkeyup(struct scc_channel *scc)
 {
 	unsigned long flags;
 	
@@ -1075,8 +1052,7 @@
  * Not exactly a timer function, but it is a close friend of the family...
  */
 
-static void
-scc_tx_done(struct scc_channel *scc)
+static void scc_tx_done(struct scc_channel *scc)
 {
 	/* 
 	 * trx remains keyed in fulldup mode 2 until t_idle expires.
@@ -1103,8 +1079,7 @@
 
 static unsigned char Rand = 17;
 
-static __inline__ int 
-is_grouped(struct scc_channel *scc)
+extern __inline__ int is_grouped(struct scc_channel *scc)
 {
 	int k;
 	struct scc_channel *scc2;
@@ -1140,8 +1115,7 @@
  * fulldup == 2:  mintime expired, reset status or key trx and start txdelay
  */
 
-static void 
-t_dwait(unsigned long channel)
+static void t_dwait(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
 	
@@ -1184,8 +1158,7 @@
  * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
  */
 
-static void 
-t_txdelay(unsigned long channel)
+static void t_txdelay(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
 
@@ -1206,8 +1179,7 @@
  * transmission after 'mintime' seconds
  */
 
-static void 
-t_tail(unsigned long channel)
+static void t_tail(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
 	unsigned long flags;
@@ -1243,8 +1215,7 @@
  * throw away send buffers if DCD remains active too long.
  */
 
-static void 
-t_busy(unsigned long channel)
+static void t_busy(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
 	unsigned long flags;
@@ -1272,8 +1243,7 @@
  * this is our watchdog.
  */
 
-static void 
-t_maxkeyup(unsigned long channel)
+static void t_maxkeyup(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
 	unsigned long flags;
@@ -1310,8 +1280,7 @@
  * expires.
  */
 
-static void 
-t_idle(unsigned long channel)
+static void t_idle(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
 	unsigned long flags;
@@ -1331,8 +1300,7 @@
 	scc->stat.tx_state = TXS_WAIT;
 }
 
-static void
-scc_init_timer(struct scc_channel *scc)
+static void scc_init_timer(struct scc_channel *scc)
 {
 	unsigned long flags;
 	
@@ -1356,8 +1324,7 @@
 
 #define CAST(x) (unsigned long)(x)
 
-static unsigned int
-scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
+static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
 {
 	int dcd;
 
@@ -1423,8 +1390,7 @@
 
 
  
-static unsigned long
-scc_get_param(struct scc_channel *scc, unsigned int cmd)
+static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
 {
 	switch (cmd)
 	{
@@ -1460,8 +1426,7 @@
  * Reset the Z8530s and setup special hardware
  */
 
-static void
-z8530_init(void)
+static void z8530_init(void)
 {
 	struct scc_channel *scc;
 	int chip, k;
@@ -1518,8 +1483,7 @@
  * Allocate device structure, err, instance, and register driver
  */
 
-static int 
-scc_net_setup(struct scc_channel *scc, unsigned char *name)
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name)
 {
 	unsigned char *buf;
 	struct device *dev;
@@ -1565,13 +1529,10 @@
 
 /* ----> Initialize device <----- */
 
-static int 
-scc_net_init(struct device *dev)
+static int scc_net_init(struct device *dev)
 {
-	int k;
-
-	for (k=0; k < DEV_NUMBUFFS; k++)
-		skb_queue_head_init(&dev->buffs[k]);
+	dev_init_buffers(dev);
+	
 	dev->tx_queue_len    = 16;	/* should be enough... */
 
 	dev->open            = scc_net_open;
@@ -1604,8 +1565,7 @@
 
 /* ----> open network device <---- */
 
-static int 
-scc_net_open(struct device *dev)
+static int scc_net_open(struct device *dev)
 {
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 
@@ -1630,8 +1590,7 @@
 
 /* ----> close network device <---- */
 
-static int 
-scc_net_close(struct device *dev)
+static int scc_net_close(struct device *dev)
 {
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	unsigned long flags;
@@ -1663,8 +1622,7 @@
 
 /* ----> receive frame, called from scc_rxint() <---- */
 
-static void 
-scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
 {
 	if (skb->len == 0)
 	{
@@ -1684,8 +1642,7 @@
 
 /* ----> transmit frame <---- */
 
-static int 
-scc_net_tx(struct sk_buff *skb, struct device *dev)
+static int scc_net_tx(struct sk_buff *skb, struct device *dev)
 {
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	unsigned long flags;
@@ -1783,8 +1740,7 @@
  * SIOCSCCGSTAT		- get driver status	arg: (struct scc_stat *) arg
  */
 
-static int
-scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {
 	struct scc_kiss_cmd kiss_cmd;
 	struct scc_mem_config memcfg;
@@ -2006,8 +1962,7 @@
 
 /* ----> set interface callsign <---- */
 
-static int 
-scc_net_set_mac_address(struct device *dev, void *addr)
+static int scc_net_set_mac_address(struct device *dev, void *addr)
 {
 	struct sockaddr *sa = (struct sockaddr *) addr;
 	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
@@ -2016,25 +1971,22 @@
 
 /* ----> rebuild header <---- */
 
-static int 
-scc_net_rebuild_header(struct sk_buff *skb)
+static int scc_net_rebuild_header(struct sk_buff *skb)
 {
     return ax25_rebuild_header(skb);
 }
 
 /* ----> "hard" header <---- */
 
-static int 
-scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, 
-	       void *daddr, void *saddr, unsigned len)
+static int  scc_net_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len)
 {
     return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
 }
 
 /* ----> get statistics <---- */
 
-static struct enet_statistics *
-scc_net_get_stats(struct device *dev)
+static struct enet_statistics *scc_net_get_stats(struct device *dev)
 {
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	
@@ -2054,8 +2006,7 @@
 /* ******************************************************************** */
 
 
-static int 
-scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	struct scc_channel *scc;
 	struct scc_kiss *kiss;
@@ -2161,10 +2112,11 @@
 }
 
 #ifdef CONFIG_PROC_FS
+
 struct proc_dir_entry scc_proc_dir_entry = 
 { 
-  PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, 
-  &proc_net_inode_operations, scc_net_get_info 
+	PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, 
+	&proc_net_inode_operations, scc_net_get_info 
 };
 
 #define scc_net_procfs_init()   proc_net_register(&scc_proc_dir_entry);
diff -u --recursive --new-file v2.1.19/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v2.1.19/linux/drivers/net/sdla.c	Tue Nov 19 15:53:56 1996
+++ linux/drivers/net/sdla.c	Thu Jan  2 15:13:26 1997
@@ -1636,7 +1636,6 @@
 int sdla_init(struct device *dev)
 {
    struct frad_local *flp;
-   int               i;
 
    /* allocate the private data structure */
    flp = kmalloc(sizeof(struct frad_local), GFP_KERNEL);
@@ -1666,9 +1665,8 @@
    dev->addr_len        = 0;
    dev->mtu             = SDLA_MAX_MTU;
 
-   for (i = 0; i < DEV_NUMBUFFS; i++) 
-      skb_queue_head_init(&dev->buffs[i]);
-
+   dev_init_buffers(dev);
+   
    flp->activate        = sdla_activate;
    flp->deactivate      = sdla_deactivate;
    flp->assoc           = sdla_assoc;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.1.19/linux/drivers/net/shaper.c	Thu Dec 12 19:37:06 1996
+++ linux/drivers/net/shaper.c	Thu Jan  2 15:13:26 1997
@@ -547,8 +547,6 @@
  
 int shaper_probe(struct device *dev)
 {
-	int i;
-	
 	/*
 	 *	Set up the shaper.
 	 */
@@ -567,8 +565,7 @@
 	 *	Intialise the packet queues
 	 */
 	 
-	for(i=0;i<DEV_NUMBUFFS;i++)
-		skb_queue_head_init(&dev->buffs[i]);
+	dev_init_buffers(dev);
 	
 	/*
 	 *	Handlers for when we attach to a device.
diff -u --recursive --new-file v2.1.19/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.19/linux/drivers/net/slip.c	Tue Dec 31 21:41:06 1996
+++ linux/drivers/net/slip.c	Thu Jan  2 15:13:26 1997
@@ -1143,12 +1143,11 @@
       }
 
 
-/* Initialize the SLIP driver.  Called by DDI. */
-int
-slip_init(struct device *dev)
+/* Initialise the SLIP driver.  Called by the device init code */
+
+int slip_init(struct device *dev)
 {
 	struct slip *sl = (struct slip*)(dev->priv);
-	int i;
 
 	if (sl == NULL)		/* Allocation failed ?? */
 	  return -ENODEV;
@@ -1159,7 +1158,10 @@
 	sl->magic  = SLIP_MAGIC;
 	sl->dev	   = dev;
 
-	/* Finish setting up the DEVICE info. */
+	/*
+	 *	Finish setting up the DEVICE info. 
+	 */
+	 
 	dev->mtu		= SL_MTU;
 	dev->hard_start_xmit	= sl_xmit;
 	dev->open		= sl_open_dev;
@@ -1170,10 +1172,8 @@
 	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
 	dev->tx_queue_len	= 10;
 
-	for (i = 0; i < DEV_NUMBUFFS; i++)  {
-		skb_queue_head_init(&dev->buffs[i]);
-	}
-
+	dev_init_buffers(dev);
+	
 	/* New-style flags. */
 	dev->flags		= IFF_NOARP|IFF_MULTICAST;
 	dev->family		= AF_INET;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v2.1.19/linux/drivers/net/strip.c	Tue Oct 29 19:58:12 1996
+++ linux/drivers/net/strip.c	Thu Jan  2 15:13:26 1997
@@ -2414,8 +2414,6 @@
 
 static int strip_dev_init(struct device *dev)
 {
-    int i;
-
     /*
      * Finish setting up the DEVICE info.
      */
@@ -2446,8 +2444,7 @@
      * Pointer to the interface buffers.
      */
 
-    for (i = 0; i < DEV_NUMBUFFS; i++)
-        skb_queue_head_init(&dev->buffs[i]);
+   dev_init_buffers(dev);
 
     /*
      * Pointers to interface service routines.
diff -u --recursive --new-file v2.1.19/linux/drivers/net/tunnel.c linux/drivers/net/tunnel.c
--- v2.1.19/linux/drivers/net/tunnel.c	Thu Dec 12 19:37:06 1996
+++ linux/drivers/net/tunnel.c	Thu Jan  2 15:13:26 1997
@@ -215,8 +215,6 @@
  
 int tunnel_init(struct device *dev)
 {
-	int i;
-
 	/* Oh, just say we're here, in case anyone cares */
 	static int tun_msg=0;
 	if(!tun_msg)
@@ -236,8 +234,8 @@
 	memset(dev->priv, 0, sizeof(struct enet_statistics));
 
 	/* Initialize the tunnel device structure */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
+	
+	dev_init_buffers(dev);
 
 	dev->hard_header	= NULL;
 	dev->rebuild_header 	= NULL;
diff -u --recursive --new-file v2.1.19/linux/drivers/net/wic.c linux/drivers/net/wic.c
--- v2.1.19/linux/drivers/net/wic.c	Tue Nov 19 15:53:56 1996
+++ linux/drivers/net/wic.c	Thu Jan  2 15:13:26 1997
@@ -77,8 +77,7 @@
 void wic_interrupt(int irq, void *dev_ptr, struct pt_regs *regs);
 
 /* Functions for DEV methods */
-int wic_rebuild_header(void *buff, struct device *dev,
-			       unsigned long raddr, struct sk_buff *skb);
+int wic_rebuild_header(struct sk_buff *skb);
 int wic_tx_packet(struct sk_buff *skb, struct device *dev);
 int wic_open(struct device *dev);
 int wic_close(struct device *dev);
@@ -155,14 +154,13 @@
 	enum wic_connection_state connection;
 	unsigned short timeout_count;
 	char is_deferred;
-	int (*orig_rebuild_header)(void *eth, struct device *dev,
-				   unsigned long raddr, struct sk_buff *skb);
+	int (*orig_rebuild_header)(struct sk_buff *skb);
 };
 
 /* Entry point of WIC driver.
    Probe the hardware, and register/initialize the driver. */
-int
-wic_init(struct device *dev)
+
+int wic_init(struct device *dev)
 {
 	struct net_local *nl;
 	struct wicconf wc;
@@ -266,8 +264,7 @@
 /* Bottom half handler for the delayed request.
    This routine is kicked by do_timer().
    Request `wic_bh' to be invoked. */
-void
-wic_kick_bh(struct device *dev)
+void wic_kick_bh(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 
@@ -309,8 +306,7 @@
 	wic_error
 };
 
-void 
-wic_set_multicast_list(struct device *dev)
+void wic_set_multicast_list(struct device *dev)
 {
 	struct wicconf wc;
 	struct wic_net *wn;
@@ -358,8 +354,7 @@
 }
 
 /* Bottom half handler of WIC. */
-void
-wic_bh(struct device *dev)
+void wic_bh(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct wic_local *snd = &nl->snd_data;
@@ -376,8 +371,7 @@
 	}
 }
 
-int
-wic_bh_timeout_error(struct device *dev, struct net_local *nl,
+int wic_bh_timeout_error(struct device *dev, struct net_local *nl,
 		      struct wic_local *snd, struct wic_local *rcv,
 		      int error)
 {
@@ -423,13 +417,11 @@
 	}
 	rcv->state = WIC_PK_DONE;
 	if (rcv->skb) {
-		rcv->skb->free = 1;
 		kfree_skb(rcv->skb, FREE_READ);
 		rcv->skb = NULL;
 	}
 	snd->state = WIC_PK_DONE;
 	if (snd->skb) {
-		snd->skb->free = 1;
 		dev_kfree_skb(snd->skb, FREE_WRITE);
 		snd->skb = NULL;
 	}
@@ -446,8 +438,7 @@
 	return TIMEOUT;
 }
 
-int
-wic_none(struct device *dev, struct net_local *nl,
+int wic_none(struct device *dev, struct net_local *nl,
 	  struct wic_local *snd, struct wic_local *rcv)
 {
 	return OK;
@@ -455,11 +446,10 @@
 
 /* WIC_RECEIVE --- receive a byte(two nibbles)
    Returns OK on success, TIMEOUT on timeout */
-inline int
-wic_receive(unsigned short nibble_timeout, unsigned short status_addr,
-	     enum wic_nibble_state *ns_p, unsigned char *data_p)
+extern inline int wic_receive(unsigned short nibble_timeout, 
+	unsigned short status_addr, enum wic_nibble_state *ns_p, unsigned char *data_p)
 {
-unsigned int cx;
+	unsigned int cx;
 
 	cx = LOOPCNT;
 	while ((inb(status_addr) & 0x08) != ((tog<<3) & 0x08)) {
@@ -474,8 +464,8 @@
 }
 
 /* WIC_RECEIVE_PACKET --- receive a packet */
-int
-wic_receive_packet(struct device *dev, struct net_local *nl,
+
+int wic_receive_packet(struct device *dev, struct net_local *nl,
 		    struct wic_local *snd, struct wic_local *rcv)
 {
 	unsigned short status_addr = PAR_STATUS(dev);
@@ -637,11 +627,11 @@
 
 /* WIC_SEND --- send a byte (two nibbles) 
    Returns OK on success, TIMEOUT when timeout    */
-inline int
-wic_send(unsigned short nibble_timeout, unsigned short data_addr,
-	  enum wic_nibble_state *ns_p, unsigned char data)
+extern inline int wic_send(unsigned short nibble_timeout, 
+	unsigned short data_addr, enum wic_nibble_state *ns_p, 
+	unsigned char data)
 {
-unsigned int cx;
+	unsigned int cx;
 
 	cx = LOOPCNT;
 	while ((inb(data_addr+1) & 0x80) == ((tog<<7) & 0x80)) {
@@ -656,8 +646,7 @@
 }
 
 /* WIC_SEND_PACKET --- send a packet */
-int
-wic_send_packet(struct device *dev, struct net_local *nl,
+int wic_send_packet(struct device *dev, struct net_local *nl,
 		 struct wic_local *snd, struct wic_local *rcv)
 {
 	unsigned short data_addr = PAR_DATA(dev);
@@ -819,11 +808,10 @@
 	return OK;
 }
 
-int
-wic_connection_close(struct device *dev, struct net_local *nl,
+int wic_connection_close(struct device *dev, struct net_local *nl,
 		      struct wic_local *snd, struct wic_local *rcv)
 {
-unsigned long flags;
+	unsigned long flags;
 
 	save_flags(flags);
 	cli();
@@ -837,8 +825,7 @@
 }
 
 /* WIC_ERROR --- wait till other end settled */
-int
-wic_error(struct device *dev, struct net_local *nl,
+int wic_error(struct device *dev, struct net_local *nl,
 	   struct wic_local *snd, struct wic_local *rcv)
 {
 	unsigned char status;
@@ -863,8 +850,7 @@
 }
 
 /* Handle the parallel port interrupts. */
-void
-wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs)
+void wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs)
 {
 	struct device *dev = (struct device *) irq2dev_map[irq];
 	struct net_local *nl = (struct net_local *)dev->priv;
@@ -916,16 +902,15 @@
 	}
 }
 
-int
-wic_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-		    struct sk_buff *skb)
+int wic_rebuild_header(struct sk_buff *skb)
 {
+	struct device *dev = skb->dev;
 	struct net_local *nl = (struct net_local *)dev->priv;
-	struct ethhdr *eth = (struct ethhdr *)buff;
+	struct ethhdr *eth = (struct ethhdr *)skb->data;
 	int i;
 
 	if ((dev->flags & IFF_NOARP)==0)
-		return nl->orig_rebuild_header(buff, dev, dst, skb);
+		return nl->orig_rebuild_header(skb);
 
 	if (eth->h_proto != htons(ETH_P_IP)) {
 		printk("wic_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
@@ -939,8 +924,7 @@
 	return 0;
 }
 
-int
-wic_tx_packet(struct sk_buff *skb, struct device *dev)
+int wic_tx_packet(struct sk_buff *skb, struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct wic_local *snd = &nl->snd_data;
@@ -994,8 +978,8 @@
    This routine gets exclusive access to the parallel port by allocating
    its IRQ line.
  */
-int
-wic_open(struct device *dev)
+ 
+int wic_open(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	unsigned long flags;
@@ -1031,8 +1015,7 @@
 }
 
 /* The inverse routine to wic_open (). */
-int
-wic_close(struct device *dev)
+int wic_close(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct wic_local *snd = &nl->snd_data;
@@ -1050,13 +1033,11 @@
 
 	snd->state = WIC_PK_DONE;
 	if (snd->skb) {
-		snd->skb->free = 1;
 		dev_kfree_skb(snd->skb, FREE_WRITE);
 		snd->skb = NULL;
 	}
 	rcv->state = WIC_PK_DONE;
 	if (rcv->skb) {
-		rcv->skb->free = 1;
 		kfree_skb(rcv->skb, FREE_READ);
 		rcv->skb = NULL;
 	}
diff -u --recursive --new-file v2.1.19/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v2.1.19/linux/drivers/sbus/char/sunserial.c	Wed Dec 18 15:58:50 1996
+++ linux/drivers/sbus/char/sunserial.c	Wed Jan  1 16:24:40 1997
@@ -1,8 +1,9 @@
-/* $Id: sunserial.c,v 1.24 1996/11/21 16:57:56 jj Exp $
+/* $Id: sunserial.c,v 1.25 1996/12/30 07:50:26 davem Exp $
  * serial.c: Serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
  */
 
 #include <linux/errno.h>
@@ -489,6 +490,8 @@
 
 static _INLINE_ void transmit_chars(struct sun_serial *info)
 {
+	struct tty_struct *tty = info->tty;
+
 	if (info->x_char) {
 		/* Send next char */
 		zs_put_char(info->zs_channel, info->x_char);
@@ -496,7 +499,7 @@
 		goto clear_and_return;
 	}
 
-	if((info->xmit_cnt <= 0) || info->tty->stopped) {
+	if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
 		/* That's peculiar... */
 		info->zs_channel->control = RES_Tx_P;
 		udelay(5);
@@ -666,6 +669,10 @@
 	tty = info->tty;
 	if (!tty)
 		return;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("do_serial_hangup<%p: tty-%d\n",
+		__builtin_return_address(0), info->line);
+#endif
 
 	tty_hangup(tty);
 }
@@ -700,7 +707,7 @@
 	save_flags(flags); cli();
 
 #ifdef SERIAL_DEBUG_OPEN
-	printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+	printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
 #endif
 
 	/*
@@ -820,10 +827,7 @@
 		 */
 		i = B9600;
 	}
-	if (i == 0) {
-		/* XXX B0, hangup the line. */
-		do_serial_hangup(info);
-	} else if (baud_table[i]) {
+	if (baud_table[i]) {
 		info->zs_baud = baud_table[i];
 		info->clk_divisor = 16;
 
@@ -833,6 +837,8 @@
 		info->curregs[12] = (brg & 255);
 		info->curregs[13] = ((brg >> 8) & 255);
 		info->curregs[14] = BRSRC | BRENAB;
+	/* } else { */
+	/* XXX */
 	}
 
 	/* byte size and parity */
@@ -1405,7 +1411,7 @@
 	}
 	
 #ifdef SERIAL_DEBUG_OPEN
-	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+	printk("rs_close tty-%d, count = %d\n", info->line, info->count);
 #endif
 	if ((tty->count == 1) && (info->count != 1)) {
 		/*
@@ -1484,6 +1490,9 @@
 	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
 			 ZILOG_CLOSING);
 	wake_up_interruptible(&info->close_wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close tty-%d exiting, count = %d\n", info->line, info->count);
+#endif
 	restore_flags(flags);
 }
 
@@ -1497,6 +1506,11 @@
 	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
 		return;
 	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_hangup<%p: tty-%d, count = %d bye\n",
+		__builtin_return_address(0), info->line, info->count);
+#endif
+
 	rs_flush_buffer(tty);
 	shutdown(info);
 	info->event = 0;
@@ -1517,6 +1531,7 @@
 	struct wait_queue wait = { current, NULL };
 	int		retval;
 	int		do_clocal = 0;
+	unsigned char	r0;
 
 	/*
 	 * If the device is in the middle of being closed, then block
@@ -1599,6 +1614,10 @@
 		current->state = TASK_INTERRUPTIBLE;
 		if (tty_hung_up_p(filp) ||
 		    !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("block_til_ready hup-ed: ttys%d, count = %d\n",
+				info->line, info->count);
+#endif
 #ifdef SERIAL_DO_RESTART
 			if (info->flags & ZILOG_HUP_NOTIFY)
 				retval = -EAGAIN;
@@ -1610,9 +1629,12 @@
 			break;
 		}
 
+		cli();
+		r0 = read_zsreg(info->zs_channel, R0);
+		sti();
 		if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
 		    !(info->flags & ZILOG_CLOSING) &&
-		    (do_clocal || (DCD & read_zsreg(info->zs_channel, R0))))
+		    (do_clocal || (DCD & r0)))
 			break;
 		if (current->signal & ~current->blocked) {
 			retval = -ERESTARTSYS;
@@ -1668,6 +1690,11 @@
 	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
 	       info->count);
 #endif
+	if (info->tty != 0 && info->tty != tty) {
+		/* Never happen? */
+		printk("rs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
+		return -EBUSY;
+	}
 	info->count++;
 	tty->driver_data = info;
 	info->tty = tty;
@@ -1709,7 +1736,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.24 $";
+	char *revision = "$Revision: 1.25 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.19/linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue Nov 12 15:56:10 1996
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue Dec 31 21:13:09 1996
@@ -1,3 +1,55 @@
+Thu Dec 26 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16b
+	- Remove useless code.
+	- Try to improve error recovery in case of abort and reset.
+	- Remove DEBUG_NEGO by default.
+	- Add boot setup command support.
+	  Now, all experimental config options can be removed.
+	- Update README file.
+
+
+Mon Dec 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c ncr53c8xx.h - revision 1.16a
+	New display for speed ##.# MB/s (From Stefan)
+	- I add "WIDE" qualifier after ULTRA and FAST
+	- I get "FAST WIDE SCSI-2 20 MB/s" with my Atlas. That's nice.
+
+	Richard Waltham reports SYMBIOS set the 875 to 20 MB/s for 13 ns 
+	period factor. I decide to trust SYMBIOS. 20 MB/s output speed 
+	instead of 19.2 MB/s should not cause problem. The ncr is only able 
+	to use 16.67 MB/s when 20 MB/s is not possible.
+
+	Fix from Markus Kossman: "Ultra SCSI enabled" wrongly printed 
+	when not enabled.
+
+	Set DEBUG_NEGO by default in order to get reports about sync nego. 
+	Will remove it in the next patch.
+
+Thu Dec 19 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16
+	Incorporate new definitions in ncr53c8xx.h (From Stefan).
+	Check changes against Stefan's current version of the driver.
+	All seems ok.
+
+Sat Nov 30 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c ncr53c8xx.h
+	Make changes in order to support: 
+	- Clock doubler and so 80 Mhz scsi clock for 875 chips.
+	- Sync transfers below 7.5 MB/sec.
+          Use Clock/2 between 5 and 10 Mega-transfers/s and Clock/4 below 5.
+	- Ultra SCSI data transfers.
+	- Offset 16.
+
+	Works with my configuration. However I cannot test Ultra transfers,
+	since my disks are only fast scsi-2.
+
+Tue Nov 28 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c
+	I received yesterday my Promise SCSI Ultra board.
+	NCR53C875 rev. 3 with clock doubler.
+	Add the code to support some bus features, the large 536 dma fifo and 
+	burst 128. Works.
+
 Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
 	* ncr53c8xx.c ncr53c8xx.h - revision 1.14c
 	Severall control command improvements:
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.19/linux/drivers/scsi/Config.in	Thu Dec 12 19:37:07 1996
+++ linux/drivers/scsi/Config.in	Tue Dec 31 21:13:09 1996
@@ -61,11 +61,6 @@
       bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
     fi
   fi
-  if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    bool '  disable master parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-    bool '  disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-    bool '  force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-  fi
 fi
 if [ "$CONFIG_MCA" = "y" ]; then
   bool 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.19/linux/drivers/scsi/README.ncr53c8xx	Thu Dec 12 19:37:07 1996
+++ linux/drivers/scsi/README.ncr53c8xx	Tue Dec 31 21:13:09 1996
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-05 November 1996
+29 December 1996
 ===============================================================================
 
 1.  Introduction
@@ -23,13 +23,18 @@
       8.6  Clear profile counters
       8.7  Set flag (no_sync)
 9.  Configuration parameters
-10. Some constants and flags of the ncr53c8xx.h header files
-11. Provided files
-12. Installation procedure
+10. Boot setup commands
+      10.1 Syntax
+      10.2 Available arguments
+      10.3 Advised boot setup commands
+11. Some constants and flags of the ncr53c8xx.h header file
+12. Installation
+      12.1 Provided files
+      12.2 Installation procedure
 13. Control commands under linux-1.2.13
 14. Known problems
       14.1 Tagged commands with Iomega Jaz device
-15. SCSI problems solving
+15. SCSI problem troubleshooting
 
 ===============================================================================
 
@@ -81,13 +86,13 @@
 810A       N         N        N            Y             Y
 815        Y         N        N            Y             Y
 825        Y         Y        N            Y             Y
-825A       Y         Y        N            Y             Not yet
+825A       Y         Y        N            Y             Y
 860        N         N        Y(1)         Y             Y
 875        Y         Y        Y(1)         Y             Y
 
-(1) Ultra SCSI extensions will be supported in a future release of the 
-    driver.
-
+(1) Ultra (fast20 / fast40) SCSI data transfers are "in theory" supported 
+    by the driver, but not fully tested, since the maintainer does not have 
+    for the moment Ultra SCSI hard disks.
 
 3. Summary of other supported features.
 
@@ -98,6 +103,7 @@
         Debugging information: written to syslog (expert only)
 	Scatter / gather
 	Shared interrupt
+        Boot setup commands
 
 
 4. Memory mapped I/O versus normal I/O
@@ -259,21 +265,8 @@
 be wrong.
 
 In the example above, we got 18038 interrupts "on the fly" and only
-1673 script breaks probably due to disconnections inside a segment of
-the scatter list.  This is an excellent result due to the fact that
-the driver tries to use small data segments (512) for the scatter
-list. The CPU load of this rescatter process is acceptable. Unlike
-other SCSI processors, NCR53C8XX controllers do not need large data
-chunks in order to get better performance, and it seems that it is
-just the opposite.  The scatter/gather algorithm of the middle SCSI
-driver is not optimal for NCR SCSI processors and should be tunable
-according to host type.
-
-You can tune the "wished" segment size for the scatterlist by changing
-the following "define" in the file ncr53c8xx.h.  Use only power of 2
-greater than 512 (1024, 2048 or 4096).
-
-SCSI_NCR_SEGMENT_SIZE		(default: 512)
+1673 script breaks generally due to disconnections inside a segment 
+of the scatter list.
 
 
 8. Control commands
@@ -407,19 +400,138 @@
     even while performing long SCSI operations.
 
 
-10. Some constants and flags of the ncr53c8xx.h header files
+10. Boot setup commands
+
+10.1 Syntax
+
+Setup commands can be passed to the driver at boot time.
+A boot setup command for the ncr53c8xx driver begins with the driver name 
+"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of
+integers separated with comma followed by an optionnal list of  comma- 
+separated strings. Example of boot setup command under lilo prompt:
+
+lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200
+
+- enable tagged commands, up to 4 tagged commands queued.
+- set synchronous negotiation speed to 10 Mega-transfers / second.
+- set DEBUG_NEGO flag.
+
+For the moment, the integer list of arguments is disgarded by the driver. 
+It will be used in the future in order to allow a per controller setup.
+
+Each string argument must be specified as "keyword:value". Only lower-case 
+characters and digits are allowed.
+
+10.2 Available arguments
+
+Master parity checking
+      mpar:y     enabled
+      mpar:n     disabled
+
+Scsi parity checking
+      spar:y     enabled
+      spar:n     disabled
+
+Scsi disconnections
+      disc:y     enabled
+      disc:n     disabled
+ 
+Special features
+   Only apply to 810A, 825A, 860 and 875 controllers.
+   Have no effect with normal 810 and 825.
+      specf:y    enabled
+      specf:n    disabled
+
+Ultra SCSI support
+   Only apply to 860 and 875 controllers.
+   Have no effect with other ones.
+      ultra:y    enabled
+      ultra:n    disabled
+
+Number of tagged commands
+      tags:0     (or tags:1 ) tagged command queuing disabled
+      tags:#tags (#tags  > 1) tagged command queuing enabled
+  #tags will be truncated to the max queued commands configuration parameter.
+  If the driver is configured with a maximum of 4 queued commands, tags:4 is 
+  the right argument to specify.
+
+Default synchronous negotiation frequency
+      sync:0     disabled (asynchronous transfer mode)
+      sync:#MHz  enabled up to #MHz.
+  #MHz > 10     Ultra SCSI
+  #MHz >  5     Fast  SCSI
+  In all cases, the driver will use the minimum transfer period supported by 
+  controllers according to NCR53C8XX chip type.
+
+Negotiate synchronous with all devices
+  (force sync nego)
+      fsn:y      enabled
+      fsn:n      disabled
+
+Verbosity level
+      verb:0     minimal
+      verb:1     normal
+      verb:2     too much
+
+Debug mode
+      debug:0	clear debug flags
+      debug:#x   set debug flags
+  #x is an integer value combining the following power-of-2 values:
+  DEBUG_ALLOC       0x1
+  DEBUG_PHASE       0x2
+  DEBUG_POLL        0x4
+  DEBUG_QUEUE       0x8
+  DEBUG_RESULT     0x10
+  DEBUG_SCATTER    0x20
+  DEBUG_SCRIPT     0x40
+  DEBUG_TINY       0x80
+  DEBUG_TIMING    0x100
+  DEBUG_NEGO      0x200
+  DEBUG_TAGS      0x400
+  DEBUG_FREEZE    0x800
+  DEBUG_RESTART  0x1000
+
+You can play safely with DEBUG_NEGO. However, some of these flags may 
+generate bunches of syslog messages. 
+
+10.3 Advised boot setup commands
+
+If the driver has been configured with default options, the equivalent 
+boot setup is:
+    ncr53c8xx=mpar:y,spar:y,disc:y,fsn:n,specf:y,ultra:y,sync:5,tags:0,verb:1
+
+For an installation diskette or a safe but not fast system, boot setup is:
+    ncr53c8xx=mpar:y,spar:y,disc:n,fsn:n,specf:n,ultra:n,sync:0,tags:0
+
+My personnal system works flawlessly with the following setup:
+    ncr53c8xx=mpar:y,spar:y,disc:y,fsn:n,specf:y,ultra:y,sync:20,tags:8
+
+The driver prints its actual setup when verbosity level is 2. You can try 
+"ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" 
+to your boot setup command in order to check the actual setup the drive use.
+
+
+11. Some constants and flags of the ncr53c8xx.h header file
 
 Some of these are defined from the configuration parameters.  To
 change other "defines", you must edit the header file.  Do that only
 if you know what you are doing.
 
-SCSI_NCR_TRUST_BIOS_SETTING	(default: not defined)
-	If defined, the driver will preserve features bits from  
-	dmode/dcntl/ctest3/ctest4 io registers.
+SCSI_NCR_SETUP_ULTRA_SUPPORT	(default: defined)
+	Ultra SCSI support.
+	Can be changed by the following boot setup command:
+		ncr53c8xx=ultra:n
 
-SCSI_NCR_SPECIAL_FEATURES	(default: not defined)
+SCSI_NCR_SETUP_SPECIAL_FEATURES	(default: defined)
 	If defined, the driver will enable some special features according  
 	to chip and revision id.
+        For 810A, 860, 825A and 875 scsi chips, this option enables support 
+        of features that reduce load of PCI bus and memory accesses during  
+        scsi transfer processing: burst op-code fetch, read multiple, 
+        read line, prefetch, cache line line, write and invalidate, 
+        burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only).
+	Can be changed by the following boot setup command:
+		ncr53c8xx=specf:n
 
 SCSI_NCR_IOMAPPED		(default: not defined)
 	If defined, normal I/O is forced.
@@ -431,12 +543,12 @@
 	Maximum number of simultaneous tagged commands to a device.
 	Can be changed by "settags <target> <maxtags>"
 
-SCSI_NCR_DEFAULT_SYNC           (default: 5)
+SCSI_NCR_SETUP_DEFAULT_SYNC     (default: 5)
 	Frequency in KHz the driver will use at boot time for synchronous 
 	negotiation. 0 means asynchronous.
 	Can be changed by "setsync <target> <period>"
 
-SCSI_NCR_DEFAULT_TAGS		(default: 4)
+SCSI_NCR_SETUP_DEFAULT_TAGS     (default: 4)
 	Default number of simultaneous tagged commands to a device.
 	< 1 means tagged command queuing disabled at start-up.
 
@@ -444,18 +556,18 @@
 	Use SIMPLE TAG for read and write commands.
 	Can be changed by "setorder <ordered|simple|default>"
 
-SCSI_NCR_NO_DISCONNECT		(default: not defined)
-	If defined, targets are not allowed to disconnect.
+SCSI_NCR_SETUP_DISCONNECTION	(default: defined)
+	If defined, targets are allowed to disconnect.
 
-SCSI_NCR_FORCE_SYNC_NEGO	(default: not defined)
+SCSI_NCR_SETUP_FORCE_SYNC_NEGO	(default: not defined)
 	If defined, synchronous negotiation is tried for all SCSI-2 devices.
 	Can be changed by "setsync <target> <period>"
 
-SCSI_NCR_DISABLE_MPARITY_CHECK	(default: not defined)
-	If defined, master parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY	(default: defined)
+	If defined, master parity checking is enabled.
 
-SCSI_NCR_DISABLE_PARITY_CHECK	(default: not defined)
-	If defined, SCSI parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY	(default: defined)
+	If defined, SCSI parity checking is enabled.
 
 SCSI_NCR_PROFILE		(default: defined)
 	If defined, profiling information is gathered.
@@ -463,11 +575,6 @@
 SCSI_NCR_MAX_SCATTER		(default: 128)
 	Scatter list size of the driver ccb.
 
-SCSI_NCR_SEGMENT_SIZE		(default: 512)
-	If defined, the driver will try to use scatter segments of this size.
-	If not defined, the Linux scatter list is used as is.
-	Not defined by default.
-
 SCSI_NCR_MAX_TARGET		(default: 16)
 	Max number of targets per host.
 
@@ -494,8 +601,9 @@
 SCSI_NCR_MAX_LUN	(default: 8)
 	Max number of LUNs per target.
 
+12. Installation
 
-11. Provided files
+12.1 Provided files
 
 Driver and common files:
 
@@ -509,20 +617,19 @@
 
 	Install.ncr53c8xx	: installation script
 
-	Patch-1.2.13.ncr53c8xx     : patch for linux-1.2.13
-	Patch-2.0.22-25.ncr53c8xx  : patch for linux-2.0.22 to linux-2.0.25
+	Patch-1.2.13.ncr53c8xx  : patch for linux-1.2.13
+	Patch-2.0.27.ncr53c8xx  : patch for linux-2.0.27
 
 Prior to installing the driver, you must untar the distribution, as follow:
 
 	mkdir ncrB2L
 	cd ncrB2L
-	tar zxvf ncrBsd2Linux-1.14c-src.tar.gz
+	tar zxvf ncrBsd2Linux-1.16b-src.tar.gz
 
 
-12. Installation procedure
+12.2 Installation procedure
 
-This install script has been tested with linux-1.2.13 and linux-2.0.22
-to 2.0.25. It will probably work with linux 2.0.X (X>25).
+This install script has been tested with linux-1.2.13 and 2.0.27.
 
 This procedure copies the new driver files to the kernel tree and
 applies a patch to some files of the kernel tree.
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v2.1.19/linux/drivers/scsi/README.st	Mon Jul  1 07:08:27 1996
+++ linux/drivers/scsi/README.st	Wed Jan  1 18:54:02 1997
@@ -2,7 +2,7 @@
 The driver is currently maintained by Kai M{kisara (email
 Kai.Makisara@metla.fi)
 
-Last modified: Sun Jun 30 15:47:14 1996 by root@kai.makisara.fi
+Last modified: Wed Jan  1 15:44:49 1997 by makisara@kai.makisara.fi
 
 
 BASICS
@@ -78,6 +78,17 @@
 The compile options are defined in the file linux/drivers/scsi/st_options.h.
 
 
+BSD AND SYS V SEMANTICS
+
+The user can choose between these two behaviours of the tape driver by
+defining the value of the symbol ST_SYSV. The semantics differ when a
+file being read is closed. The BSD semantics leaves the tape where it
+currently is whereas the SYS V semantics moves the tape past the next
+filemark unless the filemark has just been crossed.
+
+The default is BSD semantics.
+
+
 BUFFERING
 
 The driver uses tape buffers allocated either at system initialization
@@ -186,7 +197,11 @@
 MTSETDENSITY Sets the drive density code to arg. See drive
         documentation for available codes.
 MTLOCK and MTUNLOCK Explicitly lock/unlock the tape drive door.
-MTLOAD and MTUNLOAD Explicitly load and unload the tape.
+MTLOAD and MTUNLOAD Explicitly load and unload the tape. If the
+	command argument x is between MT_ST_HPLOADER_OFFSET + 1 and
+	MT_ST_HPLOADER_OFFSET + 6, the number x is used sent to the
+	drive with the command and it selects the tape slot to use of
+	HP C1553A changer.
 MTCOMPRESSION Sets compressing or uncompressing drive mode using the
 	SCSI mode page 15. Note that some drives other methods for
 	control of compression. Some drives (like the Exabytes) use
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.19/linux/drivers/scsi/ncr53c8xx.c	Tue Nov 12 15:56:11 1996
+++ linux/drivers/scsi/ncr53c8xx.c	Tue Dec 31 21:13:09 1996
@@ -40,7 +40,7 @@
 */
 
 /*
-**	13 October 1996, version 1.14a
+**	26 December 1996, version 1.16b
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -55,8 +55,8 @@
 **		53C815		(~53C810 with on board rom BIOS)
 **		53C820		(Wide, NCR BIOS in flash bios required)
 **		53C825		(Wide, ~53C820 with on board rom BIOS)
-**		53C860		(not fully ested)
-**		53C875		(not fully tested)
+**		53C860		(Narrow fast 20, BIOS required)
+**		53C875		(Wide fast 40 with on board rom BIOS)
 **
 **	Other features:
 **		Memory mapped IO (linux-1.3.X and above only)
@@ -67,7 +67,7 @@
 #define SCSI_NCR_DEBUG
 #define SCSI_NCR_DEBUG_FLAGS	(0)		
 
-#define NCR_DATE "pl23 95/09/07"
+#define NCR_DATE "pl24 96/12/14"
 
 #define NCR_VERSION	(2)
 
@@ -105,7 +105,7 @@
 
 #include <linux/version.h>
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#include "linux/blk.h"
+#include <linux/blk.h>
 #else
 #include "../block/blk.h"
 #endif
@@ -159,10 +159,6 @@
 #define SCSI_NCR_MAX_SYNC   (10000)
 #endif
 
-#ifndef SCSI_NCR_DEFAULT_SYNC
-#define SCSI_NCR_DEFAULT_SYNC	SCSI_NCR_MAX_SYNC
-#endif
-
 /*
 **    The maximal bus with (in log2 byte)
 **    (0=8 bit, 1=16 bit)
@@ -427,6 +423,24 @@
 	unsigned char and_map[MAX_TARGET];
 } target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES };
 
+/*
+**	Driver setup.
+**
+**	This structure is initialized from linux config options.
+**	It can be overridden at boot-up by the boot command line.
+*/
+static struct {
+	unsigned master_parity	: 1;
+	unsigned scsi_parity	: 1;
+	unsigned disconnection	: 1;
+	unsigned special_features : 1;
+	unsigned ultra_scsi	: 1;
+	unsigned force_sync_nego: 1;
+	unsigned verbose	: 8;
+	unsigned default_tags;
+	unsigned default_sync;
+	unsigned debug;
+} driver_setup = SCSI_NCR_DRIVER_SETUP;
 
 /*
 **	Other Linux definitions
@@ -446,7 +460,7 @@
 
 static void ncr53c8xx_timeout(unsigned long np);
 
-#define bootverbose 1
+#define bootverbose (driver_setup.verbose)
 
 /*==========================================================
 **
@@ -1269,7 +1283,7 @@
 	**	between ncr chips.
 	**	sv_xxx are some io register bit value at start-up and
 	**	so assumed to have been set by the sdms bios.
-	**	uf_xxx are the bit fields of io register that will keep 
+	**	rv_xxx are the bit fields of io register that will keep 
 	**	the features used by the driver.
 	**-----------------------------------------------
 	*/
@@ -1284,10 +1298,12 @@
 	u_char	sv_ctest3;
 	u_char	sv_ctest4;
 
-	u_char	uf_dmode;
-	u_char	uf_dcntl;
-	u_char	uf_ctest3;
-	u_char	uf_ctest4;
+	u_char	rv_dmode;
+	u_char	rv_dcntl;
+	u_char	rv_ctest3;
+	u_char	rv_ctest4;
+	u_char	rv_ctest5;
+	u_char	uf_doubler;
 
 	/*-----------------------------------------------
 	**	Scripts ..
@@ -1337,10 +1353,14 @@
 	/*
 	**	timing parameters
 	*/
-	u_char		ns_async;
 	u_char		ns_sync;
 	u_char		rv_scntl3;
 
+	/*
+	**	controller chip dependent maximal offset.
+	*/
+	u_char		maxoffs;
+
 	/*-----------------------------------------------
 	**	Link to the generic SCSI driver
 	**-----------------------------------------------
@@ -1566,7 +1586,7 @@
 static  void    ncr_script_fill (struct script * scr);
 static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
 static	void	ncr_setmaxtags	(ncb_p np, tcb_p tp, u_long usrtags);
-static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char sxfer);
+static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
 static	void	ncr_settags     (tcb_p tp, lcb_p lp);
 static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide);
 static	int	ncr_show_msg	(u_char * msg);
@@ -3437,7 +3457,7 @@
 */
 
 #define PRINT_LUN(np, target, lun) \
-printf("%s-<target %d, lun %d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
 static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
@@ -3517,7 +3537,8 @@
 #endif
 	}
 	else
-		printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+		if (bootverbose)
+			printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
 
 	/*
 	**	Make the controller's registers available.
@@ -3549,7 +3570,7 @@
 	np->maxwide = 0;
 	np->rv_scntl3 = 0x13;	/* default: 40MHz clock */
 	np->ns_sync   = 25;
-	np->ns_async  = 50;
+	np->maxoffs   = 8;
 
 	/*
 	**	Get the frequency of the chip's clock.
@@ -3561,14 +3582,24 @@
 		np->maxwide = 1;
 		break;
 	case PCI_DEVICE_ID_NCR_53C860:
-		np->rv_scntl3 = 0x35;	/* always assume 80MHz clock for 860 */
+		if (driver_setup.ultra_scsi) {
+			np->rv_scntl3 = 0x15;
+			np->ns_sync   = 12;
+		}
+		else
+			np->rv_scntl3 = 0x35;	/* always assume 80MHz clock for 860 */
 		break;
 	case PCI_DEVICE_ID_NCR_53C875:
 		np->maxwide = 1;
+		if (driver_setup.special_features)
+			np->maxoffs = 16;
 		ncr_getclock(np);
 		break;
 	}
 
+	if (bootverbose && np->ns_sync < 25)
+		printf ("%s: Ultra SCSI support enabled\n", ncr_name(np));
+
 	/*
 	**	Fill Linux host instance structure
 	*/
@@ -3624,14 +3655,6 @@
 	OUTB (nc_istat,  0   );
 
 	/*
-	**	Reset chip, once again.
-	*/
-
-	OUTB (nc_istat,  SRST);
-	DELAY (1000);
-	OUTB (nc_istat,  0   );
-
-	/*
 	**	Now check the cache handling of the pci chipset.
 	*/
 
@@ -3653,8 +3676,9 @@
 	*/
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
 #   ifdef SCSI_NCR_SHARE_IRQ
-	printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
-	        ncr_name(np), irq, (u_long) np);
+	if (bootverbose)
+		printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
+		        ncr_name(np), irq, (u_long) np);
 	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
 #   else
 	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
@@ -3671,13 +3695,11 @@
 	**	Not allow disconnections for all targets if asked by config
 	*/
 
-#ifdef	SCSI_NCR_NO_DISCONNECT
-	{
+	if (!driver_setup.disconnection) {
 		int i;
 		for (i = 0 ; i < MAX_TARGET ; i++)
 			np->target[i].usrflag |= UF_NODISC;
 	}
-#endif
 
 	/*
 	**	After SCSI devices have been opened, we cannot
@@ -3885,12 +3907,12 @@
 	**
 	**----------------------------------------------------
 	*/
-#if (SCSI_NCR_DEFAULT_TAGS < SCSI_NCR_MAX_TAGS)
- 	if (cmd->device && cmd->device->tagged_queue &&
-	    (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
-		ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
+	if (driver_setup.default_tags < SCSI_NCR_MAX_TAGS) {
+	 	if (cmd->device && cmd->device->tagged_queue &&
+		    (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
+			ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
+		}
 	}
-#endif
 
 	/*---------------------------------------------------
 	**
@@ -4346,6 +4368,9 @@
 	/*
 	**	Disable reselect.
 	**      Remove it from startqueue.
+	**	Set cp->tlimit to 0. The ncr_timeout() handler will use 
+	**	this condition in order to complete the canceled command 
+	**	after the script skipped the ccb, if necessary.
 	*/
 	cp->jump_ccb.l_cmd = (SCR_JUMP);
 	if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) {
@@ -4353,35 +4378,15 @@
 		cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
 	}
 
-	switch (cp->host_status) {
-	case HS_BUSY:
-	case HS_NEGOTIATE:
-		/*
-		** still in start queue ?
-		*/
-		if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, skip)) {
-			retv = SCSI_ABORT_BUSY;
-			break;
-		}
-	/* fall through */
-	case HS_DISCONNECT:
-		cp->host_status=HS_ABORTED;
-		cp->tag = 0;
-		/*
-		**	wakeup this ccb.
-		*/
-		ncr_complete (np, cp);
-		retv = SCSI_ABORT_SUCCESS;
-		break;
-	default:
-		cp->tag = 0;
-		/*
-		**	wakeup this ccb.
-		*/
-		ncr_complete (np, cp);
-		retv = SCSI_ABORT_SUCCESS;
-		break;
-	}
+	cp->tlimit = 0;
+	retv = SCSI_ABORT_PENDING;
+
+	/*
+	**      If there are no requests, the script
+	**      processor will sleep on SEL_WAIT_RESEL.
+	**      Let's wake it up, since it may have to work.
+	*/
+	OUTB (nc_istat, SIGP);
 
 	restore_flags(flags);
 
@@ -4453,7 +4458,7 @@
 
 	/*
 	**	Reset NCR chip
-	**	Preserve bios setting for automatic clock detection.
+	**	Restore bios setting for automatic clock detection.
 	*/
 
 	printf("%s: resetting chip\n", ncr_name(np));
@@ -4461,12 +4466,22 @@
 	DELAY (1000);
 	OUTB (nc_istat,  0   );
 
-	OUTB(nc_scntl3,	np->sv_scntl3);
 	OUTB(nc_dmode,	np->sv_dmode);
 	OUTB(nc_dcntl,	np->sv_dcntl);
 	OUTB(nc_ctest3,	np->sv_ctest3);
 	OUTB(nc_ctest4,	np->sv_ctest4);
 
+	if (np->uf_doubler) {
+		OUTB(nc_stest1, DBLEN);		/* Enable clock doubler	*/
+		DELAY(10);
+		OUTB(nc_stest3, 0x20);		/* Halt the scsi clock	*/
+		OUTB(nc_scntl3,	np->sv_scntl3);
+		OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock doubler	*/
+		OUTB(nc_stest3, 0x00);		/* Restart scsi clock 	*/
+	}
+	else
+		OUTB(nc_scntl3,	np->sv_scntl3);
+
 	/*
 	**	Release Memory mapped IO region and IO mapped region
 	*/
@@ -4641,11 +4656,11 @@
 		*/
 		if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) {
 			if (np->unit < SCSI_NCR_MAX_HOST) {
-#ifdef SCSI_NCR_FORCE_SYNC_NEGO
-				((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
-#endif
-				((char *) cmd->request_buffer)[7] &=
-				(target_capabilities[np->unit].and_map[cmd->target]);
+				if (driver_setup.force_sync_nego)
+					((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
+				else
+					((char *) cmd->request_buffer)[7] &=
+					(target_capabilities[np->unit].and_map[cmd->target]);
 			}
 			bcopy (	cmd->request_buffer,
 				&tp->inqdata,
@@ -4654,7 +4669,7 @@
 			/*
 			**	set number of tags
 			*/
-				ncr_setmaxtags (np, tp, SCSI_NCR_DEFAULT_TAGS);
+				ncr_setmaxtags (np, tp, driver_setup.default_tags);
 			/*
 			**	prepare negotiation of synch and wide.
 			*/
@@ -4916,69 +4931,70 @@
 	ncr_wakeup (np, code);
 
 	/*
-	**	Remove Reset, abort ...
-	*/
-	OUTB (nc_istat,  0      );
-
-	/*
 	**	Init chip.
 	*/
 #if defined SCSI_NCR_TRUST_BIOS_SETTING
-	np->uf_dmode	= np->sv_dmode;
-	np->uf_dcntl	= np->sv_dcntl;
-	np->uf_ctest3	= np->sv_ctest3;
-	np->uf_ctest4	= np->sv_ctest4;
+	np->rv_dmode	= np->sv_dmode;
+	np->rv_dcntl	= np->sv_dcntl;
+	np->rv_ctest3	= np->sv_ctest3;
+	np->rv_ctest4	= np->sv_ctest4;
 #else
-	np->uf_dmode	= 0;
-	np->uf_dcntl	= 0;
-	np->uf_ctest3	= 0;
-	np->uf_ctest4	= 0;
+	np->rv_dmode	= 0;
+	np->rv_dcntl	= 0;
+	np->rv_ctest3	= 0;
+	np->rv_ctest4	= 0;
 
 /**	NCR53C810			**/
 	if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) {
-		np->uf_dmode	= 0x80;	/* burst length 8 */
+		np->rv_dmode	= 0x80;	/* burst length 8 */
 	}
 	else
 /**	NCR53C815			**/
 	if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) {
-		np->uf_dmode	= 0x80;	/* burst length 8 */
+		np->rv_dmode	= 0x80;	/* burst length 8 */
 	}
 	else
 /**	NCR53C825			**/
 	if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) {
-		np->uf_dmode	= 0x8a;	/* burst length 8, burst opcode fetch */
+		np->rv_dmode	= 0x8a;	/* burst length 8, burst opcode fetch */
 	}
 	else
 /**	NCR53C810A or NCR53C860		**/
 	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) ||
 	    ChipDevice == PCI_DEVICE_ID_NCR_53C860) {
-#ifndef SCSI_NCR_SPECIAL_FEATURES
-		np->uf_dmode	= 0xc0;	/* burst length 16 */
-#else
-		np->uf_dmode	= 0xce;	/* burst op-code fetch, read multiple */
-					/* read line, burst length 16 */
-		np->uf_dcntl	= 0xa0;	/* prefetch, cache line size */
-		np->uf_ctest3	= 0x1;	/* write and invalidate */
-		np->uf_ctest4	= 0x0;	/* burst not disabled */
-#endif
+		if (!driver_setup.special_features)
+			np->rv_dmode	= 0xc0;	/* burst length 16 */
+		else {
+			np->rv_dmode	= 0xc0 | BOF | ERMP | ERL;
+						/* burst op-code fetch, read multiple */
+						/* read line, burst 16 */
+			np->rv_dcntl	= PFEN | CLSE;
+						/* prefetch, cache line size */
+			np->rv_ctest3	= WRIE;	/* write and invalidate */
+			np->rv_ctest4	= 0x0;	/* burst not disabled */
+		}
 	}
 	else
 /**	NCR53C825A or NCR53C875		**/
 	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
 	    ChipDevice == PCI_DEVICE_ID_NCR_53C875) {
-#ifndef SCSI_NCR_SPECIAL_FEATURES
-		np->uf_dmode	= 0xc0;	/* burst length 16 */
-#else
-		np->uf_dmode	= 0xce;	/* burst op-code fetch, read multiple */
-					/* read line, burst length 16 */
-		np->uf_dcntl	= 0xa0;	/* prefetch, cache line size */
-		np->uf_ctest3	= 0x1;	/* write and invalidate */
-		np->uf_ctest4	= 0x0;	/* burst not disabled */
-#endif
+		if (!driver_setup.special_features)
+			np->rv_dmode	= 0xc0;	/* burst length 16 */
+		else {
+			np->rv_dmode	= 0xc0 | BOF | ERMP | ERL;
+						/* burst op-code fetch, read multiple */
+						/* read line, burst 128 (ctest5&4) */
+			np->rv_dcntl	= PFEN | CLSE;
+						/* prefetch, cache line size */
+			np->rv_ctest3	= WRIE;	/* write and invalidate */
+			np->rv_ctest4	= 0x0;	/* burst not disabled */
+			np->rv_ctest5	= 0x24;	/* burst 128	(0x04) */
+						/* dma fifo 536	(0x20) */
+		}
 	}
 /**	OTHERS				**/
 	else {
-		np->uf_dmode	= 0xc0;	/* burst length 16 */
+		np->rv_dmode	= 0xc0;	/* burst length 16 */
 	}
 #endif /* SCSI_NCR_TRUST_BIOS_SETTING */
 
@@ -4986,28 +5002,44 @@
 	printf("%s: bios: dmode=0x%02x, dcntl=0x%02x, ctest3=0x%02x, ctest4=0x%02x\n",
 		ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4);
 	printf("%s: used: dmode=0x%02x, dcntl=0x%02x, ctest3=0x%02x, ctest4=0x%02x\n",
-		ncr_name(np), np->uf_dmode, np->uf_dcntl, np->uf_ctest3, np->uf_ctest4);
+		ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4);
 #endif
 
-#ifdef SCSI_NCR_DISABLE_PARITY_CHECK
-	OUTB (nc_scntl0, 0xc0   );      /*  full arb., (no parity)           */
-#else
-	OUTB (nc_scntl0, 0xca   );      /*  full arb., ena parity, par->ATN  */
-#endif
+	OUTB (nc_istat,  0x00   );      /*  Remove Reset, abort ...	     */
+	if (driver_setup.scsi_parity)
+		OUTB (nc_scntl0, 0xca   );      /*  full arb., ena parity, par->ATN  */
+	else
+		OUTB (nc_scntl0, 0xc0   );      /*  full arb., (no parity)           */
+
 	OUTB (nc_scntl1, 0x00	);	/*  odd parity, and remove CRST!!    */
 
+	if (np->uf_doubler) {
+		if (bootverbose >= 2)
+			printf ("%s: enabling clock doubler\n", ncr_name(np));
+		OUTB(nc_stest1, DBLEN);		/* Enable clock doubler	*/
+		DELAY(10);
+		OUTB(nc_stest3, 0x20);		/* Halt the scsi clock	*/
+		OUTB(nc_scntl3, 0x05);		/* Safe timing for now	*/
+		OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock doubler	*/
+		OUTB(nc_stest3, 0x00);		/* Restart scsi clock 	*/
+	}
+
 	OUTB (nc_scntl3, np->rv_scntl3);/*  timing prescaler		     */
 	OUTB (nc_scid  , RRE|np->myaddr);/*  host adapter SCSI address       */
 	OUTW (nc_respid, 1ul<<np->myaddr);/*  id to respond to		     */
 	OUTB (nc_istat , SIGP	);	/*  Signal Process		     */
-	OUTB (nc_dmode , np->uf_dmode);	/*  Burst length = 2 .. 16 transfers */
-	OUTB (nc_dcntl , NOCOM|np->uf_dcntl);/* no single step mode, protect SFBR*/
+	OUTB (nc_dmode , np->rv_dmode);	/*  Burst length = 2 .. 16 transfers */
 
-#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK
-	OUTB (nc_ctest4, 0x00|np->uf_ctest4);	/*  disable master parity checking   */
-#else
-	OUTB (nc_ctest4, 0x08|np->uf_ctest4);	/*  enable master parity checking    */
-#endif
+	if (driver_setup.special_features && np->rv_ctest5)
+		OUTB (nc_ctest5, np->rv_ctest5);     /*  large fifo + large burst */
+
+	OUTB (nc_dcntl , NOCOM|np->rv_dcntl);/* no single step mode, protect SFBR*/
+	OUTB (nc_ctest3, np->rv_ctest3);     /* write and invalidate       */
+
+	if (driver_setup.master_parity)
+		OUTB (nc_ctest4, MPEE|np->rv_ctest4);	/*  enable master parity checking    */
+	else
+		OUTB (nc_ctest4, 0x00|np->rv_ctest4);	/*  disable master parity checking   */
 
 	OUTB (nc_stest2, EXT    );	/*  Extended Sreq/Sack filtering     */
 	OUTB (nc_stest3, TE     );	/*  TolerANT enable		     */
@@ -5021,18 +5053,16 @@
 
 	usrsync = 255;
 
-#if defined(SCSI_NCR_DEFAULT_SYNC) && SCSI_NCR_DEFAULT_SYNC != 0
-	if (SCSI_NCR_MAX_SYNC) {
+	if (driver_setup.default_sync && SCSI_NCR_MAX_SYNC) {
 		u_long period;
-		period =1000000/SCSI_NCR_DEFAULT_SYNC; /* ns = 10e6 / kHz */
-		if (period <= 11 * np->ns_sync) {
+		period =1000000/driver_setup.default_sync; /* ns = 10e6 / kHz */
+		if (period <= 11 * 50) {
 			if (period < 4 * np->ns_sync)
 				usrsync = np->ns_sync;
 			else
 				usrsync = period / 4;
 		};
 	};
-#endif
 
 	/*
 	**	Reinitialize usrwide.
@@ -5094,7 +5124,12 @@
 
 	u_long minsync = tp->usrsync;
 
-	if (minsync < 25) minsync=25;
+	if (driver_setup.ultra_scsi) {
+		if (minsync < 12) minsync=12;
+	}
+	else {
+		if (minsync < 25) minsync=25;
+	}
 
 	/*
 	**	if not scsi 2
@@ -5115,11 +5150,11 @@
 	**	divider limit
 	*/
 
-	if (minsync > (np->ns_sync * 11) / 4)
+	if (minsync > (11*50)/4)
 		minsync = 255;
 
 	tp->minsync = minsync;
-	tp->maxoffs = (minsync<255 ? 8 : 0);
+	tp->maxoffs = (minsync<255 ? np->maxoffs : 0);
 
 	/*
 	**	period=0: has to negotiate sync transfer
@@ -5140,11 +5175,12 @@
 **==========================================================
 */
 
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
 {
 	Scsi_Cmnd *cmd;
 	tcb_p tp;
 	u_char target = INB (nc_ctest0) & 0x0f;
+	u_char p2;
 
 	assert (cp);
 	if (!cp) return;
@@ -5155,26 +5191,47 @@
 	assert (target == (cmd->target & 0xf));
 
 	tp = &np->target[target];
-	tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff;
 
-	if (tp->sval == sxfer) return;
+	if (!scntl3 || !(sxfer & 0x1f))
+		scntl3 = np->rv_scntl3;
+	scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+
+	/*
+	**	Deduce the value of controller sync period from scntl3.
+	**	p2 is twice this value since we do integer calculations.
+	*	(12.5 ns would give inaccurate results)
+	*/
+	p2 = (scntl3 & 0x07) == 0x05 ? 25 : 50;
+	switch(scntl3 & 0x70) {
+	case 0x50:	p2 *= 4;	break;
+	case 0x30:	p2 *= 2;	break;
+	default:			break;
+	}
+
+	tp->period= sxfer&0x1f ? (((sxfer>>5)+4) * p2)/2 : 0xffff;
+
+	if (tp->sval == sxfer && tp->wval == scntl3) return;
 	tp->sval = sxfer;
+	tp->wval = scntl3;
 
 	/*
 	**	Bells and whistles   ;-)
 	*/
 	PRINT_ADDR(cmd);
-	if (sxfer & 0x0f) {
+	if (sxfer & 0x01f) {
+		unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0);
+		unsigned mb10 = (f10 + tp->period/2) / tp->period;
 		/*
 		**  Disable extended Sreq/Sack filtering
 		*/
 		if (tp->period <= 200) OUTB (nc_stest2, 0);
-
-		printf ("%s%dns (%d Mb/sec) offset %d.\n",
-			tp->period<200 ? "FAST SCSI-2 ":"",
-			tp->period,
-			(((tp->wval & EWS)? 2:1)*1000+tp->period/2)/tp->period,
-			sxfer & 0x0f);
+		printf ("%s%d.%d MB/s (%d ns, offset %d)\n",
+			tp->widedone > 1    ?
+			(tp->period  < 100  ? "ULTRA WIDE SCSI " :
+			(tp->period  < 200  ? "FAST WIDE SCSI-2 ":"WIDE ")) :
+			(tp->period  < 100  ? "ULTRA SCSI " :
+			(tp->period  < 200  ? "FAST SCSI-2 ":"")),
+			mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f);
 	} else  printf ("asynchronous.\n");
 
 	/*
@@ -5182,6 +5239,8 @@
 	*/
 	OUTB (nc_sxfer, sxfer);
 	np->sync_st = sxfer;
+	OUTB (nc_scntl3, scntl3);
+	np->wide_st = scntl3;
 
 	/*
 	**	patch ALL ccbs of this target.
@@ -5190,6 +5249,7 @@
 		if (!cp->cmd) continue;
 		if (cp->cmd->target != target) continue;
 		cp->sync_status = sxfer;
+		cp->wide_status = scntl3;
 	};
 }
 
@@ -5205,7 +5265,7 @@
 	Scsi_Cmnd *cmd;
 	u_short target = INB (nc_ctest0) & 0x0f;
 	tcb_p tp;
-	u_char	scntl3 = np->rv_scntl3 | (wide ? EWS : 0);
+	u_char	scntl3;
 
 	assert (cp);
 	if (!cp) return;
@@ -5217,17 +5277,20 @@
 
 	tp = &np->target[target];
 	tp->widedone  =  wide+1;
+	scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
 	if (tp->wval == scntl3) return;
 	tp->wval = scntl3;
 
 	/*
 	**	Bells and whistles   ;-)
 	*/
-	PRINT_ADDR(cmd);
-	if (scntl3 & EWS)
-		printf ("WIDE SCSI (16 bit) enabled.\n");
-	else
-		printf ("WIDE SCSI disabled.\n");
+	if (bootverbose >= 2) {
+		PRINT_ADDR(cmd);
+		if (scntl3 & EWS)
+			printf ("WIDE SCSI (16 bit) enabled.\n");
+		else
+			printf ("WIDE SCSI disabled.\n");
+	}
 
 	/*
 	**	set actual value and sync_status
@@ -5421,7 +5484,7 @@
 	**	If release process in progress, let's go
 	**	Set the release stage from 1 to 2 to synchronize
 	**	with the release process.
-	**/
+	*/
 
 	if (np->release_stage) {
 		if (np->release_stage == 1) np->release_stage = 2;
@@ -5436,7 +5499,12 @@
 
 	add_timer(&np->timer);
 
-	if (np->lasttime + HZ < thistime) {
+	/*
+	**	Since the generic scsi driver only allows us 0.5 second 
+	**	to perform abort of a command, we must look at ccbs about 
+	**	every 0.25 second.
+	*/
+	if (np->lasttime + (HZ>>2) <= thistime) {
 		/*
 		**	block ncr interrupts
 		*/
@@ -5472,33 +5540,14 @@
 			**      But we have to check whether it died.
 			**      Let's wake it up.
 			*/
+
 			OUTB (nc_istat, SIGP);
 		}
-#ifdef undef
-		if (np->latetime>10) {
-			/*
-			**	Although we tried to wake it up,
-			**	the script processor didn't respond.
-			**
-			**	May be a target is hanging,
-			**	or another initator lets a tape device
-			**	rewind with disconnect disabled :-(
-			**
-			**	We won't accept that.
-			*/
-			if (INB (nc_sbcl) & CBSY)
-				OUTB (nc_scntl1, CRST);
-			DELAY (1000);
-			ncr_init (np, "ncr dead ?", HS_TIMEOUT);
-			np->disc = 1;
-			np->heartbeat = thistime;
-		}
-#endif /* undef */
 
 		/*----------------------------------------------------
 		**
-		**	should handle ccb timeouts
-		**	Let the middle scsi driver manage timeouts.
+		**	handle ccb timeouts
+		**
 		**----------------------------------------------------
 		*/
 
@@ -5511,7 +5560,7 @@
 			/*
 			**	Have to force ordered tag to avoid timeouts
 			*/
-			if (cp->cmd && cp->tlimit <= 
+			if (cp->cmd && cp->tlimit && cp->tlimit <= 
 				thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) {
 				lcb_p lp;
 				lp = np->target[cp->cmd->target].lp[cp->cmd->lun];
@@ -5519,24 +5568,14 @@
 					lp->force_ordered_tag = 1;
 				}
 			}
-/*
-**	Let the middle scsi driver manage timeouts
-*/
-#if 0
-			if (cp->tlimit > thistime) continue;
-
 			/*
-			**	Disable reselect.
-			**      Remove it from startqueue.
+			**	ncr_abort_command() cannot complete canceled 
+			**	commands immediately. It sets tlimit to zero 
+			**	and ask the script to skip the scsi process if 
+			**	necessary. We have to complete this work here.
 			*/
-			cp->jump_ccb.l_cmd = (SCR_JUMP);
-			if (cp->phys.header.launch.l_paddr ==
-				NCB_SCRIPT_PHYS (np, select)) {
-				printf ("%s: timeout ccb=%p (skip)\n",
-					ncr_name (np), cp);
-				cp->phys.header.launch.l_paddr
-				= NCB_SCRIPT_PHYS (np, skip);
-			};
+
+			if (cp->tlimit) continue;
 
 			switch (cp->host_status) {
 
@@ -5551,7 +5590,7 @@
 
 				/* fall through */
 			case HS_DISCONNECT:
-				cp->host_status=HS_TIMEOUT;
+				cp->host_status=HS_ABORTED;
 			};
 			cp->tag = 0;
 
@@ -5559,7 +5598,8 @@
 			**	wakeup this ccb.
 			*/
 			ncr_complete (np, cp);
-#endif
+
+			OUTB (nc_istat, SIGP);
 		}
 		restore_flags(flags);
 	}
@@ -5615,8 +5655,8 @@
 	**	Never test for an error condition you don't know how to handle.
 	*/
 
-	dstat = INB (nc_dstat);
-	sist  = INW (nc_sist) ;
+	sist  = (istat & SIP) ? INW (nc_sist)  : 0;
+	dstat = (istat & DIP) ? INB (nc_dstat) : 0;
 	np->profile.num_int++;
 
 	if (DEBUG_FLAGS & DEBUG_TINY)
@@ -5756,11 +5796,11 @@
 
 	if (((script_ofs & 3) == 0) &&
 	    (unsigned)script_ofs < sizeof(struct script)) {
-		printf ("\tscript cmd = %08x\n", 
+		printf ("%s: script cmd = %08x\n", ncr_name(np),
 			(int) *(ncrcmd *)((char*)np->script +script_ofs));
 	}
 
-        printf ("\treg:\t");
+        printf ("%s: reg:", ncr_name(np));
         for (i=0; i<16;i++)
             printf (" %02x", (unsigned)INB_OFF(i));
         printf (".\n");
@@ -5995,7 +6035,8 @@
 	u_int32	oadr, olen;
 	u_int32	*tblp;
         ncrcmd *newcmd;
-	u_char	cmd, sbcl, delta, ss0, ss2;
+	u_char	cmd, sbcl, ss0, ss2, ctest5;
+	u_short	delta;
 	ccb_p	cp;
 
 	dsp = INL (nc_dsp);
@@ -6005,9 +6046,18 @@
 	ss2 = INB (nc_sstat2);
 	sbcl= INB (nc_sbcl);
 
+	if (driver_setup.special_features)
+		ctest5 = INB (nc_ctest5);
+	else
+		ctest5 = 0;
+
 	cmd = dbc >> 24;
 	rest= dbc & 0xffffff;
-	delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+	if (ctest5 & 0x20)
+		delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+	else
+		delta=(INB (nc_dfifo) - rest) & 0x7f;
 
 	/*
 	**	The data in the dma fifo has not been transfered to
@@ -6183,6 +6233,7 @@
 
 void ncr_int_sir (ncb_p np)
 {
+	u_char scntl3;
 	u_char chg, ofs, per, fak, wide;
 	u_char num = INB (nc_dsps);
 	ccb_p	cp=0;
@@ -6384,7 +6435,7 @@
 		switch (cp->nego_status) {
 
 		case NS_SYNC:
-			ncr_setsync (np, cp, 0xe0);
+			ncr_setsync (np, cp, 0, 0xe0);
 			break;
 
 		case NS_WIDE:
@@ -6440,24 +6491,60 @@
 
 		/*
 		**	Check against controller limits.
+		**	--------------------------------
+		**	per <= 13  special case, allow fast 20 MHz transfer.
+		**	per <  25  fast 20
+		**	per <  50  fast
+		**	per < 100  slow
+		**	Use a value p2 twice the controller limit in order to 
+		**	not do wrong integer calculation for 80 MHz clock.
+		**	(12.5x2 = 25ns).
+		**	Compute scntl3&0xf0 sync clock divisor for 50 ns period.
+		**	Ajust it according to actual controller sync period.
+		**	- 0x40 divides it by 4  -> 50/4 = 12.5ns
+		**	- 0x20 divides it by 2  -> 50/2 = 25 ns
+		**	Similar stuff is used in ncr_getclock().
 		*/
+		fak	= 7;
+		scntl3	= 0;
 		if (ofs != 0) {
-			fak = (4ul * per - 1) / np->ns_sync - 3;
-			if (fak>7) {
-				chg = 1;
-				ofs = 0;
+			u_char p2;
+
+			p2	= 100;
+			scntl3	= (np->rv_scntl3 & 0x07) << 4;
+
+			if (per <= 13) {
+				fak	= 0;
+				scntl3	= (scntl3 - 0x40) | 0x80;
+			}
+			else {
+				if (per < 25) {
+					p2	= 25;
+					scntl3	= (scntl3 - 0x40) | 0x80;
+				}
+				else if (per < 50) {
+					p2	= 50;
+					scntl3	= scntl3 - 0x20;
+				}
+
+				fak = (8 * per - 1) / p2 - 3;
+				if (fak > 7) {
+					chg = 1;
+					ofs = 0;
+				}
 			}
 		}
 		if (ofs == 0) {
-			fak = 7;
-			per = 0;
+			fak	= 7;
+			per	= 0;
+			scntl3	= 0;
 			tp->minsync = 0;
 		}
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("sync: per=%d ofs=%d fak=%d chg=%d.\n",
-				per, ofs, fak, chg);
+			printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
+				per, scntl3, ofs, fak, chg);
 		}
 
 		if (INB (HS_PRT) == HS_NEGOTIATE) {
@@ -6472,13 +6559,13 @@
 					/*
 					**	Answer wasn't acceptable.
 					*/
-					ncr_setsync (np, cp, 0xe0);
+					ncr_setsync (np, cp, 0, 0xe0);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
 				} else {
 					/*
 					**	Answer is ok.
 					*/
-					ncr_setsync (np, cp, (fak<<5)|ofs);
+					ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
 				};
 				return;
@@ -6508,7 +6595,7 @@
 		**      prepare an answer message
 		*/
 
-		ncr_setsync (np, cp, (fak<<5)|ofs);
+		ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
 
 		np->msgout[0] = M_EXTENDED;
 		np->msgout[1] = 3;
@@ -6521,7 +6608,7 @@
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
 			printf ("sync msgout: ");
-			(void) ncr_show_msg (np->msgin);
+			(void) ncr_show_msg (np->msgout);
 			printf (".\n");
 		}
 
@@ -6595,7 +6682,7 @@
 				return;
 
 			case NS_SYNC:
-				ncr_setsync (np, cp, 0xe0);
+				ncr_setsync (np, cp, 0, 0xe0);
 				break;
 			};
 		};
@@ -6688,8 +6775,8 @@
 		*/
 
 		PRINT_ADDR(cp->cmd);
-		printf ("M_DISCONNECT received, but datapointer not saved:\n"
-			"\tdata=%x save=%x goal=%x.\n",
+		printf ("M_DISCONNECT received, but datapointer not saved: "
+			"data=%x save=%x goal=%x.\n",
 			(unsigned) INL (nc_temp),
 			(unsigned) np->header.savep,
 			(unsigned) np->header.goalp);
@@ -6974,7 +7061,7 @@
 		tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
 		tp->lp[lun] = lp;
 
-		ncr_setmaxtags (np, tp, SCSI_NCR_DEFAULT_TAGS);
+		ncr_setmaxtags (np, tp, driver_setup.default_tags);
 	}
 
 	/*
@@ -7112,8 +7199,7 @@
 **----------------------------------------------------------
 */
 
-/*	FreeBSD driver important comments
-**	---------------------------------
+/*
 **	We try to reduce the number of interrupts caused
 **	by unexpected phase changes due to disconnects.
 **	A typical harddisk may disconnect before ANY block.
@@ -7121,130 +7207,11 @@
 **	we had to use a break point every 512 bytes.
 **	Of course the number of scatter/gather blocks is
 **	limited.
+**	Under Linux, the scatter/gatter blocks are provided by 
+**	the generic driver. We just have to copy addresses and 
+**	sizes to the data segment array.
 */
 
-/*
-**	The scatterlist passed by the linux middle-level scsi drivers
-**	may contain blocks of any size (Generaly < 1024 bytes blocks,
-**	can be 4096 with a 4K fs).
-*/
-
-#if defined(SCSI_NCR_SEGMENT_SIZE)
-static	int	ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
-{
-	struct scatterlist *scatter;
-	struct dsb *phys;
-	register u_short segment = 0;
-	register u_short o_segment = 0;
-	u_short chunk, chunk_min;
-	u_long segaddr;
-	int segsize;
-	int datalen;
-
-	phys = &cp->phys;
-	cp->data_len = 0;
-
-	/*
-	**	Compute a good value for chunk size
-	**	If SCSI_NCR_SEGMENT_SIZE is OK, we will try to use it. 
-	*/
-
-	if (!cmd->use_sg)
-		cp->data_len	= cmd->request_bufflen;
-	else {
-		scatter = (struct scatterlist *)cmd->buffer;
-		for (segment = 0 ; segment < cmd->use_sg ; segment++)
-			cp->data_len += scatter[segment].length;
-	}
-
-
-	if (!cp->data_len) {
-		bzero (&phys->data, sizeof (phys->data));
-		return 0;
-	}
-
-	chunk_min	= cp->data_len / MAX_SCATTER;
-	for (chunk = SCSI_NCR_SEGMENT_SIZE ; chunk < chunk_min ; chunk += chunk);
-
-	/*
-	**	If the linux scsi command is not a scatterlist,
-	**	the computed chunk size is OK.
-	*/
-
-	if (!cmd->use_sg) {
-		bzero (&phys->data, sizeof (phys->data));
-		datalen = cmd->request_bufflen;
-		segaddr = vtophys(cmd->request_buffer);
-		segsize = chunk;
-		o_segment = 0;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
-	printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
-	(unsigned) segaddr, (int) datalen, (int) chunk);
-
-		while (datalen && (o_segment < MAX_SCATTER)) {
-			if (segsize > datalen) segsize	= datalen;
-			phys->data[o_segment].addr	= segaddr;
-			phys->data[o_segment].size	= segsize;
-
-			datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
-	printf ("ncr53c8xx:     seg #%d  addr=%lx  size=%d  (rest=%d).\n",
-	o_segment, segaddr, (int) segsize, (int) datalen);
-
-			segaddr	+= segsize;
-			o_segment++;
-		}
-
-		return datalen ? -1 : o_segment;
-	}
-
-	/*
-	**	Else, the computed chunk size is not so good
-	**	and we have to iterate.
-	**	Rescatter the Linux scatterlist into the data block descriptor.
-	**	Loop if necessary, beginning with the not so good chunk size and
-	**	doubling it if the scatter process fails.
-	*/
-
-	scatter = (struct scatterlist *)cmd->buffer;
-	for (segment = 0; segment < cmd->use_sg; chunk += chunk) {
-		o_segment	= 0;
-		bzero (&phys->data, sizeof (phys->data));
-		for (segment = 0 ; segment < cmd->use_sg ; segment++) {
-			datalen = scatter[segment].length;
-			segaddr = vtophys(scatter[segment].address);
-			segsize = chunk;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
-	printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
-	(unsigned) segaddr, (int) datalen, (int) chunk);
-
-			while (datalen && (o_segment < MAX_SCATTER)) {
-				if (segsize > datalen) segsize	= datalen;
-				phys->data[o_segment].addr	= segaddr;
-				phys->data[o_segment].size	= segsize;
-
-				datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
-	printf ("ncr53c8xx:     seg #%d  addr=%lx  size=%d  (rest=%d).\n",
-	o_segment, segaddr, (int) segsize, (int) datalen);
-
-				segaddr	+= segsize;
-				o_segment++;
-			}
-
-			if (datalen) break;
-		}
-	}
-
-	return segment < cmd->use_sg ? -1 : o_segment;
-}
-
-#else /* !defined SCSI_NCR_SEGMENT_SIZE */
-
 static	int	ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
 {
 	struct scr_tblmove *data;
@@ -7280,8 +7247,6 @@
 	return segment;
 }
 
-#endif /* SCSI_NCR_SEGMENT_SIZE */
-
 /*==========================================================
 **
 **
@@ -7377,6 +7342,9 @@
 	*/
 	if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) {
 		printf ("CACHE TEST FAILED: script execution failed.\n");
+		printf ("start=%08lx, pc=%08lx, end=%08lx\n", 
+			(u_long) NCB_SCRIPT_PHYS (np, snooptest), pc,
+			(u_long) NCB_SCRIPT_PHYS (np, snoopend) +8);
 		return (0x40);
 	};
 	/*
@@ -7606,7 +7574,7 @@
  	 */
  	OUTB (nc_scntl3, 0);
 
-	if (bootverbose)
+	if (bootverbose >= 2)
 		printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
   	/*
  	 * adjust for prescaler, and convert into KHz 
@@ -7624,11 +7592,19 @@
 	**	If true, disable clock doubler and assume 40 MHz clock.
 	*/
 	if ((stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
-		if (bootverbose)
-			printf ("%s: disabling clock doubler\n", ncr_name(np));
-		OUTB(nc_stest1, 0);
-		np->sv_scntl3	= 3;	/* Fix scntl3 for next insmod */
-		scntl3		= 3;
+		if (driver_setup.ultra_scsi) {
+			if (bootverbose >= 2)
+				printf ("%s: clock doubler found\n", ncr_name(np));
+			np->uf_doubler	= 1;
+			scntl3		= 5;
+		}
+		else {
+			if (bootverbose >= 2)
+				printf ("%s: disabling clock doubler\n", ncr_name(np));
+			OUTB(nc_stest1, 0);
+			np->sv_scntl3	= 3;	/* Fix scntl3 for next insmod */
+			scntl3		= 3;
+		}
 	} else {
 		if ((scntl3 & 7) == 0) {
 			unsigned f1, f2;
@@ -7655,9 +7631,14 @@
 		scntl3 = 3;
 	}
 
-	np->ns_sync   = 25;
-	np->ns_async  = 50;
-	np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
+	if (driver_setup.ultra_scsi && ((scntl3 & 0x7) == 0x5)) {
+		np->ns_sync   = 12;
+		np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x40 + (scntl3 & 0x7);
+	}
+	else {
+		np->ns_sync   = 25;
+		np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
+	}
 
 	if (bootverbose) {
 		printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
@@ -7705,6 +7686,76 @@
 #define ulong unsigned long
 #endif
 
+/* ---------------------------------------------------------------------
+**
+**	Driver setup from the boot command line
+**
+** ---------------------------------------------------------------------
+*/
+
+void ncr53c8xx_setup(char *str, int *ints)
+{
+	char *cur = str;
+	char *pv;
+	int val;
+	int base;
+	int c;
+
+	while (cur != NULL && (pv = strchr(cur, ':')) != NULL) {
+		val = 0;
+		c = *++pv;
+		if	(c == 'n')
+			val = 0;
+		else if	(c == 'y')
+			val = 1;
+		else {
+			base = 0;
+#if 0
+			if	(c == '0') {
+				c = *pv++;
+				base = 8;
+			}
+			if	(c == 'x') {
+				++pv;
+				base = 16;
+			}
+			else if (c >= '0' && c <= '9')
+				base = 10;
+			else
+				break;
+#endif
+			val = (int) simple_strtoul(pv, NULL, base);
+		}
+
+		if	(!strncmp(cur, "mpar:", 5))
+			driver_setup.master_parity	= val;
+		else if	(!strncmp(cur, "spar:", 5))
+			driver_setup.scsi_parity	= val;
+		else if	(!strncmp(cur, "disc:", 5))
+			driver_setup.disconnection	= val;
+		else if	(!strncmp(cur, "specf:", 6))
+			driver_setup.special_features = val;
+		else if	(!strncmp(cur, "ultra:", 6))
+			driver_setup.ultra_scsi	= val;
+		else if	(!strncmp(cur, "fsn:", 4))
+			driver_setup.force_sync_nego	= val;
+		else if	(!strncmp(cur, "tags:", 5)) {
+			if (val > SCSI_NCR_MAX_TAGS)
+				val = SCSI_NCR_MAX_TAGS;
+			driver_setup.default_tags	= val;
+			}
+		else if	(!strncmp(cur, "sync:", 5))
+			driver_setup.default_sync	= val * 1000;
+		else if	(!strncmp(cur, "verb:", 5))
+			driver_setup.verbose	= val;
+		else if	(!strncmp(cur, "debug:", 6))
+			driver_setup.debug	= val;
+
+		if ((cur = strchr(cur, ',')) != NULL)
+			++cur;
+	}
+}
+
 static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
 	     uchar bus, uchar device_fn, int options);
 
@@ -7751,6 +7802,27 @@
      uchar pci_bus, pci_device_fn;
      short pci_index;	/* Device index to PCI BIOS calls */
 
+#define YesNo(y)	y ? 'y' : 'n'
+    if (bootverbose >= 2) {
+         printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d\n",
+			 YesNo(driver_setup.disconnection),
+			 YesNo(driver_setup.special_features),
+			 YesNo(driver_setup.ultra_scsi),
+			 driver_setup.default_tags,
+			 driver_setup.default_sync/1000);
+         printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x\n",
+			 YesNo(driver_setup.master_parity),
+			 YesNo(driver_setup.scsi_parity),
+			 YesNo(driver_setup.force_sync_nego),
+			 driver_setup.verbose,
+			 driver_setup.debug);
+   }
+#undef YesNo
+
+#ifdef SCSI_NCR_DEBUG
+	ncr_debug = driver_setup.debug;
+#endif
+
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
      tpnt->proc_dir = &proc_scsi_ncr53c8xx;
 # ifdef SCSI_NCR_PROC_INFO_SUPPORT
@@ -8376,6 +8448,8 @@
 			SKIP_SPACES(1);
 			if	((arg_len = is_keyword(ptr, len, "alloc")))
 				uc->data |= DEBUG_ALLOC;
+			else if	((arg_len = is_keyword(ptr, len, "phase")))
+				uc->data |= DEBUG_PHASE;
 			else if	((arg_len = is_keyword(ptr, len, "poll")))
 				uc->data |= DEBUG_POLL;
 			else if	((arg_len = is_keyword(ptr, len, "queue")))
@@ -8388,6 +8462,8 @@
 				uc->data |= DEBUG_SCRIPT;
 			else if	((arg_len = is_keyword(ptr, len, "tiny")))
 				uc->data |= DEBUG_TINY;
+			else if	((arg_len = is_keyword(ptr, len, "timing")))
+				uc->data |= DEBUG_TIMING;
 			else if	((arg_len = is_keyword(ptr, len, "nego")))
 				uc->data |= DEBUG_NEGO;
 			else if	((arg_len = is_keyword(ptr, len, "tags")))
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.19/linux/drivers/scsi/ncr53c8xx.h	Thu Dec 12 19:37:07 1996
+++ linux/drivers/scsi/ncr53c8xx.h	Tue Dec 31 21:13:09 1996
@@ -45,10 +45,10 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.14c"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.16b"
  
 /*
-**	If SCSI_NCR_SPECIAL_FEATURES is defined,
+**	If SCSI_NCR_SETUP_SPECIAL_FEATURES is defined,
 **	the driver enables or not the following features according to chip id 
 **	revision id:
 **	DMODE   0xce
@@ -63,6 +63,9 @@
 **		0x01	set write and invalidate
 **	CTEST4  0x80
 **		0x80	burst disabled
+**	CTEST5  0x24	(825a and 875 only)
+**		0x04	burst 128
+**		0x80	dma fifo 536
 **
 **	If SCSI_NCR_TRUST_BIOS_SETTING is defined, the driver will use the 
 **	initial value of corresponding bit fields, assuming they have been 
@@ -71,14 +74,6 @@
 **	the driver will not be able to guess it. 
 */
 
-#if 0
-#define SCSI_NCR_TRUST_BIOS_SETTING
-#endif
-
-#if 0
-#define SCSI_NCR_SPECIAL_FEATURES
-#endif
-
 /*********** LINUX SPECIFIC SECTION ******************/
 
 /*
@@ -144,10 +139,29 @@
 #	define SCSI_NCR_SHARE_IRQ
 #endif
 
-/*
-**	Avoid to change these constants, unless you know what you are doing.
+/* ---------------------------------------------------------------------
+** Take into account kernel configured parameters.
+** Most of these options can be overridden at startup by a command line.
+** ---------------------------------------------------------------------
 */
 
+/*
+ * For Ultra SCSI support option, use special features and allow 20Mhz 
+ * synchronous data transfers.
+ */
+#if	1 /* CONFIG_SCSI_NCR53C8XX_ULTRA_SUPPORT */
+#define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(1)
+#define SCSI_NCR_SETUP_ULTRA_SCSI		(1)
+#define SCSI_NCR_MAX_SYNC (20000)
+#else
+#define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(0)
+#define SCSI_NCR_SETUP_ULTRA_SCSI		(0)
+#define SCSI_NCR_MAX_SYNC (10000)
+#endif
+
+/*
+ * Allow tags from 2 to 12, default 4
+ */
 #ifdef	CONFIG_SCSI_NCR53C8XX_MAX_TAGS
 #if	CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2
 #define SCSI_NCR_MAX_TAGS	(2)
@@ -160,57 +174,91 @@
 #define SCSI_NCR_MAX_TAGS	(4)
 #endif
 
-#define SCSI_NCR_ALWAYS_SIMPLE_TAG
-
-#ifdef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
-#define SCSI_NCR_DEFAULT_TAGS	SCSI_NCR_MAX_TAGS
+/*
+ * Allow tagged command queuing support if configured with default number 
+ * of tags set to max (see above).
+ */
+#ifdef	CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+#define	SCSI_NCR_SETUP_DEFAULT_TAGS	SCSI_NCR_MAX_TAGS
 #else
-#define SCSI_NCR_DEFAULT_TAGS	(0)
+#define	SCSI_NCR_SETUP_DEFAULT_TAGS	(0)
 #endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_IOMAPPED
+/*
+ * Use normal IO if configured. Forced for alpha.
+ */
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__)
 #define	SCSI_NCR_IOMAPPED
 #endif
 
+/*
+ * Sync transfer frequency at startup.
+ * Allow from 5Mhz to 20Mhz default 10 Mhz.
+ */
 #ifdef	CONFIG_SCSI_NCR53C8XX_SYNC
 #if	CONFIG_SCSI_NCR53C8XX_SYNC == 0
-#define	SCSI_NCR_DEFAULT_SYNC	(0)
-#elif	CONFIG_SCSI_NCR53C8XX_SYNC < 5
-#define	SCSI_NCR_DEFAULT_SYNC	(5000)
-#elif	CONFIG_SCSI_NCR53C8XX_SYNC > 10
-#define	SCSI_NCR_DEFAULT_SYNC	(10000)
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(0)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC*1000 < 5000
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(5000)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC*1000 > SCSI_NCR_MAX_SYNC
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	SCSI_NCR_MAX_SYNC
 #else
-#define	SCSI_NCR_DEFAULT_SYNC	(CONFIG_SCSI_NCR53C8XX_SYNC * 1000)
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(CONFIG_SCSI_NCR53C8XX_SYNC * 1000)
 #endif
 #else
-#define	SCSI_NCR_DEFAULT_SYNC	(10000)
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(10000)
 #endif
 
+/*
+ * Default sync to 0 will force asynchronous at startup
+ */
 #ifdef	CONFIG_SCSI_FORCE_ASYNCHRONOUS
-#undef	SCSI_NCR_DEFAULT_SYNC
-#define SCSI_NCR_DEFAULT_SYNC	(0)
+#undef	SCSI_NCR_SETUP_DEFAULT_SYNC
+#define SCSI_NCR_SETUP_DEFAULT_SYNC	(0)
 #endif
 
+/*
+ * Disallow disconnections at boot-up
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-#define SCSI_NCR_NO_DISCONNECT
+#define SCSI_NCR_SETUP_DISCONNECTION	(0)
+#else
+#define SCSI_NCR_SETUP_DISCONNECTION	(1)
 #endif
 
+/*
+ * Force synchronous negotiation for all targets
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-#define SCSI_NCR_FORCE_SYNC_NEGO
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO	(1)
+#else
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO	(0)
 #endif
 
+/*
+ * Disable master parity checking (flawed hardwares need that)
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-#define SCSI_NCR_DISABLE_MPARITY_CHECK
+#define SCSI_NCR_SETUP_MASTER_PARITY	(0)
+#else
+#define SCSI_NCR_SETUP_MASTER_PARITY	(1)
 #endif
 
+/*
+ * Disable scsi parity checking (flawed devices may need that)
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-#define SCSI_NCR_DISABLE_PARITY_CHECK
+#define SCSI_NCR_SETUP_SCSI_PARITY	(0)
+#else
+#define SCSI_NCR_SETUP_SCSI_PARITY	(1)
 #endif
 
-#if 0
-#define SCSI_NCR_SEGMENT_SIZE	(512)
-#endif
+/*
+**	Other parameters not configurable with "make config"
+**	Avoid to change these constants, unless you know what you are doing.
+*/
 
+#define SCSI_NCR_ALWAYS_SIMPLE_TAG
 #define SCSI_NCR_MAX_SCATTER	(128)
 #define SCSI_NCR_MAX_TARGET	(16)
 #define SCSI_NCR_MAX_HOST	(2)
@@ -221,13 +269,32 @@
 #define SCSI_NCR_CMD_PER_LUN	(SCSI_NCR_MAX_TAGS)
 #define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER-1)
 
+#define SCSI_NCR_TIMER_INTERVAL	((HZ+5-1)/5)
+
 #if 1 /* defined CONFIG_SCSI_MULTI_LUN */
 #define SCSI_NCR_MAX_LUN	(8)
 #else
 #define SCSI_NCR_MAX_LUN	(1)
 #endif
 
-#define SCSI_NCR_TIMER_INTERVAL	((HZ+5-1)/5)
+/*
+**	Initial setup.
+**	Can be overriden at startup by a command line.
+*/
+#define SCSI_NCR_DRIVER_SETUP			\
+{						\
+	SCSI_NCR_SETUP_MASTER_PARITY,		\
+	SCSI_NCR_SETUP_SCSI_PARITY,		\
+	SCSI_NCR_SETUP_DISCONNECTION,		\
+	SCSI_NCR_SETUP_SPECIAL_FEATURES,	\
+	SCSI_NCR_SETUP_ULTRA_SCSI,		\
+	SCSI_NCR_SETUP_FORCE_SYNC_NEGO,		\
+	1,					\
+	SCSI_NCR_SETUP_DEFAULT_TAGS,		\
+	SCSI_NCR_SETUP_DEFAULT_SYNC,		\
+	0x00					\
+}
+
 
 /*
 **	Define Scsi_Host_Template parameters
@@ -237,7 +304,11 @@
 
 #if defined(HOSTS_C) || defined(MODULE)
 
+#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
 #include <scsi/scsicam.h>
+#else
+#include <linux/scsicam.h>
+#endif
 
 int ncr53c8xx_abort(Scsi_Cmnd *);
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt);
@@ -516,12 +587,18 @@
 	#define   CSIGP   0x40
 
 /*1b*/  u_char    nc_ctest3;
-	#define   CLF	  0x04	/* clear scsi fifo		    */
+	#define   FLF     0x08  /* cmd: flush dma fifo              */
+	#define   CLF	  0x04	/* cmd: clear dma fifo		    */
+	#define   FM      0x02  /* mod: fetch pin mode              */
+	#define   WRIE    0x01  /* mod: write and invalidate enable */
 
 /*1c*/  u_int32    nc_temp;	/* ### Temporary stack              */
 
 /*20*/	u_char	  nc_dfifo;
 /*21*/  u_char    nc_ctest4;
+	#define   BDIS    0x80  /* mod: burst disable               */
+	#define   MPEE    0x08  /* mod: master parity error enable  */
+
 /*22*/  u_char    nc_ctest5;
 /*23*/  u_char    nc_ctest6;
 
@@ -532,13 +609,25 @@
 /*34*/  u_int32    nc_scratcha;  /* ??? Temporary register a         */
 
 /*38*/  u_char    nc_dmode;
+	#define   BL_2    0x80  /* mod: burst length shift value +2 */
+	#define   BL_1    0x40  /* mod: burst length shift value +1 */
+	#define   ERL     0x08  /* mod: enable read line            */
+	#define   ERMP    0x04  /* mod: enable read multiple        */
+	#define   BOF     0x02  /* mod: burst op code fetch         */
+
 /*39*/  u_char    nc_dien;
 /*3a*/  u_char    nc_dwt;
 
 /*3b*/  u_char    nc_dcntl;	/* --> Script execution control     */
-        #define   SSM     0x10  /* mod: single step mode            */
-        #define   STD     0x04  /* cmd: start dma mode              */
-	#define	  NOCOM   0x01	/* cmd: protect sfbr while reselect */
+
+	#define   CLSE    0x80  /* mod: cache line size enable      */
+	#define   PFF     0x40  /* cmd: pre-fetch flush             */
+	#define   PFEN    0x20  /* mod: pre-fetch enable            */
+	#define   SSM     0x10  /* mod: single step mode            */
+	#define   IRQM    0x08  /* mod: irq mode (1 = totem pole !) */
+	#define   STD     0x04  /* cmd: start dma mode              */
+	#define   IRQD    0x02  /* mod: irq disable                 */
+ 	#define	  NOCOM   0x01	/* cmd: protect sfbr while reselect */
 
 /*3c*/  u_int32    nc_adder;
 
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.1.19/linux/drivers/scsi/st.c	Mon Dec 30 15:39:11 1996
+++ linux/drivers/scsi/st.c	Wed Jan  1 18:54:02 1997
@@ -6,12 +6,12 @@
   Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
   Contribution and ideas from several people including (in alphabetical
   order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
-  Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+  Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
 
-  Copyright 1992 - 1996 Kai Makisara
+  Copyright 1992 - 1997 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Tue Oct 22 20:59:52 1996 by root@kai.makisara.fi
+  Last modified: Wed Jan  1 15:26:54 1997 by makisara@kai.makisara.fi
   Some small formal changes - aeb, 950809
 */
 
@@ -88,7 +88,7 @@
 static int st_write_threshold = ST_WRITE_THRESHOLD;
 static int st_max_buffers = ST_MAX_BUFFERS;
 
-Scsi_Tape * scsi_tapes = NULL;
+static Scsi_Tape * scsi_tapes = NULL;
 
 static int modes_defined = FALSE;
 
@@ -130,6 +130,9 @@
 
   if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
     return 0;
+
+  scode = sense[2] & 0x0f;
+
 #if DEBUG
   if (debugging) {
     printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
@@ -142,10 +145,8 @@
     else
       printk("\n");
   }
+  else
 #endif
-  scode = sense[2] & 0x0f;
-
-#if !DEBUG
   if (!(driver_byte(result) & DRIVER_SENSE) ||
       ((sense[0] & 0x70) == 0x70 &&
        scode != NO_SENSE &&
@@ -162,7 +163,6 @@
     else
       printk(KERN_WARNING "st%d: Error %x.\n", dev, result);
   }
-#endif
 
   if ((sense[0] & 0x70) == 0x70 &&
       scode == RECOVERED_ERROR
@@ -273,6 +273,7 @@
 write_behind_check(Scsi_Tape *STp)
 {
   ST_buffer * STbuffer;
+  ST_partstat * STps;
 
   STbuffer = STp->buffer;
 
@@ -290,11 +291,12 @@
 	   STbuffer->b_data + STbuffer->writing,
 	   STbuffer->buffer_bytes - STbuffer->writing);
   STbuffer->buffer_bytes -= STbuffer->writing;
-  if (STp->drv_block >= 0) {
+  STps = &(STp->ps[STp->partition]);
+  if (STps->drv_block >= 0) {
     if (STp->block_size == 0)
-      STp->drv_block++;
+      STps->drv_block++;
     else
-      STp->drv_block += STbuffer->writing / STp->block_size;
+      STps->drv_block += STbuffer->writing / STp->block_size;
   }
   STbuffer->writing = 0;
 
@@ -302,30 +304,37 @@
 }
 
 
-/* Back over EOF if it has been inadvertently crossed (ioctl not used because
+/* Step over EOF if it has been inadvertently crossed (ioctl not used because
    it messes up the block number). */
 	static int
-back_over_eof(Scsi_Tape *STp)
+cross_eof(Scsi_Tape *STp, int forward)
 {
   Scsi_Cmnd *SCpnt;
   unsigned char cmd[10];
 
   cmd[0] = SPACE;
   cmd[1] = 0x01; /* Space FileMarks */
-  cmd[2] = cmd[3] = cmd[4] = 0xff;  /* -1 filemarks */
+  if (forward) {
+      cmd[2] = cmd[3] = 0;
+      cmd[4] = 1;
+  }
+  else
+      cmd[2] = cmd[3] = cmd[4] = 0xff;  /* -1 filemarks */
   cmd[5] = 0;
+#if DEBUG
+  if (debugging)
+    printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n",
+	   TAPE_NR(STp->devt), forward ? "forward" : "backward");
+#endif
 
   SCpnt = st_do_scsi(NULL, STp, cmd, 0, ST_TIMEOUT, MAX_RETRIES);
   if (!SCpnt)
     return (-EBUSY);
 
   SCpnt->request.rq_status = RQ_INACTIVE;
-  if ((STp->buffer)->last_result != 0) {
-    printk(KERN_ERR "st%d: Backing over filemark failed.\n", TAPE_NR(STp->devt));
-    if ((STp->mt_status)->mt_fileno >= 0)
-      (STp->mt_status)->mt_fileno += 1;
-    (STp->mt_status)->mt_blkno = 0;
-  }
+  if ((STp->buffer)->last_result != 0)
+    printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n",
+	   TAPE_NR(STp->devt), forward ? "forward" : "backward");
 
   return (STp->buffer)->last_result_fatal;
 }
@@ -339,6 +348,7 @@
   int result;
   unsigned char cmd[10];
   Scsi_Cmnd *SCpnt;
+  ST_partstat * STps;
 
   if ((STp->buffer)->writing) {
     write_behind_check(STp);
@@ -381,6 +391,7 @@
     if (!SCpnt)
       return (-EBUSY);
 
+    STps = &(STp->ps[STp->partition]);
     if ((STp->buffer)->last_result_fatal != 0) {
       if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 	  (SCpnt->sense_buffer[2] & 0x40) &&
@@ -393,11 +404,11 @@
 	printk(KERN_ERR "st%d: Error on flush.\n", TAPE_NR(STp->devt));
 	result = (-EIO);
       }
-      STp->drv_block = (-1);
+      STps->drv_block = (-1);
     }
     else {
-      if (STp->drv_block >= 0)
-	STp->drv_block += blks;
+      if (STps->drv_block >= 0)
+	STps->drv_block += blks;
       STp->dirty = 0;
       (STp->buffer)->buffer_bytes = 0;
     }
@@ -415,6 +426,7 @@
   int backspace, result;
   Scsi_Tape * STp;
   ST_buffer * STbuffer;
+  ST_partstat * STps;
   int dev = TAPE_NR(inode->i_rdev);
 
   STp = &(scsi_tapes[dev]);
@@ -430,14 +442,12 @@
   if (STp->ready != ST_READY)
     return 0;
 
-  if (STp->ps[STp->partition].rw == ST_WRITING)  /* Writing */
+  STps = &(STp->ps[STp->partition]);
+  if (STps->rw == ST_WRITING)  /* Writing */
     return flush_write_buffer(STp);
 
-  if (STp->block_size == 0) {
-    STp->eof = ST_NOEOF;
-    STp->eof_hit = 0;
+  if (STp->block_size == 0)
     return 0;
-  }
 
   backspace = ((STp->buffer)->buffer_bytes +
     (STp->buffer)->read_pointer) / STp->block_size -
@@ -447,20 +457,24 @@
   (STp->buffer)->read_pointer = 0;
   result = 0;
   if (!seek_next) {
-    if ((STp->eof == ST_FM) && !STp->eof_hit) {
-      result = back_over_eof(STp); /* Back over the EOF hit */
-      if (!result) {
-	STp->eof = ST_NOEOF;
-	STp->eof_hit = 0;
+    if (STps->eof == ST_FM_HIT) {
+      result = cross_eof(STp, FALSE); /* Back over the EOF hit */
+      if (!result)
+	  STps->eof = ST_NOEOF;
+      else {
+	  if (STps->drv_file >= 0)
+	      STps->drv_file++;
+	  STps->drv_block = 0;
       }
     }
     if (!result && backspace > 0)
       result = st_int_ioctl(inode, MTBSR, backspace);
   }
-  else if ((STp->eof == ST_FM) && !STp->eof_hit) {
-    if ((STp->mt_status)->mt_fileno >= 0)
-      (STp->mt_status)->mt_fileno++;
-    STp->drv_block = 0;
+  else if (STps->eof == ST_FM_HIT) {
+    if (STps->drv_file >= 0)
+	STps->drv_file++;
+    STps->drv_block = 0;
+    STps->eof = ST_NOEOF;
   }
 
   return result;
@@ -514,6 +528,7 @@
     Scsi_Cmnd * SCpnt;
     Scsi_Tape * STp;
     ST_mode * STm;
+    ST_partstat * STps;
     int dev = TAPE_NR(inode->i_rdev);
     int mode = TAPE_MODE(inode->i_rdev);
 
@@ -561,12 +576,11 @@
     STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
 
     STp->dirty = 0;
-    for (i=0; i < ST_NBR_PARTITIONS; i++)
-      STp->ps[i].rw = ST_IDLE;
+    for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	STps = &(STp->ps[i]);
+	STps->rw = ST_IDLE;
+    }
     STp->ready = ST_READY;
-    if (STp->eof != ST_EOD)  /* Save EOD across opens */
-      STp->eof = ST_NOEOF;
-    STp->eof_hit = 0;
     STp->recover_count = 0;
 #if DEBUG
     STp->nbr_waits = STp->nbr_finished = 0;
@@ -589,23 +603,23 @@
 
     if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 	(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
-      (STp->mt_status)->mt_fileno = 0 ;
       memset ((void *) &cmd[0], 0, 10);
       cmd[0] = TEST_UNIT_READY;
 
       SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, ST_LONG_TIMEOUT, MAX_READY_RETRIES);
 
-      (STp->mt_status)->mt_fileno = STp->drv_block = 0;
-      STp->eof = ST_NOEOF;
       (STp->device)->was_reset = 0;
       STp->partition = STp->new_partition = 0;
       if (STp->can_partitions)
 	STp->nbr_partitions = 1;  /* This guess will be updated later if necessary */
       for (i=0; i < ST_NBR_PARTITIONS; i++) {
-	STp->ps[i].rw = ST_IDLE;
-	STp->ps[i].moves_after_eof = 1;
-	STp->ps[i].at_sm = 0;
-	STp->ps[i].last_block_valid = FALSE;
+	  STps = &(STp->ps[i]);
+	  STps->rw = ST_IDLE;
+	  STps->eof = ST_NOEOF;
+	  STps->at_sm = 0;
+	  STps->last_block_valid = FALSE;
+	  STps->drv_block = 0;
+	  STps->drv_file = 0 ;
       }
       new_session = TRUE;
     }
@@ -613,19 +627,15 @@
     if ((STp->buffer)->last_result_fatal != 0) {
       if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 	  (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
-	(STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
 	printk(KERN_NOTICE "st%d: No tape.\n", dev);
 	STp->ready = ST_NO_TAPE;
-      } else {
-	(STp->mt_status)->mt_fileno = STp->drv_block = (-1);
+      } else
 	STp->ready = ST_NOT_READY;
-      }
       SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
       STp->density = 0;   	/* Clear the erroneous "residue" */
       STp->write_prot = 0;
       STp->block_size = 0;
-      STp->eof = ST_NOEOF;
-      (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+      STp->ps[0].drv_file = STp->ps[0].drv_block = 0;
       STp->partition = STp->new_partition = 0;
       STp->door_locked = ST_UNLOCKED;
       STp->in_use = 1;
@@ -796,11 +806,16 @@
     static unsigned char cmd[10];
     Scsi_Cmnd * SCpnt;
     Scsi_Tape * STp;
+    ST_mode * STm;
+    ST_partstat * STps;
+
     kdev_t devt = inode->i_rdev;
     int dev;
 
     dev = TAPE_NR(devt);
     STp = &(scsi_tapes[dev]);
+    STm = &(STp->modes[STp->current_mode]);
+    STps = &(STp->ps[STp->partition]);
 
     if (STp->can_partitions &&
 	update_partition(inode) < 0) {
@@ -811,7 +826,7 @@
       goto out;
     }
 
-    if ( STp->ps[STp->partition].rw == ST_WRITING && !(STp->device)->was_reset) {
+    if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
 
       result = flush_write_buffer(STp);
 
@@ -845,11 +860,12 @@
 	       SCpnt->sense_buffer[6]) == 0)))  /* Filter out successful write at EOM */
 	  printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
 	else {
-	  if ((STp->mt_status)->mt_fileno >= 0)
-	      (STp->mt_status)->mt_fileno++ ;
-	  STp->drv_block = 0;
+	  if (STps->drv_file >= 0)
+	      STps->drv_file++ ;
+	  STps->drv_block = 0;
 	  if (STp->two_fm)
-	    back_over_eof(STp);
+	    cross_eof(STp, FALSE);
+	  STps->eof = ST_FM;
 	}
       }
 
@@ -860,10 +876,28 @@
 #endif
     }
     else if (!STp->rew_at_close) {
-      if (STp->can_bsr)
-	flush_buffer(inode, filp, 0);
-      else if ((STp->eof == ST_FM) && !STp->eof_hit)
-	back_over_eof(STp);
+	STps = &(STp->ps[STp->partition]);
+	if (!STm->sysv || STps->rw != ST_READING) {
+	    if (STp->can_bsr)
+		flush_buffer(inode, filp, 0);
+	    else if (STps->eof == ST_FM_HIT) {
+		if (cross_eof(STp, FALSE)) {
+		    if (STps->drv_file >= 0)
+			STps->drv_file++;
+		    STps->drv_block = 0;
+		    STps->eof = ST_FM;
+		}
+		else
+		    STps->eof = ST_NOEOF;
+	    }
+	}
+	else if ((STps->eof == ST_NOEOF && !cross_eof(STp, TRUE)) ||
+		 STps->eof == ST_FM_HIT) {
+	    if (STps->drv_file >= 0)
+		STps->drv_file++;
+	    STps->drv_block = 0;
+	    STps->eof = ST_FM;
+	}
     }
 
 out:
@@ -893,7 +927,7 @@
 	 unsigned long count)
 {
     long total;
-    int do_count, blks, retval, transfer;
+    int i, do_count, blks, retval, transfer;
     int write_threshold;
     int doing_write = 0;
     static unsigned char cmd[10];
@@ -910,6 +944,8 @@
     STm = &(STp->modes[STp->current_mode]);
     if (!STm->defined)
       return (-ENXIO);
+    if (count == 0)
+      return 0;
 
     /*
      * If there was a bus reset, block further access
@@ -949,7 +985,7 @@
       STps->rw = ST_WRITING;
     }
     else if (STps->rw != ST_WRITING &&
-	     (STp->mt_status)->mt_fileno == 0 && STp->drv_block == 0) {
+	     STps->drv_file == 0 && STps->drv_block == 0) {
       if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
 	return retval;
       if (STm->default_compression != ST_DONT_TOUCH &&
@@ -963,9 +999,6 @@
       }
     }
 
-    if (STps->moves_after_eof < 255)
-      STps->moves_after_eof++;
-
     if ((STp->buffer)->writing) {
       write_behind_check(STp);
       if ((STp->buffer)->last_result_fatal) {
@@ -974,20 +1007,24 @@
 	  printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", dev,
 		 (STp->buffer)->last_result);
 #endif
-	if ((STp->buffer)->last_result == INT_MAX) {
-	  retval = (-ENOSPC);  /* All has been written */
-	  STp->eof = ST_EOM_OK;
-	}
+	if ((STp->buffer)->last_result == INT_MAX)
+	  STps->eof = ST_EOM_OK;
 	else
-	  retval = (-EIO);
-	return retval;
+	  STps->eof = ST_EOM_ERROR;
       }
     }
-    if (STp->eof == ST_EOM_OK)
+    if (STps->eof == ST_EOM_OK)
       return (-ENOSPC);
-    else if (STp->eof == ST_EOM_ERROR)
+    else if (STps->eof == ST_EOM_ERROR)
       return (-EIO);
 
+    /* Check the buffer readability in cases where copy_user might catch
+       the problems after some tape movement. */
+    if (STp->block_size != 0 &&
+	(copy_from_user(&i, buf, 1) != 0 ||
+	 copy_from_user(&i, buf + count - 1, 1) != 0))
+	return (-EFAULT);
+
     if (!STm->do_buffer_writes) {
       if (STp->block_size != 0 && (count % STp->block_size) != 0)
 	return (-EIO);   /* Write must be integral number of blocks */
@@ -1020,8 +1057,14 @@
 	if (do_count > count)
 	  do_count = count;
       }
-      copy_from_user((STp->buffer)->b_data +
-		    (STp->buffer)->buffer_bytes, b_point, do_count);
+
+      i = copy_from_user((STp->buffer)->b_data +
+			 (STp->buffer)->buffer_bytes, b_point, do_count);
+      if (i) {
+	  if (SCpnt != NULL)
+	      SCpnt->request.rq_status = RQ_INACTIVE;
+	  return (-EFAULT);
+      }
 
       if (STp->block_size == 0)
 	blks = transfer = do_count;
@@ -1059,13 +1102,13 @@
 	  if (transfer <= do_count) {
 	    filp->f_pos += do_count - transfer;
 	    count -= do_count - transfer;
-	    if (STp->drv_block >= 0) {
+	    if (STps->drv_block >= 0) {
 	      if (STp->block_size == 0 && transfer < do_count)
-		STp->drv_block++;
+		STps->drv_block++;
 	      else if (STp->block_size != 0)
-		STp->drv_block += (do_count - transfer) / STp->block_size;
+		STps->drv_block += (do_count - transfer) / STp->block_size;
 	    }
-	    STp->eof = ST_EOM_OK;
+	    STps->eof = ST_EOM_OK;
 	    retval = (-ENOSPC); /* EOM within current request */
 #if DEBUG
 	    if (debugging)
@@ -1074,8 +1117,8 @@
 #endif
 	  }
 	  else {
-	    STp->eof = ST_EOM_ERROR;
-	    STp->drv_block = (-1);    /* Too cautious? */
+	    STps->eof = ST_EOM_ERROR;
+	    STps->drv_block = (-1);    /* Too cautious? */
 	    retval = (-EIO); /* EOM for old data */
 #if DEBUG
 	    if (debugging)
@@ -1084,7 +1127,7 @@
 	  }
 	}
 	else {
-	  STp->drv_block = (-1);    /* Too cautious? */
+	  STps->drv_block = (-1);    /* Too cautious? */
 	  retval = (-EIO);
 	}
 
@@ -1099,19 +1142,24 @@
       filp->f_pos += do_count;
       b_point += do_count;
       count -= do_count;
-      if (STp->drv_block >= 0) {
+      if (STps->drv_block >= 0) {
 	if (STp->block_size == 0)
-	  STp->drv_block++;
+	  STps->drv_block++;
 	else
-	  STp->drv_block += blks;
+	  STps->drv_block += blks;
       }
       (STp->buffer)->buffer_bytes = 0;
       STp->dirty = 0;
     }
     if (count != 0) {
       STp->dirty = 1;
-      copy_from_user((STp->buffer)->b_data +
-		    (STp->buffer)->buffer_bytes,b_point,count);
+      i = copy_from_user((STp->buffer)->b_data +
+			 (STp->buffer)->buffer_bytes, b_point, count);
+      if (i) {
+	  if (SCpnt != NULL)
+	      SCpnt->request.rq_status = RQ_INACTIVE;
+	  return (-EFAULT);
+      }
       filp->f_pos += count;
       (STp->buffer)->buffer_bytes += count;
       count = 0;
@@ -1163,8 +1211,199 @@
       SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
     STps->at_sm &= (total == 0);
+    if (total > 0)
+	STps->eof = ST_NOEOF;
+
     return( total);
 }   
+
+/* Read data from the tape. Returns zero in the normal case, one if the
+   eof status has changed, and the negative error code in case of a
+   fatal error. Otherwise updates the buffer and the eof state. */
+	static long
+read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
+{
+    int transfer, blks, bytes;
+    static unsigned char cmd[10];
+    Scsi_Cmnd *SCpnt;
+    Scsi_Tape *STp;
+    ST_mode * STm;
+    ST_partstat * STps;
+    int dev = TAPE_NR(inode->i_rdev);
+    int retval = 0;
+
+    if (count == 0)
+      return 0;
+
+    STp = &(scsi_tapes[dev]);
+    STm = &(STp->modes[STp->current_mode]);
+    STps = &(STp->ps[STp->partition]);
+    if (STps->eof == ST_FM_HIT)
+	return 1;
+
+    memset(cmd, 0, 10);
+    cmd[0] = READ_6;
+    cmd[1] = (STp->block_size != 0);
+    if (STp->block_size == 0)
+	blks = bytes = count;
+    else {
+	if (STm->do_read_ahead) {
+	    blks = (STp->buffer)->buffer_blocks;
+	    bytes = blks * STp->block_size;
+	}
+	else {
+	    bytes = count;
+	    if (bytes > (STp->buffer)->buffer_size)
+		bytes = (STp->buffer)->buffer_size;
+	    blks = bytes / STp->block_size;
+	    bytes = blks * STp->block_size;
+	}
+    }
+    cmd[2] = blks >> 16;
+    cmd[3] = blks >> 8;
+    cmd[4] = blks;
+
+    SCpnt = *aSCpnt;
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, ST_TIMEOUT, MAX_RETRIES);
+    *aSCpnt = SCpnt;
+    if (!SCpnt)
+	return (-EBUSY);
+
+    (STp->buffer)->read_pointer = 0;
+    STps->at_sm = 0;
+
+
+    /* Something to check */
+    if ((STp->buffer)->last_result_fatal) {
+	retval = 1;
+#if DEBUG
+	if (debugging)
+	    printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+		   dev,
+		   SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
+		   SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
+		   SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
+		   SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
+#endif
+	if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
+
+	    if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+		SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
+
+	    if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
+		/* Compute the residual count */
+		if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+		    transfer = (SCpnt->sense_buffer[3] << 24) |
+			(SCpnt->sense_buffer[4] << 16) |
+			(SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+		else
+		    transfer = 0;
+		if (STp->block_size == 0 &&
+		    (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
+		    transfer = bytes;
+
+		if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */
+		    if (STp->block_size == 0) {
+			if (transfer <= 0)
+			    transfer = 0;
+			(STp->buffer)->buffer_bytes = bytes - transfer;
+		    }
+		    else {
+			SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+			SCpnt = NULL;
+			if (transfer == blks) {  /* We did not get anything, error */
+			    printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
+			    if (STps->drv_block >= 0)
+				STps->drv_block += blks - transfer + 1;
+			    st_int_ioctl(inode, MTBSR, 1);
+			    return (-EIO);
+			}
+			/* We have some data, deliver it */
+			(STp->buffer)->buffer_bytes = (blks - transfer) *
+			    STp->block_size;
+#if DEBUG
+			if (debugging)
+			    printk(ST_DEB_MSG
+				   "st%d: ILI but enough data received %ld %d.\n",
+				   dev, count, (STp->buffer)->buffer_bytes);
+#endif
+			if (STps->drv_block >= 0)
+			    STps->drv_block += 1;
+			if (st_int_ioctl(inode, MTBSR, 1))
+			    return (-EIO);
+		    }
+		}
+		else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */
+		    if (STps->eof != ST_FM_HIT)
+			STps->eof = ST_FM_HIT;
+		    else
+			STps->eof = ST_EOD_2;
+		    if (STp->block_size == 0)
+			(STp->buffer)->buffer_bytes = 0;
+		    else
+			(STp->buffer)->buffer_bytes =
+			    bytes - transfer * STp->block_size;
+#if DEBUG
+		    if (debugging)
+			printk(ST_DEB_MSG
+			 "st%d: EOF detected (%d bytes read).\n",
+			       dev, (STp->buffer)->buffer_bytes);
+#endif
+		}
+		else if (SCpnt->sense_buffer[2] & 0x40) {
+		    if (STps->eof == ST_FM)
+			STps->eof = ST_EOD_1;
+		    else
+			STps->eof = ST_EOM_OK;
+		    if (STp->block_size == 0)
+			(STp->buffer)->buffer_bytes = bytes - transfer;
+		    else
+			(STp->buffer)->buffer_bytes =
+			    bytes - transfer * STp->block_size;
+#if DEBUG
+		    if (debugging)
+			printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n",
+			       dev, (STp->buffer)->buffer_bytes);
+#endif
+		}
+	    } /* end of EOF, EOM, ILI test */
+	    else { /* nonzero sense key */
+#if DEBUG
+		if (debugging)
+		    printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev);
+#endif
+		STps->drv_block = (-1);
+		if (STps->eof == ST_FM &&
+		    (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
+#if DEBUG
+		    if (debugging)
+			printk(ST_DEB_MSG
+			       "st%d: Zero returned for first BLANK CHECK after EOF.\n",
+			       dev);
+#endif
+		    STps->eof = ST_EOD_2;  /* First BLANK_CHECK after FM */
+		}
+		else /* Some other extended sense code */
+		    retval = (-EIO);
+	    }
+	} /* End of extended sense test */
+	else {  /* Non-extended sense */
+	    retval = (STp->buffer)->last_result_fatal;
+	}
+
+    } /* End of error handling */
+    else /* Read successful */
+	(STp->buffer)->buffer_bytes = bytes;
+
+    if (STps->drv_block >= 0) {
+	if (STp->block_size == 0)
+	    STps->drv_block++;
+	else
+	    STps->drv_block += (STp->buffer)->buffer_bytes / STp->block_size;
+    }
+
+    return retval;
+}
 
 
 /* Read command */
@@ -1172,8 +1411,8 @@
 st_read(struct inode * inode, struct file * filp, char * buf, unsigned long count)
 {
     long total;
-    int transfer, blks, bytes;
-    static unsigned char cmd[10];
+    int i, transfer;
+    int special;
     Scsi_Cmnd * SCpnt = NULL;
     Scsi_Tape * STp;
     ST_mode * STm;
@@ -1196,7 +1435,6 @@
     if (STp->can_partitions &&
 	(total = update_partition(inode)) < 0)
       return total;
-    STps = &(STp->ps[STp->partition]);
 
     if (STp->block_size == 0 &&
 	count > (STp->buffer)->buffer_size &&
@@ -1211,232 +1449,103 @@
 	!st_int_ioctl(inode, MTLOCK, 0))
       STp->door_locked = ST_LOCKED_AUTO;
 
+    STps = &(STp->ps[STp->partition]);
     if (STps->rw == ST_WRITING) {
       transfer = flush_buffer(inode, filp, 0);
       if (transfer)
 	return transfer;
       STps->rw = ST_READING;
     }
-    if (STps->moves_after_eof < 255)
-      STps->moves_after_eof++;
 
 #if DEBUG
-    if (debugging && STp->eof != ST_NOEOF)
-      printk(ST_DEB_MSG "st%d: EOF flag up. Bytes %d\n", dev,
-	     (STp->buffer)->buffer_bytes);
-#endif
-    if (((STp->buffer)->buffer_bytes == 0) &&
-	(STp->eof == ST_EOM_OK || STp->eof == ST_EOD))
-      return (-EIO);  /* EOM or Blank Check */
-
-    STps->rw = ST_READING;
-
-    for (total = 0; total < count; ) {
-
-      if ((STp->buffer)->buffer_bytes == 0 &&
-	  STp->eof == ST_NOEOF) {
-
-	memset(cmd, 0, 10);
-	cmd[0] = READ_6;
-	cmd[1] = (STp->block_size != 0);
-	if (STp->block_size == 0)
-	  blks = bytes = count;
-	else {
-	  if (STm->do_read_ahead) {
-	    blks = (STp->buffer)->buffer_blocks;
-	    bytes = blks * STp->block_size;
-	  }
-	  else {
-	    bytes = count - total;
-	    if (bytes > (STp->buffer)->buffer_size)
-	      bytes = (STp->buffer)->buffer_size;
-	    blks = bytes / STp->block_size;
-	    bytes = blks * STp->block_size;
-	  }
+    if (debugging && STps->eof != ST_NOEOF)
+      printk(ST_DEB_MSG "st%d: EOF/EOM flag up (%d). Bytes %d\n", dev,
+	     STps->eof, (STp->buffer)->buffer_bytes);
+#endif
+    if ((STp->buffer)->buffer_bytes == 0 &&
+	STps->eof >= ST_EOD_1) {
+	if (STps->eof < ST_EOD) {
+	    STps->eof += 1;
+	    return 0;
 	}
-	cmd[2] = blks >> 16;
-	cmd[3] = blks >> 8;
-	cmd[4] = blks;
-
-	SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, ST_TIMEOUT, MAX_RETRIES);
-	if (!SCpnt)
-	  return (-EBUSY);
+	return (-EIO);  /* EOM or Blank Check */
+    }
 
-	(STp->buffer)->read_pointer = 0;
-	STp->eof_hit = 0;
-	STps->at_sm = 0;
+    /* Check the buffer writability before any tape movement. Don't alter
+       buffer data. */
+    if (copy_from_user(&i, buf, 1) != 0 ||
+	copy_to_user(buf, &i, 1) != 0 ||
+	copy_from_user(&i, buf + count - 1, 1) != 0 ||
+	copy_to_user(buf + count - 1, &i, 1) != 0)
+	return (-EFAULT);
 
-	if ((STp->buffer)->last_result_fatal) {
-#if DEBUG
-	  if (debugging)
-	    printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
-		   dev,
-		   SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
-		   SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
-		   SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
-		   SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
-#endif
-	  if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
+    STps->rw = ST_READING;
 
-	    if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
-		SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
 
-	    if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
+    /* Loop until enough data in buffer or a special condition found */
+    for (total = 0, special = 0; total < count && !special; ) {
 
-	      if ((SCpnt->sense_buffer[0] & 0x80) != 0)
-		transfer = (SCpnt->sense_buffer[3] << 24) |
-		  (SCpnt->sense_buffer[4] << 16) |
-		    (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
-	      else
-		transfer = 0;
-	      if (STp->block_size == 0 &&
-		  (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
-		transfer = bytes;
-
-	      if (SCpnt->sense_buffer[2] & 0x20) {
-		if (STp->block_size == 0) {
-		  if (transfer <= 0)
-		    transfer = 0;
-		  (STp->buffer)->buffer_bytes = bytes - transfer;
-		}
-		else {
+      /* Get new data if the buffer is empty */
+      if ((STp->buffer)->buffer_bytes == 0) {
+	  special = read_tape(inode, count - total, &SCpnt);
+	  if (special < 0) { /* No need to continue read */
+	      if (SCpnt != NULL)
 		  SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
-		  if (transfer == blks) {  /* We did not get anything, signal error */
-		    printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
-		    if (STp->drv_block >= 0)
-		      STp->drv_block += blks - transfer + 1;
-		    st_int_ioctl(inode, MTBSR, 1);
-		    return (-EIO);
-		  }
-		  /* We have some data, deliver it */
-		  (STp->buffer)->buffer_bytes = (blks - transfer) * STp->block_size;
-#if DEBUG
-		  if (debugging)
-		    printk(ST_DEB_MSG "st%d: ILI but enough data received %ld %d.\n",
-			   dev, count - total, (STp->buffer)->buffer_bytes);
-#endif
-		  if (count - total > (STp->buffer)->buffer_bytes)
-		    count = total + (STp->buffer)->buffer_bytes;
-		  if (STp->drv_block >= 0)
-		    STp->drv_block += 1;
-		  if (st_int_ioctl(inode, MTBSR, 1))
-		    return (-EIO);
-		  SCpnt = NULL;
-		}
-	      }
-	      else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */
-		STp->eof = ST_FM;
-		if (STp->block_size == 0)
-		  (STp->buffer)->buffer_bytes = 0;
-		else
-		  (STp->buffer)->buffer_bytes =
-		    bytes - transfer * STp->block_size;
-#if DEBUG
-		if (debugging)
-		  printk(ST_DEB_MSG
-		    "st%d: EOF detected (%d bytes read, transferred %ld bytes).\n",
-			 dev, (STp->buffer)->buffer_bytes, total);
-#endif
-	      }
-	      else if (SCpnt->sense_buffer[2] & 0x40) {
-		STp->eof = ST_EOM_OK;
-		if (STp->block_size == 0)
-		  (STp->buffer)->buffer_bytes = bytes - transfer;
-		else
-		  (STp->buffer)->buffer_bytes =
-		    bytes - transfer * STp->block_size;
-#if DEBUG
-		if (debugging)
-		  printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n", dev,
-			 (STp->buffer)->buffer_bytes);
-#endif
-	      }
-	    } /* end of EOF, EOM, ILI test */
-	    else { /* nonzero sense key */
-#if DEBUG
-	      if (debugging)
-		printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev);
-#endif
-	      SCpnt->request.rq_status = RQ_INACTIVE;
-	      STp->drv_block = (-1);
-	      if (total)
-		return total;
-	      else if (STps->moves_after_eof == 1 &&
-		       (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
-#if DEBUG
-		if (debugging)
-		  printk(ST_DEB_MSG
-			 "st%d: Zero returned for first BLANK CHECK after EOF.\n",
-			 dev);
-#endif
-		STp->eof = ST_EOD;
-		return 0; /* First BLANK_CHECK after EOF */
-	      }
-	      else
-		return -EIO;
-	    }
-	  } /* End of extended sense test */
-	  else {
-	    transfer = (STp->buffer)->last_result_fatal;
-	    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
-	    return transfer;
+	      return special;
 	  }
-	} /* End of error handling */
-	else /* Read successful */
-	  (STp->buffer)->buffer_bytes = bytes;
-
-	if (STp->drv_block >= 0) {
-	  if (STp->block_size == 0)
-	    STp->drv_block++;
-	  else
-	    STp->drv_block += (STp->buffer)->buffer_bytes / STp->block_size;
-	}
-
-      } /* if ((STp->buffer)->buffer_bytes == 0 &&
-	   STp->eof == ST_NOEOF) */
+      }
 
+      /* Move the data from driver buffer to user buffer */
       if ((STp->buffer)->buffer_bytes > 0) {
 #if DEBUG
-	if (debugging && STp->eof != ST_NOEOF)
-	  printk(ST_DEB_MSG "st%d: EOF up. Left %d, needed %d.\n", dev,
-		 (STp->buffer)->buffer_bytes, count - total);
+	if (debugging && STps->eof != ST_NOEOF)
+	  printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %ld.\n", dev,
+		 STps->eof, (STp->buffer)->buffer_bytes, count - total);
 #endif
 	transfer = (STp->buffer)->buffer_bytes < count - total ?
 	  (STp->buffer)->buffer_bytes : count - total;
-	copy_to_user(buf, (STp->buffer)->b_data +
-		    (STp->buffer)->read_pointer,transfer);
+	i = copy_to_user(buf, (STp->buffer)->b_data +
+			 (STp->buffer)->read_pointer, transfer);
+	if (i) {
+	    if (SCpnt != NULL)
+		SCpnt->request.rq_status = RQ_INACTIVE;
+	    return (-EFAULT);
+	}
 	filp->f_pos += transfer;
 	buf += transfer;
 	total += transfer;
 	(STp->buffer)->buffer_bytes -= transfer;
 	(STp->buffer)->read_pointer += transfer;
       }
-      else if (STp->eof != ST_NOEOF) {
-	STp->eof_hit = 1;
-	if (SCpnt != NULL)
-	  SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
-	if (total == 0 && STp->eof == ST_FM) {
-	  STp->eof = ST_NOEOF;
-	  STp->eof_hit = 0;
-	  STp->drv_block = 0;
-	  if (STps->moves_after_eof > 1)
-	    STps->moves_after_eof = 0;
-	  if ((STp->mt_status)->mt_fileno >= 0)
-	    (STp->mt_status)->mt_fileno++;
-	}
-	if (total == 0 && STp->eof == ST_EOM_OK)
-	  return (-ENOSPC);  /* ST_EOM_ERROR not used in read */
-	return total;
-      }
 
       if (STp->block_size == 0)
-	count = total;  /* Read only one variable length block */
+	  break;  /* Read only one variable length block */
 
-    } /* for (total = 0; total < count; ) */
+    } /* for (total = 0, special = 0; total < count && !special; ) */
 
     if (SCpnt != NULL)
       SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
+    /* Change the eof state if no data from tape or buffer */
+    if (total == 0) {
+	if (STps->eof == ST_FM_HIT) {
+	    STps->eof = ST_FM;
+	    STps->drv_block = 0;
+	    if (STps->drv_file >= 0)
+		STps->drv_file++;
+	}
+	else if (STps->eof == ST_EOD_1) {
+	    STps->eof = ST_EOD_2;
+	    STps->drv_block = 0;
+	    if (STps->drv_file >= 0)
+		STps->drv_file++;
+	}
+	else if (STps->eof == ST_EOD_2)
+	    STps->eof = ST_EOD;
+    }
+    else if (STps->eof == ST_FM)
+	STps->eof = ST_NOEOF;
+
     return total;
 }
 
@@ -1705,6 +1814,7 @@
    int timeout = ST_LONG_TIMEOUT;
    long ltmp;
    int i, ioctl_result;
+   int chg_eof = TRUE;
    unsigned char cmd[10];
    Scsi_Cmnd * SCpnt;
    Scsi_Tape * STp;
@@ -1716,15 +1826,16 @@
    if (STp->ready != ST_READY && cmd_in != MTLOAD)
      return (-EIO);
    STps = &(STp->ps[STp->partition]);
-   fileno = (STp->mt_status)->mt_fileno ;
-   blkno = STp->drv_block;
+   fileno = STps->drv_file;
+   blkno = STps->drv_block;
    at_sm = STps->at_sm;
 
    memset(cmd, 0, 10);
    datalen = 0;
    switch (cmd_in) {
-     case MTFSF:
      case MTFSFM:
+       chg_eof = FALSE; /* Changed from the FSF after this */
+     case MTFSF:
        cmd[0] = SPACE;
        cmd[1] = 0x01; /* Space FileMarks */
        cmd[2] = (arg >> 16);
@@ -1740,8 +1851,9 @@
        blkno = 0;
        at_sm &= (arg == 0);
        break; 
-     case MTBSF:
      case MTBSFM:
+       chg_eof = FALSE; /* Changed from the FSF after this */
+     case MTBSF:
        cmd[0] = SPACE;
        cmd[1] = 0x01; /* Space FileMarks */
        ltmp = (-arg);
@@ -1877,6 +1989,21 @@
        cmd[0] = START_STOP;
        if (cmd_in == MTLOAD)
 	 cmd[4] |= 1;
+       /* 
+        * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A 
+       */
+       if (cmd_in != MTOFFL &&
+	   arg >= 1 + MT_ST_HPLOADER_OFFSET
+	   && arg <= 6 + MT_ST_HPLOADER_OFFSET) {
+#if DEBUG
+	   if (debugging) {
+	       printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n", 
+		      dev, (cmd[4]) ? "" : "un",
+		      arg - MT_ST_HPLOADER_OFFSET);
+	   }
+#endif
+	   cmd[3] = arg; /* MediaID field of C1553A */
+       }
 #if ST_NOWAIT
        cmd[1] = 1;  /* Don't wait for completion */
        timeout = ST_TIMEOUT;
@@ -1917,8 +2044,8 @@
        if (!STp->fast_mteom) {
 	 /* space to the end of tape */
 	 ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
-	 fileno = (STp->mt_status)->mt_fileno ;
-	 if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
+	 fileno = STps->drv_file;
+	 if (STps->eof >= ST_EOD_1)
 	   return 0;
 	 /* The next lines would hide the number of spaced FileMarks
 	    That's why I inserted the previous lines. I had no luck
@@ -1954,6 +2081,7 @@
        fileno = blkno = at_sm = 0 ;
        break;
      case MTLOCK:
+       chg_eof = FALSE;
        cmd[0] = ALLOW_MEDIUM_REMOVAL;
        cmd[4] = SCSI_REMOVAL_PREVENT;
 #if DEBUG
@@ -1962,6 +2090,7 @@
 #endif;
        break;
      case MTUNLOCK:
+       chg_eof = FALSE;
        cmd[0] = ALLOW_MEDIUM_REMOVAL;
        cmd[4] = SCSI_REMOVAL_ALLOW;
 #if DEBUG
@@ -1973,6 +2102,7 @@
      case MTSETDENSITY: /* Set tape density */
      case MTSETDRVBUFFER: /* Set drive buffering */
      case SET_DENS_AND_BLK: /* Set density and block size */
+       chg_eof = FALSE;
        if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
 	 return (-EIO);   /* Not allowed if data in buffer */
        if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
@@ -2040,64 +2170,63 @@
 
    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
-   if (cmd_in == MTFSF)
-     STps->moves_after_eof = 0;
-   else if (cmd_in != MTLOAD && cmd_in != MTLOCK && cmd_in != MTUNLOCK &&
-	    cmd_in != MTSETBLK && cmd_in != MTSETDENSITY &&
-	    cmd_in != MTSETDRVBUFFER)
-     STps->moves_after_eof = 1;
    if (!ioctl_result) {  /* SCSI command successful */
-     STp->drv_block = blkno;
-     (STp->mt_status)->mt_fileno = fileno;
+     STps->drv_block = blkno;
+     STps->drv_file = fileno;
      STps->at_sm = at_sm;
+
      if (cmd_in == MTLOCK)
        STp->door_locked = ST_LOCKED_EXPLICIT;
      else if (cmd_in == MTUNLOCK)
        STp->door_locked = ST_UNLOCKED;
+
      if (cmd_in == MTBSFM)
        ioctl_result = st_int_ioctl(inode, MTFSF, 1);
      else if (cmd_in == MTFSFM)
        ioctl_result = st_int_ioctl(inode, MTBSF, 1);
-     else if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
+
+     if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
        STp->block_size = arg & MT_ST_BLKSIZE_MASK;
        if (STp->block_size != 0)
 	 (STp->buffer)->buffer_blocks =
 	   (STp->buffer)->buffer_size / STp->block_size;
        (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
+       if (cmd_in == SET_DENS_AND_BLK)
+         STp->density = arg >> MT_ST_DENSITY_SHIFT;
      }
      else if (cmd_in == MTSETDRVBUFFER)
        STp->drv_buffer = (arg & 7);
      else if (cmd_in == MTSETDENSITY)
        STp->density = arg;
-     else if (cmd_in == MTEOM) {
-       STp->eof = ST_EOD;
-       STp->eof_hit = 0;
-     }
-     else if (cmd_in != MTSETBLK && cmd_in != MTNOP) {
-       STp->eof = ST_NOEOF;
-       STp->eof_hit = 0;
-     }
-     if (cmd_in == SET_DENS_AND_BLK)
-       STp->density = arg >> MT_ST_DENSITY_SHIFT;
+
+     if (cmd_in == MTEOM)
+       STps->eof = ST_EOD;
+     else if (cmd_in == MTFSF)
+       STps->eof = ST_FM;
+     else if (chg_eof)
+       STps->eof = ST_NOEOF;
+
+
      if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
        STp->rew_at_close = 0;
      else if (cmd_in == MTLOAD) {
        STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
        for (i=0; i < ST_NBR_PARTITIONS; i++) {
 	 STp->ps[i].rw = ST_IDLE;
-	 STp->ps[i].moves_after_eof = 1;
 	 STp->ps[i].last_block_valid = FALSE;
        }
        STp->partition = 0;
      }
+
    } else {  /* SCSI command was not completely successful */
+
      if (SCpnt->sense_buffer[2] & 0x40) {
        if (cmd_in != MTBSF && cmd_in != MTBSFM &&
 	   cmd_in != MTBSR && cmd_in != MTBSS)
-	 STp->eof = ST_EOM_OK;
-       STp->eof_hit = 0;
-       STp->drv_block = 0;
+	   STps->eof = ST_EOM_OK;
+       STps->drv_block = 0;
      }
+
      undone = (
 	  (SCpnt->sense_buffer[3] << 24) +
 	  (SCpnt->sense_buffer[4] << 16) +
@@ -2110,50 +2239,64 @@
        ioctl_result = 0;  /* EOF written succesfully at EOM */
        if (fileno >= 0)
 	 fileno++;
-       (STp->mt_status)->mt_fileno = fileno;
+       STps->drv_file = fileno;
+       STps->eof = ST_NOEOF;
      }
      else if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
        if (fileno >= 0)
-	 (STp->mt_status)->mt_fileno = fileno - undone ;
+	 STps->drv_file = fileno - undone ;
        else
-	 (STp->mt_status)->mt_fileno = fileno;
-       STp->drv_block = 0;
+	 STps->drv_file = fileno;
+       STps->drv_block = 0;
+       STps->eof = ST_NOEOF;
      }
      else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
-       (STp->mt_status)->mt_fileno = fileno + undone ;
-       STp->drv_block = 0;
+       if (fileno >= 0)
+         STps->drv_file = fileno + undone ;
+       else
+	 STps->drv_file = fileno;
+       STps->drv_block = 0;
+       STps->eof = ST_NOEOF;
      }
      else if (cmd_in == MTFSR) {
        if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
-	 (STp->mt_status)->mt_fileno++;
-	 STp->drv_block = 0;
+	 if (STps->drv_file >= 0)
+	   STps->drv_file++;
+	 STps->drv_block = 0;
+	 STps->eof = ST_FM;
        }
        else {
 	 if (blkno >= undone)
-	   STp->drv_block = blkno - undone;
+	   STps->drv_block = blkno - undone;
 	 else
-	   STp->drv_block = (-1);
+	   STps->drv_block = (-1);
+	 STps->eof = ST_NOEOF;
        }
      }
      else if (cmd_in == MTBSR) {
        if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
-	 (STp->mt_status)->mt_fileno--;
-	 STp->drv_block = (-1);
+	 STps->drv_file--;
+	 STps->drv_block = (-1);
        }
        else {
 	 if (blkno >= 0)
-	   STp->drv_block = blkno + undone;
+	   STps->drv_block = blkno + undone;
 	 else
-	   STp->drv_block = (-1);
+	   STps->drv_block = (-1);
        }
+       STps->eof = ST_NOEOF;
      }
      else if (cmd_in == MTEOM) {
-       (STp->mt_status)->mt_fileno = (-1);
-       STp->drv_block = (-1);
+       STps->drv_file = (-1);
+       STps->drv_block = (-1);
+       STps->eof = ST_EOD;
      }
-     if (STp->eof == ST_NOEOF &&
-	 (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
-       STp->eof = ST_EOD;
+     else if (chg_eof)
+       STps->eof = ST_NOEOF;
+
+     if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+       STps->eof = ST_EOD;
+
      if (cmd_in == MTLOCK)
        STp->door_locked = ST_LOCK_FAILS;
    }
@@ -2219,7 +2362,7 @@
 	*partition = (STp->buffer)->b_data[1];
 	if (((STp->buffer)->b_data[0] & 0x80) &&
 	    (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */
-	  STp->drv_block = (STp->mt_status)->mt_fileno = 0;
+	    STp->ps[0].drv_block = STp->ps[0].drv_file = 0;
       }
 #if DEBUG
       if (debugging)
@@ -2315,8 +2458,8 @@
     if (!SCpnt)
       return (-EBUSY);
 
-    STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
-    STp->eof = ST_NOEOF;
+    STps->drv_block = STps->drv_file = (-1);
+    STps->eof = ST_NOEOF;
     if ((STp->buffer)->last_result_fatal != 0) {
       result = (-EIO);
       if (STp->can_partitions &&
@@ -2330,7 +2473,6 @@
 	STps = &(STp->ps[partition]);
 	if (!STps->last_block_valid ||
 	    STps->last_block_visited != block) {
-	  STps->moves_after_eof = 1;
 	  STps->at_sm = 0;
 	  STps->rw = ST_IDLE;
 	}
@@ -2338,7 +2480,7 @@
       else
 	STps->at_sm = 0;
       if (block == 0)
-	STp->drv_block = (STp->mt_status)->mt_fileno = 0;
+	STps->drv_block = STps->drv_file = 0;
       result = 0;
     }
     SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
@@ -2528,11 +2670,9 @@
      if (_IOC_SIZE(cmd_in) != sizeof(mtc))
        return (-EINVAL);
 
-     i = verify_area(VERIFY_READ, (void *)arg, sizeof(mtc));
+     i = copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop));
      if (i)
-	return i;
-
-     copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop));
+	 return (-EFAULT);
 
      if (mtc.mt_op == MTSETDRVBUFFER && !suser()) {
        printk(KERN_WARNING "st%d: MTSETDRVBUFFER only allowed for root.\n", dev);
@@ -2544,16 +2684,16 @@
 
      if (!(STp->device)->was_reset) {
 
-       if (STp->eof_hit) {
+       if (STps->eof == ST_FM_HIT) {
 	 if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) {
 	   mtc.mt_count -= 1;
-	   if ((STp->mt_status)->mt_fileno >= 0)
-	     (STp->mt_status)->mt_fileno += 1;
+	   if (STps->drv_file >= 0)
+	     STps->drv_file += 1;
 	 }
 	 else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
 	   mtc.mt_count += 1;
-	   if ((STp->mt_status)->mt_fileno >= 0)
-	     (STp->mt_status)->mt_fileno += 1;
+	   if (STps->drv_file >= 0)
+	     STps->drv_file += 1;
 	 }
        }
 
@@ -2591,9 +2731,8 @@
 
      if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
 	 mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
-	 mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSEEK &&
-	 mtc.mt_op != MTSETPART)
-       STps->rw = ST_IDLE;  /* Prevent automatic WEOF */
+	 mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
+       STps->rw = ST_IDLE;  /* Prevent automatic WEOF and fsf */
 
      if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
        st_int_ioctl(inode, MTUNLOCK, 0);  /* Ignore result! */
@@ -2621,13 +2760,12 @@
 	 return i;
        for (i=0; i < ST_NBR_PARTITIONS; i++) {
 	 STp->ps[i].rw = ST_IDLE;
-	 STp->ps[i].moves_after_eof = 1;
 	 STp->ps[i].at_sm = 0;
 	 STp->ps[i].last_block_valid = FALSE;
        }
        STp->partition = STp->new_partition = 0;
        STp->nbr_partitions = 1;  /* Bad guess ?-) */
-       STp->drv_block = (STp->mt_status)->mt_fileno = 0;
+       STps->drv_block = STps->drv_file = 0;
        return 0;
      }
      if (mtc.mt_op == MTSEEK) {
@@ -2658,14 +2796,12 @@
 
      if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
        return (-EINVAL);
-     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtget));
-     if (i)
-       return i;
 
      (STp->mt_status)->mt_dsreg =
        ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
        ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
-     (STp->mt_status)->mt_blkno = STp->drv_block;
+     (STp->mt_status)->mt_blkno = STps->drv_block;
+     (STp->mt_status)->mt_fileno = STps->drv_file;
      if (STp->block_size != 0) {
        if (STps->rw == ST_WRITING)
 	 (STp->mt_status)->mt_blkno +=
@@ -2685,9 +2821,9 @@
 	 (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
      }
      (STp->mt_status)->mt_resid = STp->partition;
-     if (STp->eof == ST_EOM_OK || STp->eof == ST_EOM_ERROR)
+     if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
        (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
-     else if (STp->eof == ST_EOD)
+     else if (STps->eof >= ST_EOM_OK)
        (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
      if (STp->density == 1)
        (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
@@ -2705,8 +2841,10 @@
 	 STp->drv_buffer != 0)
        (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff);
 
-     copy_to_user((char *)arg, (char *)(STp->mt_status),
-		 sizeof(struct mtget));
+     i = copy_to_user((char *)arg, (char *)(STp->mt_status),
+		      sizeof(struct mtget));
+     if (i)
+	 return (-EFAULT);
 
      (STp->mt_status)->mt_erreg = 0;  /* Clear after read */
      return 0;
@@ -2717,11 +2855,10 @@
        return (-EINVAL);
      if ((i = get_location(inode, &blk, &bt, 0)) < 0)
        return i;
-     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(mt_pos));
-     if (i)
-	return i;
      mt_pos.mt_blkno = blk;
-     copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
+     i = copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
+     if (i)
+	 return (-EFAULT);
      return 0;
    }
 
@@ -2890,7 +3027,6 @@
 
    tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
    tpnt->dirty = 0;
-   tpnt->eof = ST_NOEOF;
    tpnt->waiting = NULL;
    tpnt->in_use = 0;
    tpnt->drv_buffer = 1;  /* Try buffering if no mode sense */
@@ -2907,12 +3043,11 @@
    tpnt->partition = 0;
    tpnt->new_partition = 0;
    tpnt->nbr_partitions = 0;
-   tpnt->drv_block = (-1);
-   (tpnt->mt_status)->mt_fileno = (tpnt->mt_status)->mt_blkno = (-1);
 
    for (i=0; i < ST_NBR_MODES; i++) {
      STm = &(tpnt->modes[i]);
      STm->defined = FALSE;
+     STm->sysv = ST_SYSV;
      STm->defaults_for_writes = 0;
      STm->do_async_writes = ST_ASYNC_WRITES;
      STm->do_buffer_writes = ST_BUFFER_WRITES;
@@ -2925,9 +3060,11 @@
    for (i=0; i < ST_NBR_PARTITIONS; i++) {
      STps = &(tpnt->ps[i]);
      STps->rw = ST_IDLE;
-     STps->moves_after_eof = 1;
+     STps->eof = ST_NOEOF;
      STps->at_sm = 0;
      STps->last_block_valid = FALSE;
+     STps->drv_block = 0;
+     STps->drv_file = 0;
    }
 
    tpnt->current_mode = 0;
diff -u --recursive --new-file v2.1.19/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v2.1.19/linux/drivers/scsi/st.h	Fri May  3 10:12:16 1996
+++ linux/drivers/scsi/st.h	Wed Jan  1 18:54:02 1997
@@ -29,6 +29,7 @@
 /* The tape mode definition */
 typedef struct {
   unsigned char defined;
+  unsigned char sysv;   /* SYS V semantics? */
   unsigned char do_async_writes;
   unsigned char do_buffer_writes;
   unsigned char do_read_ahead;
@@ -46,10 +47,12 @@
 /* The status related to each partition */
 typedef struct {
   unsigned char rw;
-  unsigned char moves_after_eof;
+  unsigned char eof;
   unsigned char at_sm;
   unsigned char last_block_valid;
   u32 last_block_visited;
+  int drv_block;	/* The block where the drive head is */
+  int drv_file;
 } ST_partstat;
 
 #define ST_NBR_PARTITIONS 4
@@ -87,11 +90,9 @@
   ST_partstat ps[ST_NBR_PARTITIONS];
   unsigned char dirty;
   unsigned char ready;
-  unsigned char eof;
   unsigned char write_prot;
   unsigned char drv_write_prot;
   unsigned char in_use;
-  unsigned char eof_hit;
   unsigned char blksize_changed;
   unsigned char density_changed;
   unsigned char compression_changed;
@@ -103,13 +104,14 @@
   int min_block;
   int max_block;
   int recover_count;
-  int drv_block;	/* The block where the drive head is */
   struct mtget * mt_status;
 
 #if DEBUG
   unsigned char write_pending;
   int nbr_finished;
   int nbr_waits;
+  unsigned char last_cmnd[6];
+  unsigned char last_sense[16];
 #endif
 } Scsi_Tape;
 
@@ -117,10 +119,15 @@
 
 /* Values of eof */
 #define	ST_NOEOF	0
-#define	ST_FM		1
-#define	ST_EOM_OK	2
-#define ST_EOM_ERROR	3
-#define ST_EOD		4
+#define ST_FM_HIT       1
+#define ST_FM           2
+#define ST_EOM_OK       3
+#define ST_EOM_ERROR	4
+#define	ST_EOD_1        5
+#define ST_EOD_2        6
+#define ST_EOD		7
+/* EOD hit while reading => ST_EOD_1 => return zero => ST_EOD_2 =>
+   return zero => ST_EOD, return ENOSPC */
 
 /* Values of rw */
 #define	ST_IDLE		0
diff -u --recursive --new-file v2.1.19/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.19/linux/fs/buffer.c	Mon Dec 30 15:39:13 1996
+++ linux/fs/buffer.c	Wed Jan  1 16:56:21 1997
@@ -1546,7 +1546,7 @@
 
 asmlinkage int sys_bdflush(int func, long data)
 {
-	int i, error;
+	int i;
 
 	if (!suser())
 		return -EPERM;
@@ -1560,11 +1560,7 @@
 		if (i < 0 || i >= N_PARAM)
 			return -EINVAL;
 		if((func & 1) == 0) {
-			error = verify_area(VERIFY_WRITE, (void *) data, sizeof(int));
-			if (error)
-				return error;
-			put_user(bdf_prm.data[i], (int*)data);
-			return 0;
+			return put_user(bdf_prm.data[i], (int*)data);
 		};
 		if (data < bdflush_min[i] || data > bdflush_max[i])
 			return -EINVAL;
diff -u --recursive --new-file v2.1.19/linux/fs/dquot.c linux/fs/dquot.c
--- v2.1.19/linux/fs/dquot.c	Tue Oct 29 19:58:20 1996
+++ linux/fs/dquot.c	Tue Dec 31 21:19:32 1996
@@ -19,6 +19,7 @@
  * 
  * Fixes:   Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
  *	    removed race conditions in dqput(), dqget() and iput(). 
+ *          Andi Kleen removed all verify_area() calls, 31 Dec 96  
  *
  * (C) Copyright 1994, 1995 Marco van Wieringen 
  *
@@ -591,9 +592,8 @@
 		return(-EFAULT);
 
 	if (flags & QUOTA_SYSCALL) {
-		if ((error = verify_area(VERIFY_READ, dqblk, sizeof(struct dqblk))) != 0)
-			return(error);
-		copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk));
+		if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
+			return -EFAULT;	
 	} else {
 		memcpy(&dq_dqblk, dqblk, sizeof(struct dqblk));
 	}
@@ -649,13 +649,10 @@
 		if (dqblk == (struct dqblk *)NULL)
 			return(-EFAULT);
 
-		if ((error = verify_area(VERIFY_WRITE, dqblk, sizeof(struct dqblk))) != 0)
-			return(error);
-
 		if ((dquot = dqget(dev, id, type)) != NODQUOT) {
-			copy_to_user(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+			error = copy_to_user(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
 			dqput(dquot);
-			return(0);
+			return error ? -EFAULT : 0;
 		}
 	}
 	return(-ESRCH);
@@ -665,13 +662,10 @@
 {
 	int error;
 
-	if ((error = verify_area(VERIFY_WRITE, addr, sizeof(struct dqstats))) != 0)
-		return(error);
-
 	dqstats.allocated_dquots = nr_dquots;
 	dqstats.free_dquots = nr_free_dquots;
-	copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats));
-	return(0);
+	return copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats)) 
+			? -EFAULT : 0; 
 }
 
 /*
diff -u --recursive --new-file v2.1.19/linux/fs/exec.c linux/fs/exec.c
--- v2.1.19/linux/fs/exec.c	Tue Dec 31 21:41:07 1996
+++ linux/fs/exec.c	Wed Jan  1 16:20:45 1997
@@ -386,8 +386,10 @@
 		new_page_tables(current);
 		return;
 	}
+	flush_cache_mm(current->mm);
 	exit_mmap(current->mm);
 	clear_page_tables(current);
+	flush_tlb_mm(current->mm);
 }
 
 /*
diff -u --recursive --new-file v2.1.19/linux/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c
--- v2.1.19/linux/fs/ext2/ioctl.c	Tue Oct 29 19:58:42 1996
+++ linux/fs/ext2/ioctl.c	Tue Dec 31 21:19:32 1996
@@ -19,23 +19,16 @@
 int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
 		unsigned long arg)
 {
-	int err;
 	unsigned long flags;
 
 	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
 
 	switch (cmd) {
 	case EXT2_IOC_GETFLAGS:
-		err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
-		if (err)
-			return err;
-		put_user(inode->u.ext2_i.i_flags, (int *) arg);
-		return 0;
+		return put_user(inode->u.ext2_i.i_flags, (int *) arg);
 	case EXT2_IOC_SETFLAGS:
-		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
-		if (err)
-			return err;
-		get_user(flags, (int *) arg);
+		if (get_user(flags, (int *) arg))
+			return -EFAULT;	
 		/*
 		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
 		 * the super user when the security level is zero.
@@ -64,20 +57,14 @@
 		inode->i_dirt = 1;
 		return 0;
 	case EXT2_IOC_GETVERSION:
-		err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
-		if (err)
-			return err;
-		put_user(inode->u.ext2_i.i_version, (int *) arg);
-		return 0;
+		return put_user(inode->u.ext2_i.i_version, (int *) arg);
 	case EXT2_IOC_SETVERSION:
 		if ((current->fsuid != inode->i_uid) && !fsuser())
 			return -EPERM;
 		if (IS_RDONLY(inode))
 			return -EROFS;
-		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
-		if (err)
-			return err;
-		get_user(inode->u.ext2_i.i_version, (int *) arg);
+		if (get_user(inode->u.ext2_i.i_version, (int *) arg))
+			return -EFAULT;	
 		inode->i_ctime = CURRENT_TIME;
 		inode->i_dirt = 1;
 		return 0;
diff -u --recursive --new-file v2.1.19/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- v2.1.19/linux/fs/ext2/symlink.c	Wed Dec 18 15:58:51 1996
+++ linux/fs/ext2/symlink.c	Tue Dec 31 21:19:32 1996
@@ -105,7 +105,6 @@
 	struct buffer_head * bh = NULL;
 	char * link;
 	int i, err;
-	char c;
 
 	if (!S_ISLNK(inode->i_mode)) {
 		iput (inode);
@@ -123,12 +122,14 @@
 	}
 	else
 		link = (char *) inode->u.ext2_i.i_data;
-	i = 0;
-	while (i < buflen && (c = link[i])) {
-		i++;
-		put_user (c, buffer++);
-	}
-	if (DO_UPDATE_ATIME(inode)) {
+	
+	/* XXX I hope link is always '\0'-terminated. */ 	
+	i = strlen(link)+1; 
+	if (i > buflen) 
+		i = buflen; 
+	if (copy_to_user(buffer, link, i))
+		i = -EFAULT; 	
+ 	if (DO_UPDATE_ATIME(inode)) {
 		inode->i_atime = CURRENT_TIME;
 		inode->i_dirt = 1;
 	}
diff -u --recursive --new-file v2.1.19/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.19/linux/fs/isofs/inode.c	Mon Dec 30 15:39:13 1996
+++ linux/fs/isofs/inode.c	Thu Jan  2 12:11:30 1997
@@ -193,6 +193,11 @@
   vol_desc_start=0;
   if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
     {
+      /* Whoops.  We must save the old FS, since otherwise
+       * we would destroy the kernels idea about FS on root
+       * mount in read_super... [chexum]
+       */
+      unsigned long old_fs=get_fs();
       inode_fake.i_rdev=dev;
       ms_info.addr_format=CDROM_LBA;
       set_fs(KERNEL_DS);
@@ -200,7 +205,7 @@
 				       NULL,
 				       CDROMMULTISESSION,
 				       (unsigned long) &ms_info);
-      set_fs(USER_DS);
+      set_fs(old_fs);
 #if 0
       printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
       if (i==0)
diff -u --recursive --new-file v2.1.19/linux/fs/isofs/rock.c linux/fs/isofs/rock.c
--- v2.1.19/linux/fs/isofs/rock.c	Sat May 18 11:15:09 1996
+++ linux/fs/isofs/rock.c	Thu Jan  2 12:11:30 1997
@@ -287,7 +287,7 @@
 	CHECK_CE;
 	break;
       case SIG('E','R'):
-	printk("ISO9660 Extensions: ");
+	printk(KERN_DEBUG"ISO9660 Extensions: ");
 	{ int p;
 	  for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
 	};
diff -u --recursive --new-file v2.1.19/linux/fs/isofs/symlink.c linux/fs/isofs/symlink.c
--- v2.1.19/linux/fs/isofs/symlink.c	Tue Oct 29 19:58:43 1996
+++ linux/fs/isofs/symlink.c	Wed Jan  1 16:56:07 1997
@@ -83,7 +83,6 @@
 {
         char * pnt;
 	int i;
-	char c;
 
 	if (!S_ISLNK(inode->i_mode)) {
 		iput(inode);
@@ -97,12 +96,12 @@
 	iput(inode);
 	if (!pnt)
 		return 0;
-	i = 0;
 
-	while (i<buflen && (c = pnt[i])) {
-		i++;
-		put_user(c,buffer++);
-	}
+	i = strlen(pnt)+1;
+	if (i > buflen)
+		i = buflen; 
+	if (copy_to_user(buffer, pnt, i))
+		i = -EFAULT; 	
 	kfree(pnt);
 	return i;
 }
diff -u --recursive --new-file v2.1.19/linux/fs/locks.c linux/fs/locks.c
--- v2.1.19/linux/fs/locks.c	Wed Dec 18 15:58:51 1996
+++ linux/fs/locks.c	Wed Jan  1 16:56:51 1997
@@ -258,18 +258,15 @@
  */
 int fcntl_getlk(unsigned int fd, struct flock *l)
 {
-	int error;
 	struct flock flock;
 	struct file *filp;
 	struct file_lock *fl,file_lock;
 
 	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
 		return (-EBADF);
-	error = verify_area(VERIFY_WRITE, l, sizeof(*l));
-	if (error)
-		return (error);
+	if (copy_from_user(&flock, l, sizeof(flock)))
+		return -EFAULT; 	
 
-	copy_from_user(&flock, l, sizeof(flock));
 	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		return (-EINVAL);
 
@@ -286,14 +283,12 @@
 				fl->fl_end - fl->fl_start + 1;
 			flock.l_whence = 0;
 			flock.l_type = fl->fl_type;
-			copy_to_user(l, &flock, sizeof(flock));
-			return (0);
+			return copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0; 
 		}
 	}
 
 	flock.l_type = F_UNLCK;			/* no conflict found */
-	copy_to_user(l, &flock, sizeof(flock));
-	return (0);
+	return copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0; 
 }
 
 /* Apply the lock described by l to an open file descriptor.
@@ -301,7 +296,6 @@
  */
 int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
 {
-	int error;
 	struct file *filp;
 	struct file_lock file_lock;
 	struct flock flock;
@@ -313,10 +307,6 @@
 	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
 		return (-EBADF);
 	
-	error = verify_area(VERIFY_READ, l, sizeof(*l));
-	if (error)
-		return (error);
-	
 	if (!(inode = filp->f_inode))
 		return (-EINVAL);
 	
@@ -334,7 +324,8 @@
 		} while (vma != inode->i_mmap);
 	}
 
-	copy_from_user(&flock, l, sizeof(flock));
+	if (copy_from_user(&flock, l, sizeof(flock)))
+		return -EFAULT; 	
 	if (!posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
 	
diff -u --recursive --new-file v2.1.19/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- v2.1.19/linux/fs/nfs/symlink.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/nfs/symlink.c	Tue Dec 31 21:19:32 1996
@@ -108,10 +108,12 @@
 	error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
 		&res, &len, buflen);
 	iput(inode);
-	if (! error) {
-		copy_to_user(buffer, res, len);
-		put_user('\0', buffer + len);
-		error = len;
+	if (!error) {
+		error = copy_to_user(buffer, res, len);
+		if (!error)
+			error = put_user('\0', buffer + len);
+		if (!error)
+			error = len;
 	}
 	kfree(mem);
 	return error;
diff -u --recursive --new-file v2.1.19/linux/fs/open.c linux/fs/open.c
--- v2.1.19/linux/fs/open.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/open.c	Tue Dec 31 21:19:32 1996
@@ -176,13 +176,13 @@
 	/* Don't worry, the checks are done in inode_change_ok() */
 	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
 	if (times) {
-		error = verify_area(VERIFY_READ, times, sizeof(*times));
+		error = get_user(newattrs.ia_atime, &times->actime);
+		if (!error) 
+			error = get_user(newattrs.ia_mtime, &times->modtime);
 		if (error) {
 			iput(inode);
 			return error;
 		}
-		get_user(newattrs.ia_atime, &times->actime);
-		get_user(newattrs.ia_mtime, &times->modtime);
 		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
 	} else {
 		if (current->fsuid != inode->i_uid &&
@@ -219,12 +219,10 @@
 	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
 	if (utimes) {
 		struct timeval times[2];
-		error = verify_area(VERIFY_READ, utimes, sizeof(times));
-		if (error) {
+		if (copy_from_user(&times, utimes, sizeof(times))) {
 			iput(inode);
-			return error;
-		}
-		copy_from_user(&times, utimes, sizeof(times));
+			return -EFAULT;
+		}		
 		newattrs.ia_atime = times[0].tv_sec;
 		newattrs.ia_mtime = times[1].tv_sec;
 		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
diff -u --recursive --new-file v2.1.19/linux/fs/pipe.c linux/fs/pipe.c
--- v2.1.19/linux/fs/pipe.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/pipe.c	Wed Jan  1 16:24:40 1997
@@ -18,7 +18,11 @@
  * Define this if you want SunOS compatibility wrt braindead
  * select behaviour on FIFO's.
  */
+#ifdef __sparc__
+#define FIFO_SUNOS_BRAINDAMAGE
+#else
 #undef FIFO_SUNOS_BRAINDAMAGE
+#endif
 
 /* We don't use the head/tail construction any more. Now we use the start/len*/
 /* construction providing full use of PIPE_BUF (multiple of PAGE_SIZE) */
@@ -147,14 +151,9 @@
 static int pipe_ioctl(struct inode *pino, struct file * filp,
 	unsigned int cmd, unsigned long arg)
 {
-	int error;
-
 	switch (cmd) {
 		case FIONREAD:
-			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-			if (!error)
-				put_user(PIPE_SIZE(*pino),(int *) arg);
-			return error;
+			return put_user(PIPE_SIZE(*pino),(int *) arg);
 		default:
 			return -EINVAL;
 	}
diff -u --recursive --new-file v2.1.19/linux/fs/stat.c linux/fs/stat.c
--- v2.1.19/linux/fs/stat.c	Sun Dec 22 16:37:38 1996
+++ linux/fs/stat.c	Wed Jan  1 16:56:33 1997
@@ -20,7 +20,7 @@
  * For backward compatibility?  Maybe this should be moved
  * into arch/i386 instead?
  */
-static void cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
+static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
 {
 	struct __old_kernel_stat tmp;
 
@@ -39,12 +39,12 @@
 	tmp.st_atime = inode->i_atime;
 	tmp.st_mtime = inode->i_mtime;
 	tmp.st_ctime = inode->i_ctime;
-	copy_to_user(statbuf,&tmp,sizeof(tmp));
+	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
 }
 
 #endif
 
-static void cp_new_stat(struct inode * inode, struct stat * statbuf)
+static int cp_new_stat(struct inode * inode, struct stat * statbuf)
 {
 	struct stat tmp;
 	unsigned int blocks, indirect;
@@ -99,7 +99,7 @@
 		tmp.st_blocks = inode->i_blocks;
 		tmp.st_blksize = inode->i_blksize;
 	}
-	copy_to_user(statbuf,&tmp,sizeof(tmp));
+	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
 }
 
 #if !defined(__alpha__) && !defined(__sparc__)
@@ -112,15 +112,12 @@
 	struct inode * inode;
 	int error;
 
-	error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
-	if (error)
-		return error;
 	error = namei(filename,&inode);
 	if (error)
 		return error;
-	cp_old_stat(inode,statbuf);
+	error = cp_old_stat(inode,statbuf);
 	iput(inode);
-	return 0;
+	return error;
 }
 #endif
 
@@ -129,15 +126,12 @@
 	struct inode * inode;
 	int error;
 
-	error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
-	if (error)
-		return error;
 	error = namei(filename,&inode);
 	if (error)
 		return error;
-	cp_new_stat(inode,statbuf);
+	error = cp_new_stat(inode,statbuf);
 	iput(inode);
-	return 0;
+	return error;
 }
 
 #if !defined(__alpha__) && !defined(__sparc__)
@@ -151,15 +145,12 @@
 	struct inode * inode;
 	int error;
 
-	error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
-	if (error)
-		return error;
 	error = lnamei(filename,&inode);
 	if (error)
 		return error;
-	cp_old_stat(inode,statbuf);
+	error = cp_old_stat(inode,statbuf);
 	iput(inode);
-	return 0;
+	return error;
 }
 
 #endif
@@ -169,15 +160,12 @@
 	struct inode * inode;
 	int error;
 
-	error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
-	if (error)
-		return error;
 	error = lnamei(filename,&inode);
 	if (error)
 		return error;
-	cp_new_stat(inode,statbuf);
+	error = cp_new_stat(inode,statbuf);
 	iput(inode);
-	return 0;
+	return error;
 }
 
 #if !defined(__alpha__) && !defined(__sparc__)
@@ -190,15 +178,10 @@
 {
 	struct file * f;
 	struct inode * inode;
-	int error;
 
-	error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
-	if (error)
-		return error;
 	if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
 		return -EBADF;
-	cp_old_stat(inode,statbuf);
-	return 0;
+	return cp_old_stat(inode,statbuf);
 }
 
 #endif
@@ -207,15 +190,10 @@
 {
 	struct file * f;
 	struct inode * inode;
-	int error;
 
-	error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
-	if (error)
-		return error;
 	if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
 		return -EBADF;
-	cp_new_stat(inode,statbuf);
-	return 0;
+	return cp_new_stat(inode,statbuf);
 }
 
 asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz)
diff -u --recursive --new-file v2.1.19/linux/fs/super.c linux/fs/super.c
--- v2.1.19/linux/fs/super.c	Sun Dec 22 16:37:38 1996
+++ linux/fs/super.c	Tue Dec 31 21:19:32 1996
@@ -213,7 +213,7 @@
 static int fs_name(unsigned int index, char * buf)
 {
 	struct file_system_type * tmp;
-	int err, len;
+	int len;
 
 	tmp = file_systems;
 	while (tmp && index > 0) {
@@ -223,11 +223,7 @@
 	if (!tmp)
 		return -EINVAL;
 	len = strlen(tmp->name) + 1;
-	err = verify_area(VERIFY_WRITE, buf, len);
-	if (err)
-		return err;
-	copy_to_user(buf, tmp->name, len);
-	return 0;
+	return copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;
 }
 
 static int fs_maxindex(void)
@@ -477,7 +473,6 @@
         struct ustat tmp;
         struct statfs sbuf;
         unsigned long old_fs;
-        int error;
 
         s = get_super(to_kdev_t(dev));
         if (s == NULL)
@@ -486,10 +481,6 @@
         if (!(s->s_op->statfs))
                 return -ENOSYS;
 
-        error = verify_area(VERIFY_WRITE,ubuf,sizeof(struct ustat));
-        if (error)
-                return error;
-
         old_fs = get_fs();
         set_fs(get_ds());
         s->s_op->statfs(s,&sbuf,sizeof(struct statfs));
@@ -499,8 +490,7 @@
         tmp.f_tfree = sbuf.f_bfree;
         tmp.f_tinode = sbuf.f_ffree;
 
-        copy_to_user(ubuf,&tmp,sizeof(struct ustat));
-        return 0;
+        return copy_to_user(ubuf,&tmp,sizeof(struct ustat)) ? -EFAULT : 0;
 }
 
 static struct super_block * read_super(kdev_t dev,const char *name,int flags,
@@ -807,7 +797,10 @@
 	if (!(page = __get_free_page(GFP_KERNEL))) {
 		return -ENOMEM;
 	}
-	copy_from_user((void *) page,data,i);
+	if (copy_from_user((void *) page,data,i)) {
+		free_page(page); 
+		return -EFAULT;
+	}
 	*where = page;
 	return 0;
 }
diff -u --recursive --new-file v2.1.19/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v2.1.19/linux/include/asm-i386/string.h	Wed Dec 18 15:58:52 1996
+++ linux/include/asm-i386/string.h	Thu Jan  2 12:15:01 1997
@@ -550,7 +550,9 @@
 	"cmpl $-1,%2\n\t"
 	"jne 1b\n"
 	"3:\tsubl %1,%0"
-	:"=a" (__res):"c" (s),"d" (count));
+	:"=a" (__res)
+	:"c" (s),"d" (count)
+	:"dx");
 return __res;
 }
 /* end of additional stuff */
diff -u --recursive --new-file v2.1.19/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.1.19/linux/include/asm-i386/uaccess.h	Fri Nov 22 18:28:20 1996
+++ linux/include/asm-i386/uaccess.h	Thu Jan  2 15:34:45 1997
@@ -342,6 +342,33 @@
 	return n;
 }
 
+static inline unsigned long
+__generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n)
+{
+	__copy_user(to,from,n);
+	return n;
+}
+
+static inline unsigned long
+__constant_copy_to_user_nocheck(void *to, const void *from, unsigned long n)
+{
+	__constant_copy_user(to,from,n);
+	return n;
+}
+
+static inline unsigned long
+__generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n)
+{
+	__copy_user(to,from,n);
+	return n;
+}
+
+static inline unsigned long
+__constant_copy_from_user_nocheck(void *to, const void *from, unsigned long n)
+{
+	__constant_copy_user(to,from,n);
+	return n;
+}
 
 #define copy_to_user(to,from,n)				\
 	(__builtin_constant_p(n) ?			\
@@ -354,11 +381,22 @@
 	 __generic_copy_from_user((to),(from),(n)))
 
 
+#define __copy_to_user(to,from,n)			\
+	(__builtin_constant_p(n) ?			\
+	 __constant_copy_to_user_nocheck((to),(from),(n)) :	\
+	 __generic_copy_to_user_nocheck((to),(from),(n)))
+
+#define __copy_from_user(to,from,n)			\
+	(__builtin_constant_p(n) ?			\
+	 __constant_copy_from_user_nockeck((to),(from),(n)) :	\
+	 __generic_copy_from_user_nocheck((to),(from),(n)))
+
+
 /*
  * Zero Userspace
  */
 
-#define __clear_user(addr,size)						\
+#define __do_clear_user(addr,size)						\
 	__asm__ __volatile__(						\
 		"0:	rep; stosl\n"					\
 		"	movl %1,%0\n"					\
@@ -380,7 +418,14 @@
 clear_user(void *to, unsigned long n)
 {
 	if (access_ok(VERIFY_WRITE, to, n))
-		__clear_user(to, n);
+		__do_clear_user(to, n);
+	return n;
+}
+
+static inline unsigned long
+__clear_user(void *to, unsigned long n)
+{
+	__do_clear_user(to, n);
 	return n;
 }
 
@@ -389,7 +434,7 @@
  * Copy a null terminated string from userspace.
  */
 
-#define __strncpy_from_user(dst,src,count,res)				   \
+#define __do_strncpy_from_user(dst,src,count,res)			   \
 	__asm__ __volatile__(						   \
 		"	testl %1,%1\n"					   \
 		"	jz 2f\n"					   \
@@ -413,11 +458,19 @@
 		: "si", "di", "ax", "memory")
 
 static inline long
+__strncpy_from_user(char *dst, const char *src, long count)
+{
+	long res;
+	__do_strncpy_from_user(dst, src, count, res);
+	return res;
+}
+
+static inline long
 strncpy_from_user(char *dst, const char *src, long count)
 {
 	long res = -EFAULT;
 	if (access_ok(VERIFY_READ, src, 1))
-		__strncpy_from_user(dst, src, count, res);
+		__do_strncpy_from_user(dst, src, count, res);
 	return res;
 }
 
diff -u --recursive --new-file v2.1.19/linux/include/linux/fddidevice.h linux/include/linux/fddidevice.h
--- v2.1.19/linux/include/linux/fddidevice.h	Tue Nov 19 15:53:57 1996
+++ linux/include/linux/fddidevice.h	Thu Jan  2 15:13:26 1997
@@ -25,18 +25,15 @@
 #include <linux/if_fddi.h>
 
 #ifdef __KERNEL__
-extern int				fddi_header(struct sk_buff *skb,
-									struct device *dev,
-									unsigned short type,
-									void *daddr,
-									void *saddr,
-									unsigned len);
-extern int				fddi_rebuild_header(void *buff,
-											struct device *dev,
-											unsigned long dest,
-											struct sk_buff *skb);
+extern int		fddi_header(struct sk_buff *skb,
+				    struct device *dev,
+				    unsigned short type,
+				    void *daddr,
+				    void *saddr,
+				    unsigned len);
+extern int		fddi_rebuild_header(struct sk_buff *skb);
 extern unsigned short	fddi_type_trans(struct sk_buff *skb,
-										struct device *dev);
+				struct device *dev);
 #endif
 
 #endif	/* _LINUX_FDDIDEVICE_H */
diff -u --recursive --new-file v2.1.19/linux/include/linux/ipv6.h linux/include/linux/ipv6.h
--- v2.1.19/linux/include/linux/ipv6.h	Tue Nov 12 15:56:14 1996
+++ linux/include/linux/ipv6.h	Thu Jan  2 15:36:46 1997
@@ -4,35 +4,6 @@
 #include <linux/in6.h>
 #include <asm/byteorder.h>
 
-/*
- *	IPv6 fixed header
- */
-
-struct ipv6hdr {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
-	__u8			priority:4,
-				version:4;
-#elif defined(__BIG_ENDIAN_BITFIELD)
-	__u8			version:4,
-				priority:4;
-#else
-#error	"Please fix <asm/byteorder.h>"
-#endif						
-	__u8			flow_lbl[3];
-
-	__u16			payload_len;
-	__u8			nexthdr;
-	__u8			hop_limit;
-
-	struct	in6_addr	saddr;
-	struct	in6_addr	daddr;
-};
-
-struct in6_ifreq {
-	struct in6_addr	addr;
-	__u32		prefix_len;
-	char		devname[8]; 
-};
 
 /*
  *	Advanced API
@@ -46,6 +17,13 @@
 	int		ipi6_ifindex;
 };
 
+
+struct in6_ifreq {
+	struct in6_addr	ifr6_addr;
+	__u32		ifr6_prefixlen;
+	int		ifr6_ifindex; 
+};
+
 #define IPV6_SRCRT_STRICT	0x01	/* this hop must be a neighbor	*/
 #define IPV6_SRCRT_TYPE_0	0	/* IPv6 type 0 Routing Header	*/
 
@@ -77,6 +55,30 @@
 };
 
 #ifdef __KERNEL__
+
+/*
+ *	IPv6 fixed header
+ */
+
+struct ipv6hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8			priority:4,
+				version:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u8			version:4,
+				priority:4;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif						
+	__u8			flow_lbl[3];
+
+	__u16			payload_len;
+	__u8			nexthdr;
+	__u8			hop_limit;
+
+	struct	in6_addr	saddr;
+	struct	in6_addr	daddr;
+};
 
 /*
  *	The length of this struct cannot be greater than the length of
diff -u --recursive --new-file v2.1.19/linux/include/linux/lapb.h linux/include/linux/lapb.h
--- v2.1.19/linux/include/linux/lapb.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/lapb.h	Thu Jan  2 15:13:26 1997
@@ -0,0 +1,56 @@
+/*
+ * These are the public elements of the Linux LAPB module.
+ */
+ 
+#ifndef	LAPB_KERNEL_H
+#define	LAPB_KERNEL_H
+ 
+#define	LAPB_OK			0
+#define	LAPB_BADTOKEN		1
+#define	LAPB_INVALUE		2
+#define	LAPB_CONNECTED		3
+#define	LAPB_NOTCONNECTED	4
+#define	LAPB_REFUSED		5
+#define	LAPB_TIMEDOUT		6
+#define	LAPB_NOMEM		7
+
+#define	LAPB_STANDARD		0x00
+#define	LAPB_EXTENDED		0x01
+
+#define	LAPB_SLP		0x00
+#define	LAPB_MLP		0x02
+
+#define	LAPB_DTE		0x00
+#define	LAPB_DCE		0x04
+
+struct lapb_register_struct {
+	void (*connect_confirmation)(void *token, int reason);
+	void (*connect_indication)(void *token, int reason);
+	void (*disconnect_confirmation)(void *token, int reason);
+	void (*disconnect_indication)(void *token, int reason);
+	void (*data_indication)(void *token, struct sk_buff *skb);
+	void (*data_transmit)(void *token, struct sk_buff *skb);
+};
+
+struct lapb_parms_struct {
+	unsigned int t1;
+	unsigned int t1timer;
+	unsigned int t2;
+	unsigned int t2timer;
+	unsigned int n2;
+	unsigned int n2count;
+	unsigned int window;
+	unsigned int state;
+	unsigned int mode;
+};
+
+extern int lapb_register(void *token, struct lapb_register_struct *callbacks);
+extern int lapb_unregister(void *token);
+extern int lapb_getparms(void *token, struct lapb_parms_struct *parms);
+extern int lapb_setparms(void *token, struct lapb_parms_struct *parms);
+extern int lapb_connect_request(void *token);
+extern int lapb_disconnect_request(void *token);
+extern int lapb_data_request(void *token, struct sk_buff *skb);
+extern int lapb_data_received(void *token, struct sk_buff *skb);
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/include/linux/lapbether.h linux/include/linux/lapbether.h
--- v2.1.19/linux/include/linux/lapbether.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/lapbether.h	Thu Jan  2 15:13:26 1997
@@ -0,0 +1,19 @@
+#ifndef	__LAPBETHER_H
+#define	__LAPBETHER_H
+
+/*
+ * 	Defines for the LAPBETHER pseudo device driver
+ */
+
+#ifndef __LINUX_IF_ETHER_H
+#include <linux/if_ether.h>
+#endif
+
+#define SIOCSLAPBETHADDR	(SIOCDEVPRIVATE+1)
+ 
+struct lapbeth_ethaddr {
+	unsigned char destination[ETH_ALEN];
+	unsigned char accept[ETH_ALEN];
+};
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.1.19/linux/include/linux/mm.h	Sun Dec 22 16:37:41 1996
+++ linux/include/linux/mm.h	Thu Jan  2 15:34:30 1997
@@ -256,7 +256,7 @@
 extern int new_page_tables(struct task_struct * tsk);
 extern int copy_page_tables(struct task_struct * to);
 
-extern int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
+extern void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
 extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma);
 extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot);
 extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot);
diff -u --recursive --new-file v2.1.19/linux/include/linux/mtio.h linux/include/linux/mtio.h
--- v2.1.19/linux/include/linux/mtio.h	Sun Aug  4 13:39:07 1996
+++ linux/include/linux/mtio.h	Wed Jan  1 18:54:02 1997
@@ -245,4 +245,7 @@
 #define MT_ST_DEF_COMPRESSION	(MT_ST_DEF_OPTIONS | 0x200000)
 #define MT_ST_DEF_DRVBUFFER	(MT_ST_DEF_OPTIONS | 0x300000)
 
+/* The offset for the arguments for the special HP changer load command. */
+#define MT_ST_HPLOADER_OFFSET 10000
+
 #endif /* _LINUX_MTIO_H */
diff -u --recursive --new-file v2.1.19/linux/include/linux/net_alias.h linux/include/linux/net_alias.h
--- v2.1.19/linux/include/linux/net_alias.h	Mon Sep 30 11:21:31 1996
+++ linux/include/linux/net_alias.h	Thu Jan  2 15:36:44 1997
@@ -17,6 +17,7 @@
 #ifndef _NET_ALIAS_H
 #define _NET_ALIAS_H
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/if.h>
 #include <linux/netdevice.h>
@@ -33,20 +34,20 @@
 
 
 /*
- * main alias structure
- * note that *defines* dev & devname
+ *	Main alias structure
+ *	Note that *defines* dev & devname.
  */
 
 struct net_alias
 {
-  struct device dev;		/* alias device defn*/
-  char name[IFNAMSIZ];		/* device name defn */
-  unsigned hash;		/* my hash value: for quick rehash */
-  unsigned slot;		/* slot number */
-  void *data;			/* private data */
-  struct device *main_dev;	/* pointer to main device */
-  struct net_alias_type *nat;	/* alias type object bound */
-  struct net_alias *next;	/* next alias (hashed linked list) */
+	struct device dev;		/* alias device defn*/
+	char name[IFNAMSIZ];		/* device name defn */
+	unsigned hash;			/* my hash value: for quick rehash */
+	unsigned slot;			/* slot number */
+	void *data;			/* private data */
+	struct device *main_dev;	/* pointer to main device */
+	struct net_alias_type *nat;	/* alias type object bound */
+	struct net_alias *next;		/* next alias (hashed linked list) */
 };
 
 
@@ -57,59 +58,102 @@
 
 struct net_alias_info
 {
-  int n_aliases;		/* num aliases */
-  struct device *taildev;	/* my last (alias) device */
-  struct net_alias *hash_tab[16]; /* hashed alias table */
+	int n_aliases;			/* num aliases */
+	struct device *taildev;		/* my last (alias) device */
+	struct net_alias *hash_tab[16]; /* hashed alias table */
 };
 
 /*
- * net_alias_type class
- * declares a generic (AF_ independent) structure that will
- * manage generic to family-specific behavior.
+ *	net_alias_type class
+ *	Declares a generic (AF_ independent) structure that will
+ *	manage generic to family-specific behavior.
  */
 
 struct net_alias_type
 {
-  int type;			/* aliasing type: address family */
-  int n_attach;	 		/* number of aliases attached */
-  char name[16];		/* af_name */
-  __u32 (*get_addr32)		/* get __u32 addr 'representation'*/
-    (struct net_alias_type *this, struct sockaddr*);	
-  int (*dev_addr_chk)		/* address checking func: */
-    (struct net_alias_type *this, struct device *, struct sockaddr *);
-  struct device * (*dev_select)	/* closest alias selector*/
-    (struct net_alias_type *this, struct device *, struct sockaddr *sa);
-  int (*alias_init_1)		/* called after alias creation: */
-    (struct net_alias_type *this,struct net_alias *alias, struct sockaddr *sa);
-  int (*alias_done_1)		/* called before alias deletion */
-    (struct net_alias_type *this, struct net_alias *alias);
-  int (*alias_print_1)	
-    (struct net_alias_type *this, struct net_alias *alias, char *buf, int len);
-  struct net_alias_type *next;	/* link */
+	int type;			/* aliasing type: address family */
+	int n_attach;	 		/* number of aliases attached */
+	char name[16];			/* af_name */
+	__u32 (*get_addr32)		/* get __u32 addr 'representation'*/
+		(struct net_alias_type *this, struct sockaddr*);	
+	int (*dev_addr_chk)		/* address checking func: */
+		(struct net_alias_type *this, struct device *, struct sockaddr *);
+	struct device * (*dev_select)	/* closest alias selector*/
+		(struct net_alias_type *this, struct device *, struct sockaddr *sa);
+	int (*alias_init_1)		/* called after alias creation: */
+		(struct net_alias_type *this,struct net_alias *alias, struct sockaddr *sa);
+	int (*alias_done_1)		/* called before alias deletion */
+		(struct net_alias_type *this, struct net_alias *alias);
+	int (*alias_print_1)	
+		(struct net_alias_type *this, struct net_alias *alias, char *buf, int len);
+	struct net_alias_type *next;	/* link */
 };
 
 
 /*
- * is dev an alias?
+ *	is dev an alias?
  */
 
-static __inline__ int
-net_alias_is(struct device *dev)
+#ifdef CONFIG_NET_ALIAS
+
+extern __inline__ int net_alias_is(struct device *dev)
+{
+	return (dev->my_alias != NULL);
+}
+
+/*
+ *	Does dev have aliases?
+ */
+
+extern __inline__ int net_alias_has(struct device *dev)
+{
+	return (dev->alias_info != NULL);
+}
+
+/*
+ *	Returns MY 'true' main device
+ *	intended for alias devices
+ */
+
+extern __inline__ struct device *net_alias_main_dev(struct device *dev)
 {
-  return (dev->my_alias != NULL);
+	return (net_alias_is(dev))? dev->my_alias->main_dev : dev;
 }
 
 
 /*
- * does dev have aliases?
+ *	Returns NEXT 'true' device
+ *	intended for true devices
+ */
+
+extern __inline__ struct device *net_alias_nextdev(struct device *dev)
+{
+	return (dev->alias_info)? dev->alias_info->taildev->next : dev->next;
+}
+
+/*
+ *	Sets NEXT 'true' device
+ *	Intended for main devices (treat main device as block: dev+aliases).
  */
 
-static __inline__ int
-net_alias_has(struct device *dev)
+extern __inline__ struct device *net_alias_nextdev_set(struct device *dev, struct device *nextdev)
 {
-  return (dev->alias_info != NULL);
+	struct device *pdev = dev;
+	if (net_alias_has(dev))
+	{
+		pdev = dev->alias_info->taildev; /* point to last dev alias */
+	}
+	pdev->next = nextdev;
+	return nextdev;
 }
 
+#else
+
+#define net_alias_has(dev)	(0)
+#define net_alias_is(dev)	(0)
+#define net_alias_main_dev(dev)	(dev)
+#endif
+
 
 extern void net_alias_init(void);
 
@@ -129,44 +173,5 @@
 extern struct device * net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst);
 
 
-/*
- * returns MY 'true' main device
- * intended for alias devices
- */
-
-static __inline__ struct device *net_alias_main_dev(struct device *dev)
-{
-  return (net_alias_is(dev))? dev->my_alias->main_dev : dev;
-}
-
-
-/*
- * returns NEXT 'true' device
- * intended for true devices
- */
-
-static __inline__ struct device *
-net_alias_nextdev(struct device *dev)
-{
-  return (dev->alias_info)? dev->alias_info->taildev->next : dev->next;
-}
-
-
-/*
- * sets NEXT 'true' device
- * intended for main devices (treat main device as block: dev+aliases).
- */
-
-static __inline__ struct device *
-net_alias_nextdev_set(struct device *dev, struct device *nextdev)
-{
-  struct device *pdev = dev;
-  if (net_alias_has(dev))
-  {
-    pdev = dev->alias_info->taildev; /* point to last dev alias */
-  }
-  pdev->next = nextdev;
-  return nextdev;
-}
 
 #endif  /* _NET_ALIAS_H */
diff -u --recursive --new-file v2.1.19/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.19/linux/include/linux/netdevice.h	Mon Dec 30 15:39:15 1996
+++ linux/include/linux/netdevice.h	Thu Jan  2 15:36:42 1997
@@ -10,8 +10,8 @@
  * 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>
- *		Alan Cox, <A.Cox@swansea.ac.uk>
+ *		Donald J. Becker, <becker@cesdis.gsfc.nasa.gov>
+ *		Alan Cox, <Alan.Cox@linux.org>
  *		Bjorn Ekwall. <bj0rn@blox.se>
  *
  *		This program is free software; you can redistribute it and/or
@@ -28,10 +28,18 @@
 #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
+/*
+ *	For future expansion when we will have different priorities. 
+ */
+ 
+#define DEV_NUMBUFFS	3		/* Number of queues per device	   */
+#define MAX_ADDR_LEN	7		/* Largest hardware address length */
 
+/*
+ *	Compute the worst case header length according to the protocols
+ *	used.
+ */
+ 
 #if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR)
 #define LL_MAX_HEADER	32
 #else
@@ -79,11 +87,11 @@
 
 struct hh_cache
 {
-	struct hh_cache *hh_next;
-	int		hh_refcnt;	/* number of users */
+	struct hh_cache *hh_next;	/* Next entry			     */
+	int		hh_refcnt;	/* number of users                   */
 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
-	char		hh_uptodate;	/* hh_data is valid */
-	/* cached hardware header; allow for machine alignment needs.  */
+	char		hh_uptodate;	/* hh_data is valid                  */
+	/* cached hardware header; allow for machine alignment needs.        */
 	unsigned long	hh_data[16/sizeof(unsigned long)];
 };
 
@@ -140,6 +148,12 @@
 	unsigned char		if_port;	/* Selectable AUI, TP,..*/
 	unsigned char		dma;		/* DMA channel		*/
 
+	/*
+	 *	FIXME:
+	 *	The description 'enet_statistics' is misleading. We
+	 *	should change this.
+	 */
+	 
 	struct enet_statistics* (*get_stats)(struct device *dev);
 #ifdef CONFIG_NET_RADIO
 	struct iw_statistics*	(*get_wireless_stats)(struct device *dev);
@@ -179,8 +193,8 @@
 	int			mc_count;	/* Number of installed mcasts	*/
   
 	struct ip_mc_list	*ip_mc_list;	/* IP multicast filter chain    */
-	unsigned		ip_flags;
-	__u8			hash;	
+	unsigned		ip_flags;	/* IP layer control flags	*/
+	__u8			hash;		/* Hashing index 		*/	
 	__u32			tx_queue_len;	/* Max frames per queue allowed */
     
 	/* For load balancing driver pair support */
@@ -229,12 +243,13 @@
 };
 
 
-struct packet_type {
-	unsigned short		type;	/* This is really htons(ether_type). */
-	struct device		*dev;
+struct packet_type 
+{
+	unsigned short		type;	/* This is really htons(ether_type).	*/
+	struct device		*dev;	/* NULL is wildcarded here		*/
 	int			(*func) (struct sk_buff *, struct device *,
 					 struct packet_type *);
-	void			*data;
+	void			*data;	/* Private to the packet type		*/
 	struct packet_type	*next;
 };
 
@@ -242,16 +257,14 @@
 #include <linux/interrupt.h>
 #include <linux/notifier.h>
 
-/* Used by dev_rint */
-#define IN_SKBUFF	1
-
-extern struct device	loopback_dev;
-extern struct device	*dev_base;
-extern struct packet_type *ptype_base[16];
+extern struct device		loopback_dev;		/* The loopback */
+extern struct device		*dev_base;		/* All devices */
+extern struct packet_type 	*ptype_base[16];	/* Hashed types */
 
 /* NOTE: move to INET specific header;
    __ip_chk_addr is deprecated, do not use if it's possible.
  */
+
 extern int		__ip_chk_addr(unsigned long addr);
 extern struct device 	*ip_dev_find(unsigned long addr, char *name);
 /* This is the wrong place but it'll do for the moment */
@@ -311,7 +324,7 @@
 
 /* NOTE: about to be replaced with if_index */
 
-static __inline__ __u8 dev_hash_name(char *name)
+extern __inline__ __u8 dev_hash_name(char *name)
 {
 	__u8 hash = 0;
 	__u8 *p;
@@ -320,7 +333,7 @@
 	return hash;
 }
 
-static __inline__ __u8 dev_hash_mc_name(char *name)
+extern __inline__ __u8 dev_hash_mc_name(char *name)
 {
 	int i;
 	__u8 hash = 0;
@@ -332,6 +345,21 @@
 		hash ^= h;
 	}
 	return hash;
+}
+
+/*
+ *	Buffer initialisation function. This used to appear in all the
+ *	drivers but is now an inline in case we ever want to change the
+ *	schemes used.
+ */
+ 
+extern __inline__ void dev_init_buffers(struct device *dev)
+{
+	int i;
+	for(i=0;i<DEV_NUMBUFFS;i++)
+	{
+		skb_queue_head_init(&dev->buffs[i]);
+	}
 }
 
 
diff -u --recursive --new-file v2.1.19/linux/include/linux/netrom.h linux/include/linux/netrom.h
--- v2.1.19/linux/include/linux/netrom.h	Tue Nov 12 15:56:14 1996
+++ linux/include/linux/netrom.h	Thu Jan  2 15:13:27 1997
@@ -33,6 +33,8 @@
 	char mnemonic[7];
 	ax25_address neighbour;
 	unsigned int obs_count;
+	unsigned int ndigis;
+	ax25_address digipeaters[AX25_MAX_DIGIS];
 };
 
 struct nr_ctl_struct {
diff -u --recursive --new-file v2.1.19/linux/include/linux/tpqic02.h linux/include/linux/tpqic02.h
--- v2.1.19/linux/include/linux/tpqic02.h	Thu Dec 12 19:37:19 1996
+++ linux/include/linux/tpqic02.h	Thu Jan  2 15:36:22 1997
@@ -1,4 +1,4 @@
-/* $Id: tpqic02.h,v 0.25 1994/07/21 02:16:30 root Exp root $
+/* $Id: tpqic02.h,v 1.5 1996/12/14 23:01:38 root Exp root $
  *
  * Include file for QIC-02 driver for Linux.
  *
@@ -12,7 +12,7 @@
 
 #include <linux/config.h>
 
-#if defined(CONFIG_QIC02_TAPE) || defined (CONFIG_QIC02_TAPE_MODULE)
+#if CONFIG_QIC02_TAPE || CONFIG_QIC02_TAPE_MODULE
 
 /* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */
 #include <linux/mtio.h>
@@ -28,6 +28,9 @@
  *
  * Support for Mountain controllers was added by Erik Jacobson
  * and severely hacked by me.   -- hhb
+ * 
+ * Support for Emerald controllers by Alan Bain <afrb2@chiark.chu.cam.ac.uk>
+ * with more hacks by me.   -- hhb
  */
 #define WANGTEK		1		   /* don't know about Wangtek QIC-36 */
 #define EVEREX		(WANGTEK+1)  /* I heard *some* of these are identical */
@@ -39,6 +42,11 @@
 #define ARCHIVE_SC499	ARCHIVE       /* SC402 and SC499R should be identical */
 
 #define MOUNTAIN	5		       /* Mountain Computer Interface */
+#define EMERALD		6		       /* Emerald Interface card */
+
+
+
+#define QIC02_TAPE_PORT_RANGE 	8	 /* number of IO locations to reserve */
 
 
 /*********** START OF USER CONFIGURABLE SECTION ************/
@@ -205,6 +213,7 @@
 #define WT_QIC02_DATA_PORT	(QIC02_TAPE_PORT+1)
 
 /* status register bits (Active LOW!) */
+#define WT_QIC02_STAT_POLARITY	0
 #define WT_QIC02_STAT_READY	0x01
 #define WT_QIC02_STAT_EXCEPTION	0x02
 #define WT_QIC02_STAT_MASK	(WT_QIC02_STAT_READY|WT_QIC02_STAT_EXCEPTION)
@@ -221,6 +230,31 @@
 #define WT_CTL_DMA3		0x10			  /* enable dma chan3 */
 #define WT_CTL_DMA1		0x08	         /* enable dma chan1 or chan2 */
 
+/* EMERALD interface card specifics
+ * Much like Wangtek, only different polarity and bit locations
+ */
+#define EMR_QIC02_STAT_PORT	(QIC02_TAPE_PORT)
+#define EMR_QIC02_CTL_PORT	(QIC02_TAPE_PORT)
+#define EMR_QIC02_CMD_PORT	(QIC02_TAPE_PORT+1)
+#define EMR_QIC02_DATA_PORT	(QIC02_TAPE_PORT+1)
+
+/* status register bits (Active High!) */
+#define EMR_QIC02_STAT_POLARITY		1
+#define EMR_QIC02_STAT_READY		0x01
+#define EMR_QIC02_STAT_EXCEPTION	0x02
+#define EMR_QIC02_STAT_MASK	(EMR_QIC02_STAT_READY|EMR_QIC02_STAT_EXCEPTION)
+
+#define EMR_QIC02_STAT_RESETMASK	0x07
+#define EMR_QIC02_STAT_RESETVAL	(EMR_QIC02_STAT_RESETMASK & ~EMR_QIC02_STAT_EXCEPTION)
+
+/* controller register (QIC02_CTL_PORT) bits */
+#define EMR_QIC02_CTL_RESET	0x02
+#define EMR_QIC02_CTL_REQUEST	0x04
+#define EMR_CTL_ONLINE		0x01
+#define EMR_CTL_CMDOFF		0xC0 
+
+#define EMR_CTL_DMA3		0x10			  /* enable dma chan3 */
+#define EMR_CTL_DMA1		0x08	         /* enable dma chan1 or chan2 */
 
 
 
@@ -234,6 +268,7 @@
 #define AR_RESET_DMA_PORT	(QIC02_TAPE_PORT+3)
 
 /* STAT port bits */
+#define AR_QIC02_STAT_POLARITY	0
 #define AR_STAT_IRQF		0x80	/* active high, interrupt request flag */
 #define AR_QIC02_STAT_READY	0x40	/* active low */
 #define AR_QIC02_STAT_EXCEPTION	0x20	/* active low */
@@ -266,6 +301,7 @@
 #define MTN_W_DMA_WRITE_PORT	(QIC02_TAPE_PORT+3)
 
 /* STAT port bits */
+#define MTN_QIC02_STAT_POLARITY	 0
 #define MTN_QIC02_STAT_READY	 0x02	/* active low */
 #define MTN_QIC02_STAT_EXCEPTION 0x04	/* active low */
 #define MTN_QIC02_STAT_MASK	 (MTN_QIC02_STAT_READY|MTN_QIC02_STAT_EXCEPTION)
@@ -274,7 +310,7 @@
 #define MTN_QIC02_STAT_RESETMASK 0x07	/* check RDY,EXC,DMADONE */
 #define MTN_QIC02_STAT_RESETVAL	 ((MTN_QIC02_STAT_RESETMASK & ~MTN_QIC02_STAT_EXCEPTION) | MTN_STAT_DMADONE)
 
-  /* CTL port bits */
+/* CTL port bits */
 #define MTN_QIC02_CTL_RESET_NOT	 0x80	/* drive reset, active low */
 #define MTN_QIC02_CTL_RESET	 0x80	/* Fodder #definition to keep gcc happy */
 
@@ -294,6 +330,7 @@
 # define QIC02_TAPE_DEBUG	(qic02_tape_debug)
 
 # if QIC02_TAPE_IFC == WANGTEK	
+#  define QIC02_STAT_POLARITY	WT_QIC02_STAT_POLARITY
 #  define QIC02_STAT_PORT	WT_QIC02_STAT_PORT
 #  define QIC02_CTL_PORT	WT_QIC02_CTL_PORT
 #  define QIC02_CMD_PORT	WT_QIC02_CMD_PORT
@@ -320,7 +357,36 @@
 #   error Unsupported or incorrect DMA configuration.
 #  endif
 
+# elif QIC02_TAPE_IFC == EMERALD
+#  define QIC02_STAT_POLARITY	EMR_QIC02_STAT_POLARITY
+#  define QIC02_STAT_PORT	EMR_QIC02_STAT_PORT
+#  define QIC02_CTL_PORT	EMR_QIC02_CTL_PORT
+#  define QIC02_CMD_PORT	EMR_QIC02_CMD_PORT
+#  define QIC02_DATA_PORT	EMR_QIC02_DATA_PORT
+
+#  define QIC02_STAT_READY	EMR_QIC02_STAT_READY
+#  define QIC02_STAT_EXCEPTION	EMR_QIC02_STAT_EXCEPTION
+#  define QIC02_STAT_MASK	EMR_QIC02_STAT_MASK
+#  define QIC02_STAT_RESETMASK	EMR_QIC02_STAT_RESETMASK
+#  define QIC02_STAT_RESETVAL	EMR_QIC02_STAT_RESETVAL
+
+#  define QIC02_CTL_RESET	EMR_QIC02_CTL_RESET
+#  define QIC02_CTL_REQUEST	EMR_QIC02_CTL_REQUEST
+
+#  if QIC02_TAPE_DMA == 3
+#   ifdef QIC02_TAPE_DMA3_FIX
+#    define EMR_CTL_DMA		EMR_CTL_DMA1
+#   else
+#    define EMR_CTL_DMA		EMR_CTL_DMA3
+#   endif
+#  elif QIC02_TAPE_DMA == 1
+#    define EMR_CTL_DMA		EMR_CTL_DMA1
+#  else
+#   error Unsupported or incorrect DMA configuration.
+#  endif
+
 # elif QIC02_TAPE_IFC == ARCHIVE
+#  define QIC02_STAT_POLARITY	AR_QIC02_STAT_POLARITY
 #  define QIC02_STAT_PORT	AR_QIC02_STAT_PORT
 #  define QIC02_CTL_PORT	AR_QIC02_CTL_PORT
 #  define QIC02_CMD_PORT	AR_QIC02_CMD_PORT
@@ -340,6 +406,7 @@
 #  endif
 
 # elif QIC02_TAPE_IFC == MOUNTAIN
+#  define QIC02_STAT_POLARITY	MTN_QIC02_STAT_POLARITY
 #  define QIC02_STAT_PORT	MTN_QIC02_STAT_PORT
 #  define QIC02_CTL_PORT	MTN_QIC02_CTL_PORT
 #  define QIC02_CMD_PORT	MTN_QIC02_CMD_PORT
@@ -393,6 +460,7 @@
 # define QIC02_CMD_PORT 	(qic02_tape_ccb.port_cmd)
 # define QIC02_DATA_PORT 	(qic02_tape_ccb.port_data)
 
+# define QIC02_STAT_POLARITY	(qic02_tape_ccb.stat_polarity)
 # define QIC02_STAT_READY	(qic02_tape_ccb.stat_ready)
 # define QIC02_STAT_EXCEPTION	(qic02_tape_ccb.stat_exception)
 # define QIC02_STAT_MASK	(qic02_tape_ccb.stat_mask)
@@ -617,10 +685,10 @@
 
 /* NR_BLK_BUF is a `tuneable parameter'. If you're really low on
  * kernel space, you could decrease it to 1, or if you got a very
- * slow machine, you could increase it up to 128 blocks. Less kernel
+ * slow machine, you could increase it up to 127 blocks. Less kernel
  * buffer blocks result in more context-switching.
  */
-#define NR_BLK_BUF	20				    /* max 128 blocks */
+#define NR_BLK_BUF	20				    /* max 127 blocks */
 #define TAPE_BLKSIZE	512		  /* streamer tape block size (fixed) */
 #define TPQBUF_SIZE	(TAPE_BLKSIZE*NR_BLK_BUF)	       /* buffer size */
 
@@ -642,6 +710,7 @@
 	unsigned short	port_data;	/* Data port address */
 
 	/* status register bits */
+	unsigned short	stat_polarity;	/* invert status bits or not */
 	unsigned short	stat_ready;	/* drive ready */
 	unsigned short	stat_exception;	/* drive signals exception */
 	unsigned short	stat_mask;
@@ -656,8 +725,12 @@
 	unsigned short	dma_enable_value;
 };
 
-
+#if MODULE
+static int qic02_tape_init(void);
+#else
 extern int qic02_tape_init(void);			  /* for mem.c */
+#endif
+
 
 
 #endif /* CONFIG_QIC02_TAPE */
diff -u --recursive --new-file v2.1.19/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.19/linux/include/linux/tty.h	Thu Dec 12 19:37:20 1996
+++ linux/include/linux/tty.h	Thu Jan  2 15:34:30 1997
@@ -294,7 +294,7 @@
 extern int stl_init(void);
 extern int stli_init(void);
 extern int riscom8_init(void);
-extern int esp_init(void);
+extern int espserial_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
 			      const char *routine);
diff -u --recursive --new-file v2.1.19/linux/include/net/lapb.h linux/include/net/lapb.h
--- v2.1.19/linux/include/net/lapb.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/lapb.h	Thu Jan  2 15:13:27 1997
@@ -0,0 +1,121 @@
+#ifndef _LAPB_H
+#define _LAPB_H 
+#include <linux/lapb.h>
+
+#define LAPB_SLOWHZ	10		/* Run timing at 1/10 second */
+
+#define	LAPB_HEADER_LEN	20		/* LAPB over Ethernet + a bit more */
+
+#define	LAPB_ACK_PENDING_CONDITION	0x01
+#define	LAPB_REJECT_CONDITION		0x02
+#define	LAPB_PEER_RX_BUSY_CONDITION	0x04
+
+/* Control field templates */
+#define	LAPB_I		0x00	/* Information frames */
+#define	LAPB_S		0x01	/* Supervisory frames */
+#define	LAPB_U		0x03	/* Unnumbered frames */
+
+#define	LAPB_RR		0x01	/* Receiver ready */
+#define	LAPB_RNR	0x05	/* Receiver not ready */
+#define	LAPB_REJ	0x09	/* Reject */
+
+#define	LAPB_SABM	0x2F	/* Set Asynchronous Balanced Mode */
+#define	LAPB_SABME	0x6F	/* Set Asynchronous Balanced Mode Extended */
+#define	LAPB_DISC	0x43	/* Disconnect */
+#define	LAPB_DM		0x0F	/* Disconnected mode */
+#define	LAPB_UA		0x63	/* Unnumbered acknowledge */
+#define	LAPB_FRMR	0x87	/* Frame reject */
+
+#define LAPB_ILLEGAL	0x100	/* Impossible to be a real frame type */
+
+#define	LAPB_SPF	0x10	/* Poll/final bit for standard LAPB */
+#define	LAPB_EPF	0x01	/* Poll/final bit for extended LAPB */
+
+#define	LAPB_POLLOFF	0
+#define	LAPB_POLLON	1
+
+/* LAPB C-bit */
+#define LAPB_COMMAND	1
+#define LAPB_RESPONSE	2
+
+#define	LAPB_ADDR_A	0x03
+#define	LAPB_ADDR_B	0x01
+#define	LAPB_ADDR_C	0x0F
+#define	LAPB_ADDR_D	0x07
+
+/* Define Link State constants. */
+#define LAPB_STATE_0	0
+#define LAPB_STATE_1	1
+#define LAPB_STATE_2	2
+#define LAPB_STATE_3	3
+#define LAPB_STATE_4	4
+
+#define	LAPB_DEFAULT_MODE		(LAPB_STANDARD | LAPB_SLP | LAPB_DTE)
+#define	LAPB_DEFAULT_WINDOW		7			/* Window=7 */
+#define	LAPB_DEFAULT_T1			(5 * LAPB_SLOWHZ)	/* T1=5s    */
+#define	LAPB_DEFAULT_T2			(1 * LAPB_SLOWHZ)	/* T2=1s    */
+#define	LAPB_DEFAULT_N2			20			/* N2=20    */
+
+#define	LAPB_SMODULUS	8
+#define	LAPB_EMODULUS	128
+
+typedef struct lapb_cb {
+	struct lapb_cb		*next;
+	void			*token;
+	unsigned int		mode;
+	unsigned char		state;
+	unsigned short		vs, vr, va;
+	unsigned char		condition;
+	unsigned short		n2, n2count;
+	unsigned short		t1, t2;
+	unsigned short		t1timer, t2timer;
+	struct sk_buff_head	write_queue;
+	struct sk_buff_head	ack_queue;
+	unsigned char		window;
+	struct timer_list	timer;
+	struct lapb_register_struct callbacks;
+} lapb_cb;
+
+/* lapb_iface.c */
+extern lapb_cb *lapb_tokentostruct(void *);
+extern void lapb_connect_confirmation(lapb_cb *, int);
+extern void lapb_connect_indication(lapb_cb *, int);
+extern void lapb_disconnect_confirmation(lapb_cb *, int);
+extern void lapb_disconnect_indication(lapb_cb *, int);
+extern int  lapb_data_indication(lapb_cb *, struct sk_buff *);
+extern int  lapb_data_transmit(lapb_cb *, struct sk_buff *);
+
+/* lapb_in.c */
+
+/* lapb_out.c */
+extern void lapb_kick(lapb_cb *);
+extern void lapb_transmit_buffer(lapb_cb *, struct sk_buff *, int);
+extern void lapb_nr_error_recovery(lapb_cb *);
+extern void lapb_establish_data_link(lapb_cb *);
+extern void lapb_transmit_enquiry(lapb_cb *);
+extern void lapb_enquiry_response(lapb_cb *);
+extern void lapb_timeout_response(lapb_cb *);
+extern void lapb_check_iframes_acked(lapb_cb *, unsigned short);
+extern void lapb_check_need_response(lapb_cb *, int, int);
+
+/* lapb_subr.c */
+extern void lapb_clear_queues(lapb_cb *);
+extern void lapb_frames_acked(lapb_cb *, unsigned short);
+extern void lapb_requeue_frames(lapb_cb *);
+extern int  lapb_validate_nr(lapb_cb *, unsigned short);
+extern int  lapb_decode(lapb_cb *, struct sk_buff *, int *, int *, int *, int *);
+extern void lapb_send_control(lapb_cb *, int, int, int);
+
+/* lapb_timer.c */
+extern void lapb_set_timer(lapb_cb *);
+
+/*
+ * Debug levels.
+ *	0 = Off
+ *	1 = State Changes
+ *	2 = Packets I/O and State Changes
+ *	3 = Hex dumps, Packets I/O and State Changes.
+ */
+#define	LAPB_DEBUG	2
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/include/net/lapbcall.h linux/include/net/lapbcall.h
--- v2.1.19/linux/include/net/lapbcall.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/lapbcall.h	Thu Jan  2 15:13:27 1997
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void lapb_proto_init(struct net_proto *pro);
diff -u --recursive --new-file v2.1.19/linux/include/net/llc.h linux/include/net/llc.h
--- v2.1.19/linux/include/net/llc.h	Wed Dec 18 15:59:03 1996
+++ linux/include/net/llc.h	Thu Jan  2 15:13:27 1997
@@ -6,34 +6,6 @@
 typedef struct llc_struct *llcptr;
 
 /*
- *	LLC operations object.
- */
- 
-typedef struct
-{
-	void (* data_indication_ep) (llcptr llc, struct sk_buff *skb);
-	/* unit data returns 0 to keep the data 1 to free it */
-	int (* unit_data_indication_ep) (llcptr llc, int ll, char *xid_data);       
-	void (* connect_indication_ep) (llcptr llc);
-	void (* connect_confirm_ep) (llcptr llc);
-	void (* data_connect_indication_ep) (llcptr llc);
-	void (* data_connect_confirm_ep) (llcptr llc);
-	void (* disconnect_indication_ep) (llcptr llc);
-	void (* disconnect_confirm_ep) (llcptr llc);
-	void (* reset_confirm_ep) (llcptr llc, char lr);
-	void (* reset_indication_ep) (llcptr llc, char lr);
-#define LOCAL		0
-#define REMOTE		1
-	void (* xid_indication_ep) (llcptr llc, int ll, char *xid_data);
-	void (* test_indication_ep) (llcptr llc, int ll, char *test_data);
-	void (* report_status_ep) (llcptr llc, char status);
-#define FRMR_RECEIVED	0
-#define FRMR_SENT	1
-#define REMOTE_BUSY	2
-#define REMOTE_NOT_BUSY	3
-} llc_ops;
-
-/*
  *	LLC private data area structure.
  */
 
@@ -84,10 +56,10 @@
 	struct timer_list tl[4];
 
 	/* 
-	 *	Client entry points, called by the LLC 
+	 *	Client entry point, called by the LLC.
 	 */
 	 
-	llc_ops *ops;
+	void	(*llc_event)(struct llc_struct *);
 	
 	/*
 	 *	Mux and Demux variables
@@ -106,18 +78,20 @@
 #define MODE_ABM 2
 
 	int llc_callbacks;		/* Pending callbacks */
-#define LLC_CONNECT_INDICATION	1	
-#define LLC_CONNECT_CONFIRM	2
-#define LLC_DATA_INDICATION	4
+#define LLC_CONN_INDICATION	1	/* We have to ensure the names don't */
+#define LLC_CONN_CONFIRM	2	/* mix up with the 802 state table */
+#define LLC_DATA_INDIC		4
 #define LLC_DISC_INDICATION	8
 #define LLC_RESET_INDIC_LOC	16
 #define LLC_RESET_INDIC_REM	32
-#define LLC_RESET_CONFIRM	64
+#define LLC_RST_CONFIRM		64
 #define LLC_FRMR_RECV		128
 #define LLC_FRMR_SENT		256
 #define LLC_REMOTE_BUSY		512
 #define LLC_REMOTE_NOTBUSY	1024
-#define LLC_SET_REMOTE_BUSY	2048
+#define LLC_TEST_INDICATION	2048
+#define LLC_XID_INDICATION	4096
+#define LLC_UI_DATA		8192
 
 	struct sk_buff *inc_skb;	/* Saved data buffer for indications */
 	
@@ -156,5 +130,5 @@
 void		llc_xid_request(llcptr lp, char opt, int data_len, char *pdu_data);
 void		llc_test_request(llcptr lp, int data_len, char *pdu_data);
 
-int		register_cl2llc_client(llcptr llc, const char *device, llc_ops *ops, u8 *rmac, u8 ssap, u8 dsap);
+int		register_cl2llc_client(llcptr llc, const char *device, void (*ops)(llcptr), u8 *rmac, u8 ssap, u8 dsap);
 void		unregister_cl2llc_client(llcptr lp);
diff -u --recursive --new-file v2.1.19/linux/include/net/ndisc.h linux/include/net/ndisc.h
--- v2.1.19/linux/include/net/ndisc.h	Thu Dec 12 19:37:20 1996
+++ linux/include/net/ndisc.h	Thu Jan  2 15:40:04 1997
@@ -23,7 +23,7 @@
 #define NDISC_QUEUE_LEN	3
 
 #define NCF_NOARP		0x0100	/* no ARP needed on this device */
-#define NCF_SUBNET		0x0200   /* NC entry for subnet		*/
+#define NCF_SUBNET		0x0200  /* NC entry for subnet		*/
 #define NCF_INVALID		0x0400
 #define NCF_DELAY_EXPIRED	0x0800	/* time to move to PROBE	*/
 #define NCF_ROUTER		0x1000	/* neighbour is a router	*/
diff -u --recursive --new-file v2.1.19/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.1.19/linux/include/net/neighbour.h	Thu Dec 12 19:37:20 1996
+++ linux/include/net/neighbour.h	Thu Jan  2 15:40:04 1997
@@ -119,15 +119,13 @@
 	if (tbl->tbl_lock == 1)
 	{
 		neigh_table_ins(tbl, neigh);
-		end_bh_atomic();
 	}
 	else
 	{
-		end_bh_atomic();
 		tbl->tbl_bh_mask |= NT_MASK_QUEUE;
 		neigh_queue_ins(tbl, neigh);
 	}
-	
+	end_bh_atomic();	
 }
 
 
@@ -164,7 +162,9 @@
 {
 	start_bh_atomic();
 	if (atomic_dec_and_test(&tbl->tbl_lock) && tbl->tbl_bh_mask)
+	{
 		neigh_tbl_run_bh(tbl);
+	}
 	end_bh_atomic();
 }
 
diff -u --recursive --new-file v2.1.19/linux/include/net/netbeui.h linux/include/net/netbeui.h
--- v2.1.19/linux/include/net/netbeui.h	Thu Dec 12 19:37:20 1996
+++ linux/include/net/netbeui.h	Thu Jan  2 15:13:27 1997
@@ -23,11 +23,17 @@
  
 struct nb_link
 {
+	struct  llc llc;	/* Must be first */
 	u8	mac[6];		/* Mac address of remote */
 	struct device *dev;	/* Device we heard him on */
-	struct llc *llc;	/* 802.2 link layer */
 	struct nb_ses *sessions;/* Netbeui sessions on this LLC link */
 	struct wait_queue *wait;/* Wait queue for this netbios LLC */
+	int	busy;		/* Track the LLC busy state */
+	int	state;		/* Link state */
+#define NETBEUI_OPEN		1	/* Up and going */
+#define NETBEUI_CONNWAIT	2	/* Waiting to come up */
+#define NETBEUI_DISCWAIT	3	/* Waiting to drop and recover */
+#define NETBEUI_DEADWAIT	4	/* Trying to die */
 };
 
 
diff -u --recursive --new-file v2.1.19/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.19/linux/include/net/sock.h	Wed Dec 18 15:59:03 1996
+++ linux/include/net/sock.h	Thu Jan  2 15:36:47 1997
@@ -689,6 +689,10 @@
 						     int noblock,
 						     int *errcode);
 
+extern void sklist_remove_socket(struct sock **list, struct sock *sk);
+extern void sklist_insert_socket(struct sock **list, struct sock *sk);
+extern void sklist_destroy_socket(struct sock **list, struct sock *sk);
+
 /*
  * 	Queue a received datagram if it will fit. Stream and sequenced
  *	protocols can't normally use this as they need to fit buffers in
diff -u --recursive --new-file v2.1.19/linux/include/net/x25.h linux/include/net/x25.h
--- v2.1.19/linux/include/net/x25.h	Wed Dec 18 15:59:03 1996
+++ linux/include/net/x25.h	Thu Jan  2 15:13:27 1997
@@ -59,6 +59,11 @@
 #define X25_STATE_3		3		/* Data Transfer */
 #define	X25_STATE_4		4		/* Awaiting Reset Confirmation */
 
+#define	X25_LINK_STATE_0	0
+#define	X25_LINK_STATE_1	1
+#define	X25_LINK_STATE_2	2
+#define	X25_LINK_STATE_3	3
+
 #define X25_DEFAULT_T20		(180 * X25_SLOWHZ)	/* Default T20 value */
 #define X25_DEFAULT_T21		(200 * X25_SLOWHZ)	/* Default T21 value */
 #define X25_DEFAULT_T22		(180 * X25_SLOWHZ)	/* Default T22 value */
@@ -148,10 +153,11 @@
 #include <net/x25call.h>
 
 /* x25_dev.c */
-extern int  x25_link_up(struct device *);
-extern void x25_send_frame(struct sk_buff *, struct device *);
+extern void x25_send_frame(struct sk_buff *, struct x25_neigh *);
 extern int  x25_lapb_receive_frame(struct sk_buff *, struct device *, struct packet_type *);
 extern int  x25_llc_receive_frame(struct sk_buff *, struct device *, struct packet_type *);
+extern void x25_establish_link(struct x25_neigh *);
+extern void x25_terminate_link(struct x25_neigh *);
 
 /* x25_in.c */
 extern int  x25_process_rx_frame(struct sock *, struct sk_buff *);
@@ -160,6 +166,8 @@
 extern void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short);
 extern void x25_link_device_up(struct device *);
 extern void x25_link_device_down(struct device *);
+extern void x25_link_established(struct x25_neigh *);
+extern void x25_link_terminated(struct x25_neigh *);
 extern void x25_transmit_restart_request(struct x25_neigh *);
 extern void x25_transmit_restart_confirmation(struct x25_neigh *);
 extern void x25_transmit_diagnostic(struct x25_neigh *, unsigned char);
diff -u --recursive --new-file v2.1.19/linux/init/main.c linux/init/main.c
--- v2.1.19/linux/init/main.c	Mon Dec 30 15:39:16 1996
+++ linux/init/main.c	Thu Jan  2 15:13:27 1997
@@ -97,6 +97,7 @@
 extern void aic7xxx_setup(char *str, int *ints);
 extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
+extern void ncr53c8xx_setup(char *str, int *ints);
 extern void eata2x_setup(char *str, int *ints);
 extern void u14_34f_setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
@@ -192,7 +193,9 @@
 #ifdef CONFIG_SOUNDMODEM
 extern void sm_setup(char *str, int *ints);
 #endif
-
+#ifdef CONFIG_WDT
+extern void wdt_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -345,6 +348,9 @@
 #ifdef CONFIG_SCSI_BUSLOGIC
 	{ "BusLogic=", BusLogic_Setup},
 #endif
+#ifdef CONFIG_SCSI_NCR53C8XX
+	{ "ncr53c8xx=", ncr53c8xx_setup},
+#endif
 #ifdef CONFIG_SCSI_EATA
 	{ "eata=", eata2x_setup},
 #endif
@@ -460,6 +466,9 @@
 #endif
 #ifdef CONFIG_SOUNDMODEM
 	{ "soundmodem=", sm_setup },
+#endif
+#ifdef CONFIG_WDT
+	{ "wdt=", wdt_setup },
 #endif
 	{ 0, 0 }
 };
diff -u --recursive --new-file v2.1.19/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.19/linux/kernel/fork.c	Tue Dec 31 21:41:12 1996
+++ linux/kernel/fork.c	Wed Jan  1 16:20:45 1997
@@ -82,10 +82,12 @@
 
 	mm->mmap = NULL;
 	p = &mm->mmap;
+	flush_cache_mm(current->mm);
 	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
 		tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
 		if (!tmp) {
 			exit_mmap(mm);
+			flush_tlb_mm(current->mm);
 			return -ENOMEM;
 		}
 		*tmp = *mpnt;
@@ -101,6 +103,7 @@
 		}
 		if (copy_page_range(mm, current->mm, tmp)) {
 			exit_mmap(mm);
+			flush_tlb_mm(current->mm);
 			return -ENOMEM;
 		}
 		if (tmp->vm_ops && tmp->vm_ops->open)
@@ -108,6 +111,7 @@
 		*p = tmp;
 		p = &tmp->vm_next;
 	}
+	flush_tlb_mm(current->mm);
 	build_mmap_avl(mm);
 	return 0;
 }
diff -u --recursive --new-file v2.1.19/linux/mm/memory.c linux/mm/memory.c
--- v2.1.19/linux/mm/memory.c	Sun Dec 22 16:37:42 1996
+++ linux/mm/memory.c	Wed Jan  1 16:20:45 1997
@@ -137,10 +137,8 @@
 		printk("%s trying to clear kernel page-directory: not good\n", tsk->comm);
 		return;
 	}
-	flush_cache_mm(tsk->mm);
 	for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
 		free_one_pgd(page_dir + i);
-	flush_tlb_mm(tsk->mm);
 }
 
 /*
@@ -171,10 +169,8 @@
 	if (!(new_pg = pgd_alloc()))
 		return -ENOMEM;
 	page_dir = pgd_offset(&init_mm, 0);
-	flush_cache_mm(tsk->mm);
 	memcpy(new_pg + USER_PTRS_PER_PGD, page_dir + USER_PTRS_PER_PGD,
 	       (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof (pgd_t));
-	flush_tlb_mm(tsk->mm);
 	SET_PAGE_DIR(tsk, new_pg);
 	tsk->mm->pgd = new_pg;
 	return 0;
@@ -286,17 +282,12 @@
 	cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
 	src_pgd = pgd_offset(src, address);
 	dst_pgd = pgd_offset(dst, address);
-	flush_cache_range(src, vma->vm_start, vma->vm_end);
-	flush_cache_range(dst, vma->vm_start, vma->vm_end);
 	while (address < end) {
 		error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address, cow);
 		if (error)
 			break;
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 	}
-	/* Note that the src ptes get c-o-w treatment, so they change too. */
-	flush_tlb_range(src, vma->vm_start, vma->vm_end);
-	flush_tlb_range(dst, vma->vm_start, vma->vm_end);
 	return error;
 }
 
@@ -380,20 +371,17 @@
 /*
  * remove user pages in a given range.
  */
-int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
+void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
 {
 	pgd_t * dir;
 	unsigned long end = address + size;
 
 	dir = pgd_offset(mm, address);
-	flush_cache_range(mm, end - size, end);
 	while (address < end) {
 		zap_pmd_range(dir, address, end - address);
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	}
-	flush_tlb_range(mm, end - size, end);
-	return 0;
 }
 
 static inline void zeromap_pte_range(pte_t * pte, unsigned long address, unsigned long size, pte_t zero_pte)
@@ -580,7 +568,7 @@
 	}
 	flush_page_to_ram(page);
 	set_pte(pte, pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY))));
-/* no need for invalidate */
+/* no need for flush_tlb */
 	return page;
 }
 
@@ -731,13 +719,17 @@
 		return;
 	mpnt = inode->i_mmap;
 	do {
+		struct mm_struct *mm = mpnt->vm_mm;
 		unsigned long start = mpnt->vm_start;
-		unsigned long len = mpnt->vm_end - start;
+		unsigned long end = mpnt->vm_end;
+		unsigned long len = end - start;
 		unsigned long diff;
 
 		/* mapping wholly truncated? */
 		if (mpnt->vm_offset >= offset) {
-			zap_page_range(mpnt->vm_mm, start, len);
+			flush_cache_range(mm, start, end);
+			zap_page_range(mm, start, len);
+			flush_tlb_range(mm, start, end);
 			continue;
 		}
 		/* mapping wholly unaffected? */
@@ -751,7 +743,9 @@
 			partial_clear(mpnt, start);
 			start = (start + ~PAGE_MASK) & PAGE_MASK;
 		}
-		zap_page_range(mpnt->vm_mm, start, len);
+		flush_cache_range(mm, start, end);
+		zap_page_range(mm, start, len);
+		flush_tlb_range(mm, start, end);
 	} while ((mpnt = mpnt->vm_next_share) != inode->i_mmap);
 }
 
diff -u --recursive --new-file v2.1.19/linux/mm/mmap.c linux/mm/mmap.c
--- v2.1.19/linux/mm/mmap.c	Tue Dec 31 21:41:12 1996
+++ linux/mm/mmap.c	Wed Jan  1 16:20:45 1997
@@ -833,7 +833,7 @@
 	 * it will put new vm_area_struct(s) into the address space.
 	 */
 	do {
-		unsigned long st, end;
+		unsigned long st, end, size;
 
 		mpnt = free;
 		free = free->vm_next;
@@ -843,11 +843,14 @@
 		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
 		end = addr+len;
 		end = end > mpnt->vm_end ? mpnt->vm_end : end;
+		size = end - st;
 
 		if (mpnt->vm_ops && mpnt->vm_ops->unmap)
-			mpnt->vm_ops->unmap(mpnt, st, end-st);
-		zap_page_range(current->mm, st, end-st);
-		unmap_fixup(mpnt, st, end-st);
+			mpnt->vm_ops->unmap(mpnt, st, size);
+		flush_cache_range(current->mm, st, end);
+		zap_page_range(current->mm, st, size);
+		flush_tlb_range(current->mm, st, end);
+		unmap_fixup(mpnt, st, size);
 		kfree(mpnt);
 	} while (free);
 
@@ -879,14 +882,18 @@
 	mm->locked_vm = 0;
 	while (mpnt) {
 		struct vm_area_struct * next = mpnt->vm_next;
+		unsigned long start = mpnt->vm_start;
+		unsigned long end = mpnt->vm_end;
+		unsigned long size = end - start;
+
 		if (mpnt->vm_ops) {
 			if (mpnt->vm_ops->unmap)
-				mpnt->vm_ops->unmap(mpnt, mpnt->vm_start, mpnt->vm_end-mpnt->vm_start);
+				mpnt->vm_ops->unmap(mpnt, start, size);
 			if (mpnt->vm_ops->close)
 				mpnt->vm_ops->close(mpnt);
 		}
 		remove_shared_vm_struct(mpnt);
-		zap_page_range(mm, mpnt->vm_start, mpnt->vm_end-mpnt->vm_start);
+		zap_page_range(mm, start, size);
 		if (mpnt->vm_inode)
 			iput(mpnt->vm_inode);
 		kfree(mpnt);
diff -u --recursive --new-file v2.1.19/linux/mm/mremap.c linux/mm/mremap.c
--- v2.1.19/linux/mm/mremap.c	Fri Nov 22 18:28:22 1996
+++ linux/mm/mremap.c	Wed Jan  1 16:20:45 1997
@@ -119,8 +119,8 @@
 	flush_cache_range(mm, new_addr, new_addr + len);
 	while ((offset += PAGE_SIZE) < len)
 		move_one_page(mm, new_addr + offset, old_addr + offset);
-	flush_tlb_range(mm, new_addr, new_addr + len);
 	zap_page_range(mm, new_addr, new_addr + len);
+	flush_tlb_range(mm, new_addr, new_addr + len);
 	return -1;
 }
 
diff -u --recursive --new-file v2.1.19/linux/net/802/cl2llc.c linux/net/802/cl2llc.c
--- v2.1.19/linux/net/802/cl2llc.c	Wed Dec 18 15:59:14 1996
+++ linux/net/802/cl2llc.c	Thu Jan  2 15:13:27 1997
@@ -97,6 +97,11 @@
 				break;
 			default:
 		}
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
 		return 0;  
 	}              
 }
@@ -118,6 +123,14 @@
 	{
 		lp->state = D_CONN;
 		llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
+		/*
+ 		 *	lp may be invalid after the callback
+		 */
 	}
 }
 
@@ -132,6 +145,14 @@
 	{
 		lp->state = SETUP;
 		llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
+		/*
+ 		 *	lp may be invalid after the callback
+		 */
 	}
 }
 
@@ -196,53 +217,41 @@
 			case 2:
 				lp->state = NORMAL;  /* needed to eliminate connect_response() */
 				lp->llc_mode = MODE_ABM;
-				if (lp->ops->connect_indication_ep != NULL)
-					lp->ops->connect_indication_ep(lp);
+				lp->llc_callbacks|=LLC_CONN_INDICATION;
 				break;
 			case 3:
 				lp->llc_mode = MODE_ABM;
-				if (lp->ops->connect_confirm_ep != NULL)
-					lp->ops->connect_confirm_ep(lp);
+				lp->llc_callbacks|=LLC_CONN_CONFIRM;
 				break;
 			case 4:
-				if (lp->ops->data_indication_ep != NULL)
-				{
-					skb_pull(skb, 4);
-					lp->ops->data_indication_ep(lp, skb);
-				}
+				skb_pull(skb, 4);
+				lp->inc_skb=skb;
+				lp->llc_callbacks|=LLC_DATA_INDIC;
 				break;
 			case 5:
 				lp->llc_mode = MODE_ADM;
-				if (lp->ops->disconnect_indication_ep != NULL)
-					lp->ops->disconnect_indication_ep(lp);
+				lp->llc_callbacks|=LLC_DISC_INDICATION;
 				break;
 			case 70:
-				if (lp->ops->reset_indication_ep != NULL)
-					lp->ops->reset_indication_ep(lp, LOCAL);
+				lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
 				break;
 			case 71:
-				if (lp->ops->reset_indication_ep != NULL)
-					lp->ops->reset_indication_ep(lp, REMOTE);
+				lp->llc_callbacks|=LLC_RESET_INDIC_REM;
 				break;
 			case 7:
-				if (lp->ops->reset_confirm_ep != NULL)
-					lp->ops->reset_confirm_ep(lp, REMOTE);
+				lp->llc_callbacks|=LLC_RST_CONFIRM;
 				break;
 			case 66:
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, FRMR_RECEIVED);
+				lp->llc_callbacks|=LLC_FRMR_RECV;
 				break;
 			case 67:
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, FRMR_SENT);
+				lp->llc_callbacks|=LLC_FRMR_SENT;
 				break;
 			case 68:
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				lp->llc_callbacks|=LLC_REMOTE_BUSY;
 				break;
 			case 69:
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY);
+				lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
 				break;
 			case 11:
 				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
@@ -322,8 +331,7 @@
 				{
 					lp->remote_busy = 1;
 					llc_start_timer(lp, BUSY_TIMER);
-					if (lp->ops->report_status_ep != NULL)
-						lp->ops->report_status_ep(lp, REMOTE_BUSY);
+					lp->llc_callbacks|=LLC_REMOTE_BUSY;
 				}
 				else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
 				{
@@ -559,6 +567,15 @@
 	if (pc_label != 0)
 	{ 
 		llc_interpret_pseudo_code(lp, pc_label, skb, type);
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
+		/*
+ 		 *	lp may no longer be valid after this point. Be
+		 *	careful what is added!
+		 */
 	}
 }
 
@@ -585,5 +602,13 @@
 		lp->state = timertr_entry[idx +1];
 	}
 	lp->timer_state[t] = TIMER_IDLE;
+	if(lp->llc_callbacks)
+	{
+		lp->llc_event(lp);
+		lp->llc_callbacks=0;
+	}
+	/*
+ 	 *	And lp may have vanished in the event callback
+ 	 */
 }
 
diff -u --recursive --new-file v2.1.19/linux/net/802/cl2llc.pre linux/net/802/cl2llc.pre
--- v2.1.19/linux/net/802/cl2llc.pre	Wed Dec 18 15:59:14 1996
+++ linux/net/802/cl2llc.pre	Thu Jan  2 15:13:27 1997
@@ -21,6 +21,10 @@
  *	Changes
  *		Alan Cox	:	Chainsawed into Linux format
  *					Modified to use llc_ names
+ *					Changed callbacks
+ *
+ *			Note: TST/XID stuff is broken at the moment. The
+ *			buffer is freed before being passed up.
  *
  *	This file must be processed by sed before it can be compiled.
  */
@@ -97,6 +101,11 @@
 				break;
 			default:
 		}
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
 		return 0;  
 	}              
 }
@@ -118,6 +127,14 @@
 	{
 		lp->state = D_CONN;
 		llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
+		/*
+ 		 *	lp may be invalid after the callback
+		 */
 	}
 }
 
@@ -132,6 +149,14 @@
 	{
 		lp->state = SETUP;
 		llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
+		/*
+ 		 *	lp may be invalid after the callback
+		 */
 	}
 }
 
@@ -196,53 +221,41 @@
 			case CONNECT_INDICATION:
 				lp->state = NORMAL;  /* needed to eliminate connect_response() */
 				lp->llc_mode = MODE_ABM;
-				if (lp->ops->connect_indication_ep != NULL)
-					lp->ops->connect_indication_ep(lp);
+				lp->llc_callbacks|=LLC_CONN_INDICATION;
 				break;
 			case CONNECT_CONFIRM:
 				lp->llc_mode = MODE_ABM;
-				if (lp->ops->connect_confirm_ep != NULL)
-					lp->ops->connect_confirm_ep(lp);
+				lp->llc_callbacks|=LLC_CONN_CONFIRM;
 				break;
 			case DATA_INDICATION:
-				if (lp->ops->data_indication_ep != NULL)
-				{
-					skb_pull(skb, 4);
-					lp->ops->data_indication_ep(lp, skb);
-				}
+				skb_pull(skb, 4);
+				lp->inc_skb=skb;
+				lp->llc_callbacks|=LLC_DATA_INDIC;
 				break;
 			case DISCONNECT_INDICATION:
 				lp->llc_mode = MODE_ADM;
-				if (lp->ops->disconnect_indication_ep != NULL)
-					lp->ops->disconnect_indication_ep(lp);
+				lp->llc_callbacks|=LLC_DISC_INDICATION;
 				break;
 			case RESET_INDICATION(LOCAL):
-				if (lp->ops->reset_indication_ep != NULL)
-					lp->ops->reset_indication_ep(lp, LOCAL);
+				lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
 				break;
 			case RESET_INDICATION(REMOTE):
-				if (lp->ops->reset_indication_ep != NULL)
-					lp->ops->reset_indication_ep(lp, REMOTE);
+				lp->llc_callbacks|=LLC_RESET_INDIC_REM;
 				break;
 			case RESET_CONFIRM:
-				if (lp->ops->reset_confirm_ep != NULL)
-					lp->ops->reset_confirm_ep(lp, REMOTE);
+				lp->llc_callbacks|=LLC_RST_CONFIRM;
 				break;
 			case REPORT_STATUS(FRMR_RECEIVED):
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, FRMR_RECEIVED);
+				lp->llc_callbacks|=LLC_FRMR_RECV;
 				break;
 			case REPORT_STATUS(FRMR_SENT):
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, FRMR_SENT);
+				lp->llc_callbacks|=LLC_FRMR_SENT;
 				break;
 			case REPORT_STATUS(REMOTE_BUSY):
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				lp->llc_callbacks|=LLC_REMOTE_BUSY;
 				break;
 			case REPORT_STATUS(REMOTE_NOT_BUSY):
-				if (lp->ops->report_status_ep != NULL)
-					lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY);
+				lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
 				break;
 			case SEND_DISC_CMD(P=X):
 				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
@@ -322,8 +335,7 @@
 				{
 					lp->remote_busy = 1;
 					llc_start_timer(lp, BUSY_TIMER);
-					if (lp->ops->report_status_ep != NULL)
-						lp->ops->report_status_ep(lp, REMOTE_BUSY);
+					lp->llc_callbacks|=LLC_REMOTE_BUSY;
 				}
 				else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
 				{
@@ -559,6 +571,15 @@
 	if (pc_label != NOP)
 	{ 
 		llc_interpret_pseudo_code(lp, pc_label, skb, type);
+		if(lp->llc_callbacks)
+		{
+			lp->llc_event(lp);
+			lp->llc_callbacks=0;
+		}
+		/*
+ 		 *	lp may no longer be valid after this point. Be
+		 *	careful what is added!
+		 */
 	}
 }
 
@@ -585,5 +606,13 @@
 		lp->state = timertr_entry[idx +1];
 	}
 	lp->timer_state[t] = TIMER_IDLE;
+	if(lp->llc_callbacks)
+	{
+		lp->llc_event(lp);
+		lp->llc_callbacks=0;
+	}
+	/*
+ 	 *	And lp may have vanished in the event callback
+ 	 */
 }
 
diff -u --recursive --new-file v2.1.19/linux/net/802/fddi.c linux/net/802/fddi.c
--- v2.1.19/linux/net/802/fddi.c	Tue Nov 19 15:53:59 1996
+++ linux/net/802/fddi.c	Thu Jan  2 15:13:27 1997
@@ -20,7 +20,11 @@
  *		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.
+ *
+ *	Changes
+ *		Alan Cox	:	New arp/rebuild header
  */
+ 
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <linux/types.h>
@@ -45,21 +49,14 @@
  * daddr=NULL	means leave destination address (eg unresolved arp)
  */
 
-int fddi_header(
-	struct sk_buff	*skb,
-	struct device	*dev,
-	unsigned short	type,
-	void			*daddr,
-	void			*saddr,
-	unsigned		len
-	)
-
-	{
+int fddi_header(struct sk_buff	*skb, struct device *dev, unsigned short type,
+		void *daddr, void *saddr, unsigned len)
+{
 	struct fddihdr *fddi = (struct fddihdr *)skb_push(skb, FDDI_K_SNAP_HLEN);
 
 	/* Fill in frame header - assume 802.2 SNAP frames for now */
 
-	fddi->fc					 = FDDI_FC_K_ASYNC_LLC_DEF;
+	fddi->fc			 = FDDI_FC_K_ASYNC_LLC_DEF;
 	fddi->hdr.llc_snap.dsap		 = FDDI_EXTENDED_SAP;
 	fddi->hdr.llc_snap.ssap		 = FDDI_EXTENDED_SAP;
 	fddi->hdr.llc_snap.ctrl		 = FDDI_UI_CMD;
@@ -76,12 +73,12 @@
 		memcpy(fddi->saddr, dev->dev_addr, dev->addr_len);
 
 	if (daddr != NULL)
-		{
+	{
 		memcpy(fddi->daddr, daddr, dev->addr_len);
 		return(FDDI_K_SNAP_HLEN);
-		}
-	return(-FDDI_K_SNAP_HLEN);
 	}
+	return(-FDDI_K_SNAP_HLEN);
+}
 
 
 /*
@@ -90,32 +87,22 @@
  * this sk_buff.  We now let ARP fill in the other fields.
  */
  
-int fddi_rebuild_header(
-	void			*buff,
-	struct device	*dev,
-	unsigned long	dest,
-	struct sk_buff	*skb
-	)
-
-	{
-	struct fddihdr *fddi = (struct fddihdr *)buff;
+int fddi_rebuild_header(struct sk_buff	*skb)
+{
+	struct fddihdr *fddi = (struct fddihdr *)skb->data;
 
 	/* Only ARP/IP is currently supported */
 	 
 	if (fddi->hdr.llc_snap.ethertype != htons(ETH_P_IP))
-		{
+	{
 		printk("fddi_rebuild_header: Don't know how to resolve type %04X addresses?\n", (unsigned int)htons(fddi->hdr.llc_snap.ethertype));
 		return(0);
-		}
+	}
 
 	/* Try to get ARP to resolve the header and fill destination address */
 
-	if (arp_find(fddi->daddr, dest, dev, dev->pa_addr, skb))
-		return(1);
-	else
-		return(0);
-	}
-
+	return arp_find(fddi->daddr, skb) ? 1 : 0;
+}
 
 /*
  * Determine the packet's protocol ID and fill in skb fields.
@@ -124,12 +111,8 @@
  * the proper pointer to the start of packet data (skb->data).
  */
  
-unsigned short fddi_type_trans(
-	struct sk_buff	*skb,
-	struct device	*dev
-	)
-
-	{
+unsigned short fddi_type_trans(struct sk_buff *skb, struct device *dev)
+{
 	struct fddihdr *fddi = (struct fddihdr *)skb->data;
 
 	/*
@@ -143,20 +126,20 @@
 	/* Set packet type based on destination address and flag settings */
 			
 	if (*fddi->daddr & 0x01)
-		{
+	{
 		if (memcmp(fddi->daddr, dev->broadcast, FDDI_K_ALEN) == 0)
 			skb->pkt_type = PACKET_BROADCAST;
 		else
 			skb->pkt_type = PACKET_MULTICAST;
-		}
+	}
 	
 	else if (dev->flags & IFF_PROMISC)
-		{
+	{
 		if (memcmp(fddi->daddr, dev->dev_addr, FDDI_K_ALEN))
 			skb->pkt_type = PACKET_OTHERHOST;
-		}
+	}
 
 	/* Assume 802.2 SNAP frames, for now */
 
 	return(fddi->hdr.llc_snap.ethertype);
-	}
+}
diff -u --recursive --new-file v2.1.19/linux/net/802/llc_macinit.c linux/net/802/llc_macinit.c
--- v2.1.19/linux/net/802/llc_macinit.c	Mon Dec 30 15:39:16 1996
+++ linux/net/802/llc_macinit.c	Thu Jan  2 15:13:27 1997
@@ -84,11 +84,8 @@
 					fr->u_hdr.u_info);
 				break;
 			case TEST_RSP:
-				if (lp->ops->test_indication_ep != NULL)
-				{
-					lp->ops->test_indication_ep(lp,
-						ll -3, fr->u_hdr.u_info);
-				}
+				lp->llc_callbacks|=LLC_TEST_INDICATION;
+				lp->inc_skb=skb;
 				break;
 			case XID_CMD:
 				/*
@@ -110,19 +107,14 @@
 				{
 					lp->k = fr->u_hdr.u_info[2];
 				}
-				if (lp->ops->xid_indication_ep != NULL)
-				{
-					lp->ops->xid_indication_ep(lp,
-						ll-3, fr->u_hdr.u_info);
-				}
+				lp->llc_callbacks|=LLC_XID_INDICATION;
+				lp->inc_skb=skb;
 				break;
 
 			case UI_CMD:
-				if(lp->ops->unit_data_indication_ep != NULL)
-				{
-					free=lp->ops->unit_data_indication_ep(lp,
-						ll-3, fr->u_hdr.u_info);
-				}
+				lp->llc_callbacks|=LLC_UI_DATA;
+				skb_pull(skb,3);
+				lp->inc_skb=skb;
 				break;
 
 			default:
@@ -140,6 +132,12 @@
 		skb->sk = NULL;
 		kfree_skb(skb, FREE_READ);
 	}
+
+	if(lp->llc_callbacks)
+	{
+		lp->llc_event(lp);
+		lp->llc_callbacks=0;
+	}
 	return 0;
 }
 
@@ -149,7 +147,7 @@
  *	LLC's on device down, the device list must be locked before this call.
  */
 
-int register_cl2llc_client(llcptr lp, const char *device, llc_ops *ops, u8 *rmac, u8 ssap, u8 dsap)
+int register_cl2llc_client(llcptr lp, const char *device, void (*event)(llcptr), u8 *rmac, u8 ssap, u8 dsap)
 {
 	char eye_init[] = "LLC\0";
 
@@ -167,7 +165,7 @@
 	lp->timer_interval[ACK_TIMER] = HZ/8;
 	lp->timer_interval[BUSY_TIMER] = HZ*2;
 	lp->local_sap = ssap;
-	lp->ops = ops;
+	lp->llc_event = event;
 	lp->remote_mac_len = lp->dev->addr_len;
 	memcpy(lp->remote_mac, rmac, lp->remote_mac_len);
 	lp->state = 0;
diff -u --recursive --new-file v2.1.19/linux/net/802/p8022.c linux/net/802/p8022.c
--- v2.1.19/linux/net/802/p8022.c	Mon Dec 30 15:39:16 1996
+++ linux/net/802/p8022.c	Thu Jan  2 15:13:27 1997
@@ -1,3 +1,21 @@
+/*
+ *	NET3:	Support for 802.2 demultiplexing off ethernet (Token ring
+ *		is kept seperate see p8022tr.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 of the License, or (at your option) any later version.
+ *
+ *		Demultiplex 802.2 encoded protocols. We match the entry by the
+ *		SSAP/DSAP pair and then deliver to the registered datalink that
+ *		matches. The control byte is ignored and handling of such items
+ *		is up to the routine passed the frame.
+ *
+ *		Unlike the 802.3 datalink we have a list of 802.2 entries as there
+ *		are multiple protocols to demux. The list is currently short (3 or
+ *		4 entries at most). The current demux assumes this.
+ */
+
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
@@ -12,11 +30,11 @@
  *	We don't handle the loopback SAP stuff, the extended
  *	802.2 command set, multicast SAP identifiers and non UI
  *	frames. We have the absolute minimum needed for IPX,
- *	IP and Appletalk phase 2.
+ *	IP and Appletalk phase 2. See the llc_* routines for
+ *	support libraries if your protocol needs these.
  */
 
-static struct datalink_proto *
-find_8022_client(unsigned char type)
+static struct datalink_proto *find_8022_client(unsigned char type)
 {
 	struct datalink_proto	*proto;
 
@@ -28,13 +46,13 @@
 	return proto;
 }
 
-int
-p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+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) {
+	if (proto != NULL) 
+	{
 		skb->h.raw += 3;
 		skb_pull(skb,3);
 		return proto->rcvfunc(skb, dev, pt);
@@ -45,8 +63,7 @@
 	return 0;
 }
 
-static void
-p8022_datalink_header(struct datalink_proto *dl,
+static void p8022_datalink_header(struct datalink_proto *dl,
 		struct sk_buff *skb, unsigned char *dest_node)
 {
 	struct device	*dev = skb->dev;
@@ -77,8 +94,7 @@
 	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 *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
 {
 	struct datalink_proto	*proto;
 
diff -u --recursive --new-file v2.1.19/linux/net/802/p8022tr.c linux/net/802/p8022tr.c
--- v2.1.19/linux/net/802/p8022tr.c	Mon Dec 30 15:39:16 1996
+++ linux/net/802/p8022tr.c	Thu Jan  2 15:13:27 1997
@@ -1,3 +1,13 @@
+/*
+ * NET3:	Handling for token ring frames that are not IP. IP is hooked
+ *		early in the token ring support code.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
@@ -14,11 +24,14 @@
  *	We don't handle the loopback SAP stuff, the extended
  *	802.2 command set, multicast SAP identifiers and non UI
  *	frames. We have the absolute minimum needed for IPX,
- *	IP and Appletalk phase 2.
+ *	IP and Appletalk phase 2. See the llc_* routines for support
+ *	to handle the fun stuff.
+ *
+ *	We assume the list will be very short (at the moment its normally
+ *	one or two entries).
  */
 
-static struct datalink_proto *
-find_8022tr_client(unsigned char type)
+static struct datalink_proto *find_8022tr_client(unsigned char type)
 {
 	struct datalink_proto	*proto;
 
@@ -30,8 +43,7 @@
 	return proto;
 }
 
-int
-p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+int p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct datalink_proto	*proto;
 
@@ -47,8 +59,7 @@
 	return 0;
 }
 
-static void
-p8022tr_datalink_header(struct datalink_proto *dl,
+static void p8022tr_datalink_header(struct datalink_proto *dl,
 		struct sk_buff *skb, unsigned char *dest_node)
 {
 	struct device	*dev = skb->dev;
@@ -85,8 +96,8 @@
 	dev_add_pack(&p8022tr_packet_type);
 }
 
-struct datalink_proto *
-register_8022tr_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+struct datalink_proto *register_8022tr_client(unsigned char type, 
+	int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
 {
 	struct datalink_proto	*proto;
 
diff -u --recursive --new-file v2.1.19/linux/net/802/p8023.c linux/net/802/p8023.c
--- v2.1.19/linux/net/802/p8023.c	Mon Mar 25 08:58:23 1996
+++ linux/net/802/p8023.c	Thu Jan  2 15:13:27 1997
@@ -1,34 +1,59 @@
+/*
+ *	NET3:	802.3 data link hooks used for IPX 802.3
+ *
+ *	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.
+ *
+ *	802.3 isn't really a protocol data link layer. Some old IPX stuff
+ *	uses it however. Note that there is only one 802.3 protocol layer
+ *	in the system. We don't currently support different protocols
+ *	running raw 802.3 on different devices. Thankfully nobody else
+ *	has done anything like the old IPX.
+ */
+ 
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <net/datalink.h>
 #include <linux/mm.h>
 #include <linux/in.h>
 
-static void
-p8023_datalink_header(struct datalink_proto *dl, 
+/*
+ *	Place an 802.3 header on a packet. The driver will do the mac
+ *	addresses, we just need to give it the buffer length.
+ */
+ 
+static void p8023_datalink_header(struct datalink_proto *dl, 
 		struct sk_buff *skb, unsigned char *dest_node)
 {
 	struct device	*dev = skb->dev;
-	
 	dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
 }
 
-struct datalink_proto *
-make_8023_client(void)
+/*
+ *	Create an 802.3 client. Note there can be only one 802.3 client
+ */
+ 
+struct datalink_proto *make_8023_client(void)
 {
 	struct datalink_proto	*proto;
 
 	proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
-	if (proto != NULL) {
+	if (proto != NULL) 
+	{
 		proto->type_len = 0;
 		proto->header_length = 0;
 		proto->datalink_header = p8023_datalink_header;
 		proto->string_name = "802.3";
 	}
-
 	return proto;
 }
 
+/*
+ *	Destroy the 802.3 client.
+ */
+ 
 void destroy_8023_client(struct datalink_proto *dl)
 {
 	if (dl)
diff -u --recursive --new-file v2.1.19/linux/net/802/sysctl_net_802.c linux/net/802/sysctl_net_802.c
--- v2.1.19/linux/net/802/sysctl_net_802.c	Tue Apr  2 09:03:35 1996
+++ linux/net/802/sysctl_net_802.c	Thu Jan  2 15:13:27 1997
@@ -1,8 +1,13 @@
 /* -*- linux-c -*-
- * sysctl_net_802.c: sysctl interface to net 802 subsystem.
+ *		sysctl_net_802.c: sysctl interface to net 802 subsystem.
  *
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/802 directory entry (empty =) ). [MS]
+ *		Begun April 1, 1996, Mike Shaver.
+ *		Added /proc/sys/net/802 directory entry (empty =) ). [MS]
+ *
+ *		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/mm.h>
diff -u --recursive --new-file v2.1.19/linux/net/802/tr.c linux/net/802/tr.c
--- v2.1.19/linux/net/802/tr.c	Thu Dec 12 19:37:21 1996
+++ linux/net/802/tr.c	Thu Jan  2 15:13:27 1997
@@ -1,3 +1,14 @@
+/*
+ * NET3:	Token ring device handling subroutines
+ * 
+ *		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.
+ *
+ * Fixes:
+ */
+
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/types.h>
@@ -23,7 +34,12 @@
 
 typedef struct rif_cache_s *rif_cache;
 
-struct rif_cache_s {	
+/*
+ *	Each RIF entry we learn is kept this way
+ */
+ 
+struct rif_cache_s
+{	
 	 unsigned char addr[TR_ALEN];
 	 unsigned short rcf;
 	 unsigned short rseg[8];
@@ -32,12 +48,32 @@
 };
 
 #define RIF_TABLE_SIZE 16
+
+/*
+ *	We hash the RIF cache 16 ways. We do after all have to look it
+ *	up a lot.
+ */
+ 
 rif_cache rif_table[RIF_TABLE_SIZE]={ NULL, };
 
 #define RIF_TIMEOUT 60*10*HZ
 #define RIF_CHECK_INTERVAL 60*HZ
-static struct timer_list rif_timer={ NULL,NULL,RIF_CHECK_INTERVAL,0L,rif_check_expire };
 
+/*
+ *	Garbage disposal timer.
+ */
+ 
+static struct timer_list rif_timer=
+{
+	NULL,NULL,RIF_CHECK_INTERVAL,0L,rif_check_expire
+};
+
+
+/*
+ *	Put the headers on a token ring packet. Token ring source routing
+ *	makes this a little more exciting than on ethernet.
+ */
+ 
 int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type,
               void *daddr, void *saddr, unsigned len) 
 {
@@ -53,13 +89,23 @@
 	else
 		memset(trh->saddr,0,dev->addr_len); /* Adapter fills in address */
 
+	/*
+	 *	This is the stuff needed for IP encoding - IP over 802.2
+	 *	with SNAP.
+	 */
+	 
 	trllc->dsap=trllc->ssap=EXTENDED_SAP;
 	trllc->llc=UI_CMD;
 	
 	trllc->protid[0]=trllc->protid[1]=trllc->protid[2]=0x00;
 	trllc->ethertype=htons(type);
 
-	if(daddr) {
+	/*
+	 *	Build the destination and then source route the frame
+	 */
+	 
+	if(daddr) 
+	{
 		memcpy(trh->daddr,daddr,dev->addr_len);
 		tr_source_route(trh,dev);
 		return(dev->hard_header_len);
@@ -68,12 +114,21 @@
 
 }
 	
-int tr_rebuild_header(struct sk_buff *skb) {
-
+/*
+ *	A neighbour discovery of some species (eg arp) has completed. We
+ *	can now send the packet.
+ */
+ 
+int tr_rebuild_header(struct sk_buff *skb) 
+{
 	struct trh_hdr *trh=(struct trh_hdr *)skb->data;
 	struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
 	struct device *dev = skb->dev;
 
+	/*
+	 *	FIXME: We don't yet support IPv6 over token rings
+	 */
+	 
 	if(trllc->ethertype != htons(ETH_P_IP)) {
 		printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(	trllc->ethertype));
 		return 0;
@@ -82,13 +137,21 @@
 	if(arp_find(trh->daddr, skb)) {
 			return 1;
 	}
-	else {	
+	else 
+	{	
 		tr_source_route(trh,dev); 
 		return 0;
 	}
 }
 	
-unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) {
+/*
+ *	Some of this is a bit hackish. We intercept RIF information
+ *	used for source routing. We also grab IP directly and don't feed
+ *	it via SNAP.
+ */
+ 
+unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) 
+{
 
 	struct trh_hdr *trh=(struct trh_hdr *)skb->data;
 	struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
@@ -117,31 +180,46 @@
  	return trllc->ethertype;
 }
 
-/* We try to do source routing... */
+/*
+ *	We try to do source routing... 
+ */
 
-static void tr_source_route(struct trh_hdr *trh,struct device *dev) {
+static void tr_source_route(struct trh_hdr *trh,struct device *dev) 
+{
 
 	int i;
 	unsigned int hash;
 	rif_cache entry;
 
-	/* Broadcasts are single route as stated in RFC 1042 */
-	if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) {
+	/*
+	 *	Broadcasts are single route as stated in RFC 1042 
+	 */
+	if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) 
+	{
 		trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK)  
 			       | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
 		trh->saddr[0]|=TR_RII;
 	}
-	else {
+	else 
+	{
 		for(i=0,hash=0;i<TR_ALEN;hash+=trh->daddr[i++]);
 		hash&=RIF_TABLE_SIZE-1;
+		/*
+		 *	Walk the hash table and look for an entry
+		 */
 		for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->daddr[0]),TR_ALEN);entry=entry->next);
 
-		if(entry) {
+		/*
+		 *	If we found an entry we can route the frame.
+		 */
+		if(entry) 
+		{
 #if 0
 printk("source routing for %02X %02X %02X %02X %02X %02X\n",trh->daddr[0],
 		  trh->daddr[1],trh->daddr[2],trh->daddr[3],trh->daddr[4],trh->daddr[5]);
 #endif
-			if((ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) {
+			if((ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) 
+			{
 				trh->rcf=entry->rcf;
 				memcpy(&trh->rseg[0],&entry->rseg[0],8*sizeof(unsigned short));
 				trh->rcf^=htons(TR_RCF_DIR_BIT);	
@@ -151,40 +229,58 @@
 				entry->last_used=jiffies;
 			}
 		}
-		else {
+		else 
+		{
+			/*
+			 *	Without the information we simply have to shout
+			 *	on the wire. The replies should rapidly clean this
+			 *	situation up.
+			 */
 			trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK)  
 				       | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
 			trh->saddr[0]|=TR_RII;
 		}
 	}
-			
 }
 
-static void tr_add_rif_info(struct trh_hdr *trh) {
-
+/*
+ *	We have learned some new RIF information for our source
+ *	routing.
+ */
+ 
+static void tr_add_rif_info(struct trh_hdr *trh) 
+{
 	int i;
 	unsigned int hash;
 	rif_cache entry;
 
-
+	/*
+	 *	Firstly see if the entry exists
+	 */
 	trh->saddr[0]&=0x7f;
 	for(i=0,hash=0;i<TR_ALEN;hash+=trh->saddr[i++]);
 	hash&=RIF_TABLE_SIZE-1;
-#if 0
-	printk("hash: %d\n",hash);
-#endif
 	for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);entry=entry->next);
 
-	if(entry==NULL) {
+	if(entry==NULL) 
+	{
 #if 0
 printk("adding rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n",
 		trh->saddr[0],trh->saddr[1],trh->saddr[2],
        		trh->saddr[3],trh->saddr[4],trh->saddr[5],
 		trh->rcf);
 #endif
+		/*
+		 *	Allocate our new entry. A failure to allocate loses
+		 *	use the information. This is harmless.
+		 *
+		 *	FIXME: We ought to keep some kind of cache size
+		 *	limiting and adjust the timers to suit.
+		 */
 		entry=kmalloc(sizeof(struct rif_cache_s),GFP_ATOMIC);
-		if(!entry) {
-			printk("tr.c: Couldn't malloc rif cache entry !\n");
+		if(!entry) 
+		{
+			printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n");
 			return;
 		}
 		entry->rcf=trh->rcf;
@@ -193,69 +289,92 @@
 		entry->next=rif_table[hash];
 		entry->last_used=jiffies;
 		rif_table[hash]=entry;
-	}
-/* Y. Tahara added */
-   else {                                       
-		if ( entry->rcf != trh->rcf ) {               
-				if (!(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) {
+	} 	
+	else	/* Y. Tahara added */
+	{ 
+		/*
+		 *	Update existing entries
+		 */
+		if ( entry->rcf != trh->rcf ) 
+		{ 
+			if (!(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) 
+			{
 #if 0
 printk("updating rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n",
 		trh->saddr[0],trh->saddr[1],trh->saddr[2],
 		trh->saddr[3],trh->saddr[4],trh->saddr[5],
 		trh->rcf);
 #endif
-     		       entry->rcf = trh->rcf;                  
-        		    memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
-           		 entry->last_used=jiffies;               
-				}                                          
+				entry->rcf = trh->rcf;                  
+				memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
+				entry->last_used=jiffies;               
+			}                                          
 		}                                             
 	}
-
 }
 
-static void rif_check_expire(unsigned long dummy) {
+/*
+ *	Scan the cache with a timer and see what we need to throw out.
+ */
 
+static void rif_check_expire(unsigned long dummy) 
+{
 	int i;
 	unsigned long now=jiffies,flags;
 
 	save_flags(flags);
 	cli();
 
-	for(i=0; i < RIF_TABLE_SIZE;i++) {
-
-	rif_cache entry, *pentry=rif_table+i;	
-
+	for(i=0; i < RIF_TABLE_SIZE;i++) 
+	{
+		rif_cache entry, *pentry=rif_table+i;	
 		while((entry=*pentry)) 
-			if((now-entry->last_used) > RIF_TIMEOUT) {
+		{
+			/*
+			 *	Out it goes
+			 */
+			if((now-entry->last_used) > RIF_TIMEOUT) 
+			{
 				*pentry=entry->next;
 				kfree_s(entry,sizeof(struct rif_cache_s));
 			}
 			else
-				pentry=&entry->next;	
+				pentry=&entry->next;
+		}
 	}
 	restore_flags(flags);
 
+	/*
+	 *	Reset the timer
+	 */
+	 
 	del_timer(&rif_timer);
 	rif_timer.expires=jiffies+RIF_CHECK_INTERVAL;
 	add_timer(&rif_timer);
 
 }
 
-int rif_get_info(char *buffer,char **start, off_t offset, int length) {
-
-   int len=0;
-   off_t begin=0;
-   off_t pos=0;
-   int size,i;
+/*
+ *	Generate the /proc/net information for the token ring RIF
+ *	routing.
+ */
+ 
+int rif_get_info(char *buffer,char **start, off_t offset, int length) 
+{
+	int len=0;
+	off_t begin=0;
+	off_t pos=0;
+	int size,i;
 
-   rif_cache entry;
+	rif_cache entry;
 
 	size=sprintf(buffer,
-"   TR address     rcf             routing segments             TTL\n\n");
-   pos+=size;
-   len+=size;
+		"   TR address     rcf             routing segments             TTL\n\n");
+	pos+=size;
+	len+=size;
 
-	for(i=0;i < RIF_TABLE_SIZE;i++) {
+	for(i=0;i < RIF_TABLE_SIZE;i++) 
+	{
 		for(entry=rif_table[i];entry;entry=entry->next) {
 			size=sprintf(buffer+len,"%02X:%02X:%02X:%02X:%02X:%02X %04X %04X %04X %04X %04X %04X %04X %04X %04X %lu\n",
 								entry->addr[0],entry->addr[1],entry->addr[2],entry->addr[3],entry->addr[4],entry->addr[5],
@@ -264,27 +383,33 @@
 			len+=size;
 			pos=begin+len;
 
-			if(pos<offset) {
+			if(pos<offset) 
+			{
 				len=0;
 				begin=pos;
 			}
 			if(pos>offset+length)
 				break;
-   	}
+	   	}
 		if(pos>offset+length)
 			break;
 	}
 
-   *start=buffer+(offset-begin); /* Start of wanted data */
-   len-=(offset-begin);    /* Start slop */
-   if(len>length)
-      len=length;    /* Ending slop */
-   return len;
+	*start=buffer+(offset-begin); /* Start of wanted data */
+	len-=(offset-begin);    /* Start slop */
+	if(len>length)
+		len=length;    /* Ending slop */
+	return len;
 }
 
-void rif_init(struct net_proto *unused) {
-
+/*
+ *	Called during bootup.  We don't actually have to initialise
+ *	too much for this. The timer structure is setup statically. Thats
+ *	probably NOT a good thing if we change the structure.
+ */
+ 
+void rif_init(struct net_proto *unused) 
+{
 	add_timer(&rif_timer);
-
 }
 
diff -u --recursive --new-file v2.1.19/linux/net/Config.in linux/net/Config.in
--- v2.1.19/linux/net/Config.in	Wed Dec 18 15:59:14 1996
+++ linux/net/Config.in	Thu Jan  2 15:13:27 1997
@@ -33,6 +33,7 @@
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+  tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
   bool '802.2 LLC (VERY EXPERIMENTAL)' CONFIG_LLC
 fi
diff -u --recursive --new-file v2.1.19/linux/net/Makefile linux/net/Makefile
--- v2.1.19/linux/net/Makefile	Wed Dec 18 15:59:14 1996
+++ linux/net/Makefile	Thu Jan  2 15:13:27 1997
@@ -9,7 +9,7 @@
 
 MOD_SUB_DIRS := ipv4
 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
-		netrom rose x25 #decnet
+		netrom rose lapb x25 #decnet
 SUB_DIRS     := core ethernet unix
 MOD_LIST_NAME := NET_MISC_MODULES
 
@@ -54,6 +54,14 @@
 else
   ifeq ($(CONFIG_X25),m)
   MOD_SUB_DIRS += x25
+  endif
+endif
+
+ifeq ($(CONFIG_LAPB),y)
+SUB_DIRS += lapb
+else
+  ifeq ($(CONFIG_LAPB),m)
+  MOD_SUB_DIRS += lapb
   endif
 endif
 
diff -u --recursive --new-file v2.1.19/linux/net/README linux/net/README
--- v2.1.19/linux/net/README	Sun Dec 22 16:37:42 1996
+++ linux/net/README	Thu Jan  2 15:13:27 1997
@@ -11,6 +11,7 @@
 ethernet		alan@lxorguk.ukuu.org.uk
 ipv4			alan@lxorguk.ukuu.org.uk
 ipx			alan@lxorguk.ukuu.org.uk,greg@caldera.com
+lapb			jsn@cs.nott.ac.uk
 netrom			jsn@cs.nott.ac.uk
 rose			jsn@cs.nott.ac.uk
 unix			alan@lxorguk.ukuu.org.uk
diff -u --recursive --new-file v2.1.19/linux/net/appletalk/aarp.c linux/net/appletalk/aarp.c
--- v2.1.19/linux/net/appletalk/aarp.c	Thu Dec 12 19:37:21 1996
+++ linux/net/appletalk/aarp.c	Thu Jan  2 15:13:27 1997
@@ -3,15 +3,15 @@
  *			ethernet 'ELAP'.
  *
  *		Alan Cox  <Alan.Cox@linux.org>
- *			  <alan@cymru.net>
  *
- *	This doesn't fit cleanly with the IP arp. This isn't a problem as
- *	the IP arp wants extracting from the device layer in 1.3.x anyway.
- *	[see the pre-1.3 test code for details 8)]
+ *	This doesn't fit cleanly with the IP arp. Potentially we can use
+ *	the generic neighbour discovery code to clean this up.
  *
  *	FIXME:
  *		We ought to handle the retransmits with a single list and a 
  *	separate fast timer for when it is needed.
+ *		Use neighbour discovery code.
+ *		Token Ring Support.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.19/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.19/linux/net/appletalk/ddp.c	Mon Dec 30 15:39:16 1996
+++ linux/net/appletalk/ddp.c	Thu Jan  2 15:13:28 1997
@@ -3,7 +3,6 @@
  *		ethernet 'ELAP'.
  *
  *		Alan Cox  <Alan.Cox@linux.org>
- *			  <iialan@www.linux.org.uk>
  *
  *		With more than a little assistance from
  *
@@ -30,8 +29,6 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- *	TODO
- *		ASYNC I/O
  */
 
 #include <linux/config.h>
@@ -88,7 +85,7 @@
 *															*
 \***********************************************************************************************************************/
 
-static struct sock *volatile atalk_socket_list=NULL;
+static struct sock *atalk_socket_list=NULL;
 
 /*
  *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
@@ -96,42 +93,14 @@
  *	use this facility.
  */
 
-static void atalk_remove_socket(struct sock *sk)
+extern inline void atalk_remove_socket(struct sock *sk)
 {
-	unsigned long flags;
-	struct sock *s;
-
-	save_flags(flags);
-	cli();
-
-	s=atalk_socket_list;
-	if(s==sk)
-	{
-		atalk_socket_list=s->next;
-		restore_flags(flags);
-		return;
-	}
-	while(s && s->next)
-	{
-		if(s->next==sk)
-		{
-			s->next=sk->next;
-			restore_flags(flags);
-			return;
-		}
-		s=s->next;
-	}
-	restore_flags(flags);
+	sklist_remove_socket(&atalk_socket_list,sk);
 }
 
-static void atalk_insert_socket(struct sock *sk)
+extern inline void atalk_insert_socket(struct sock *sk)
 {
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	sk->next=atalk_socket_list;
-	atalk_socket_list=sk;
-	restore_flags(flags);
+	sklist_insert_socket(&atalk_socket_list,sk);
 }
 
 static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif)
@@ -191,53 +160,11 @@
 	return( s );
 }
 
-/*
- *	This is only called from user mode. Thus it protects itself against
- *	interrupt users but doesn't worry about being called during work.
- *	Once it is removed from the queue no interrupt or bottom half will
- *	touch it and we are (fairly 8-) ) safe.
- */
-
-static void atalk_destroy_socket(struct sock *sk);
-
-/*
- *	Handler for deferred kills.
- */
-
-static void atalk_destroy_timer(unsigned long data)
+extern inline void atalk_destroy_socket(struct sock *sk)
 {
-	atalk_destroy_socket((struct sock *)data);
+	sklist_destroy_socket(&atalk_socket_list,sk);
 }
 
-static void atalk_destroy_socket(struct sock *sk)
-{
-	struct sk_buff *skb;
-	atalk_remove_socket(sk);
-
-	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
-	{
-		kfree_skb(skb,FREE_READ);
-	}
-
-	if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead)
-	{
-		sk_free(sk);
-		MOD_DEC_USE_COUNT;
-	}
-	else
-	{
-		/*
-		 *	Someone is using our buffers still.. defer
-		 */
-		init_timer(&sk->timer);
-		sk->timer.expires=jiffies+10*HZ;
-		sk->timer.function=atalk_destroy_timer;
-		sk->timer.data = (unsigned long)sk;
-		add_timer(&sk->timer);
-	}
-}
-
-
 /*
  *	Called from proc fs
  */
@@ -364,9 +291,8 @@
 
 /*
  *	THIS IS A HACK: Farallon cards want to do their own picking of
- *	addresses. This needs tidying up post 1.4, but we need it in
- *	now for the 1.4 release as is.
- *
+ *	addresses. This needs tidying up when someone does localtalk
+ *	drivers
  */
 	if((atif->dev->type == ARPHRD_LOCALTLK || atif->dev->type == ARPHRD_PPP)
 		&& atif->dev->do_ioctl)
@@ -1105,7 +1031,16 @@
 	if(!sk->dead)
 	{
 		wake_up_interruptible(sk->sleep);
-		sock_wake_async(sk->socket,0);
+		sock_wake_async(sk->socket,1);
+	}
+}
+
+static void def_callback3(struct sock *sk)
+{
+	if(!sk->dead)
+	{
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket, 2);
 	}
 }
 
@@ -1159,7 +1094,7 @@
 
 	sk->state_change=def_callback1;
 	sk->data_ready=def_callback2;
-	sk->write_space=def_callback1;
+	sk->write_space=def_callback3;
 	sk->error_report=def_callback1;
 
 	sk->zapped=1;
diff -u --recursive --new-file v2.1.19/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.19/linux/net/ax25/af_ax25.c	Mon Dec 30 15:39:16 1996
+++ linux/net/ax25/af_ax25.c	Thu Jan  2 15:13:28 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 034
+ *	AX.25 release 035
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
  *	releases, misbehave and/or generally screw up. It might even work.
@@ -88,6 +88,8 @@
  *			Alan(GW4PTS)		Small POSIXisations
  *	AX.25 035	Alan(GW4PTS)		Started fixing to the new
  *						format.
+ *			Hans(PE1AYX)		Fixed interface to IP layer.
+ *			Alan(GW4PTS)		Added asynchronous support.
  *
  *	To do:
  *		Restructure the ax25_rcv code to be cleaner/faster and
@@ -729,6 +731,8 @@
 
 	MOD_INC_USE_COUNT;
 
+	memset(ax25, 0x00, sizeof(*ax25));
+
 	skb_queue_head_init(&ax25->write_queue);
 	skb_queue_head_init(&ax25->frag_queue);
 	skb_queue_head_init(&ax25->ack_queue);
@@ -736,8 +740,6 @@
 
 	init_timer(&ax25->timer);
 
-	ax25->dama_slave = 0;
-
 	ax25->rtt     = AX25_DEF_T1 / 2;
 	ax25->t1      = AX25_DEF_T1;
 	ax25->t2      = AX25_DEF_T2;
@@ -755,29 +757,8 @@
 		ax25->window  = AX25_DEF_WINDOW;
 	}
 
-	ax25->fragno    = 0;
-	ax25->fraglen   = 0;
-	ax25->hdrincl   = 0;
-	ax25->backoff   = AX25_DEF_BACKOFF;
-	ax25->condition = 0x00;
-	ax25->t1timer   = 0;
-	ax25->t2timer   = 0;
-	ax25->t3timer   = 0;
-	ax25->n2count   = 0;
-	ax25->idletimer = 0;
-
-	ax25->va      = 0;
-	ax25->vr      = 0;
-	ax25->vs      = 0;
-
-	ax25->device   = NULL;
-	ax25->digipeat = NULL;
-	ax25->sk       = NULL;
-
-	ax25->state    = AX25_STATE_0;
-
-	memset(&ax25->dest_addr,   '\0', AX25_ADDR_LEN);
-	memset(&ax25->source_addr, '\0', AX25_ADDR_LEN);
+	ax25->backoff = AX25_DEF_BACKOFF;
+	ax25->state   = AX25_STATE_0;
 
 	return ax25;
 }
@@ -820,8 +801,6 @@
 	ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_MAXQUEUE);
 	ax25->idle     = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
 
-	ax25->dama_slave = 0;
-
 	if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
 		ax25->modulus = EMODULUS;
 		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
@@ -1116,7 +1095,19 @@
 static void def_callback2(struct sock *sk, int len)
 {
 	if (!sk->dead)
+	{
 		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket,1);
+	}
+}
+
+static void def_callback3(struct sock *sk, int len)
+{
+	if (!sk->dead)
+	{
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket,2);
+	}
 }
 
 static int ax25_create(struct socket *sock, int protocol)
@@ -1185,7 +1176,7 @@
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
-	sk->write_space  = def_callback1;
+	sk->write_space  = def_callback3;
 	sk->error_report = def_callback1;
 
 	if (sock != NULL) {
@@ -1245,7 +1236,7 @@
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
-	sk->write_space  = def_callback1;
+	sk->write_space  = def_callback3;
 	sk->error_report = def_callback1;
 
 	ax25->modulus = osk->protinfo.ax25->modulus;
@@ -1367,6 +1358,8 @@
  *	digipeated via a local address as source. This is a hack until we add
  *	BSD 4.4 ADDIFADDR type support. It is however small and trivially backward
  *	compatible 8)
+ *
+ *	FIXME: Check family
  */
 static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
@@ -1430,6 +1423,11 @@
 	return 0;
 }
 
+/*
+ *	FIXME: nonblock behaviour looks like it may have a bug. Also check
+ *	the family in the connect.
+ */
+ 
 static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
 	int addr_len, int flags)
 {
@@ -1687,8 +1685,7 @@
 		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) {
 			struct device *dev_out = dev;
 
-			skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ);
-			if(skb==NULL)
+			if ((skb = skb_unshare(skb, GFP_ATOMIC, FREE_READ)) == NULL)
 				return 0;
 
 			/* We are the digipeater. Mark ourselves as repeated
@@ -1723,7 +1720,6 @@
 				return 0;
 			}
 #endif
-
 			skb->arp = 1;
 			skb->dev = dev_out;
 			skb->priority = SOPRI_NORMAL;
@@ -1764,11 +1760,19 @@
 #ifdef CONFIG_INET
 			case AX25_P_IP:
 				skb_pull(skb,2);		/* drop PID/CTRL */
+				skb->h.raw    = skb->data;
+				skb->nh.raw   = skb->data;
+				skb->dev      = dev;
+				skb->pkt_type = PACKET_HOST;
 				ip_rcv(skb, dev, ptype);	/* Note ptype here is the wrong one, fix me later */
 				break;
 
 			case AX25_P_ARP:
 				skb_pull(skb,2);
+				skb->h.raw    = skb->data;
+				skb->nh.raw   = skb->data;
+				skb->dev      = dev;
+				skb->pkt_type = PACKET_HOST;
 				arp_rcv(skb, dev, ptype);	/* Note ptype here is wrong... */
 				break;
 #endif
@@ -2115,7 +2119,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	if (sk->protinfo.ax25->hdrincl) {
@@ -2395,6 +2399,7 @@
 EXPORT_SYMBOL(ax2asc);
 EXPORT_SYMBOL(asc2ax);
 EXPORT_SYMBOL(null_ax25_address);
+#endif
 
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_ax25_route = {
@@ -2431,7 +2436,7 @@
 	proc_net_register(&proc_ax25_calls);
 #endif
 
-	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.34 for Linux NET3.037 (Linux 2.1)\n");
+	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.35 for Linux NET3.038 (Linux 2.1)\n");
 }
 
 /*
@@ -2605,7 +2610,7 @@
 	ax25_packet_type.type = htons(ETH_P_AX25);
 	dev_remove_pack(&ax25_packet_type);
 
-	sock_unregister(ax25_proto_ops.family);
+	sock_unregister(AF_AX25);
 }
 #endif
 
diff -u --recursive --new-file v2.1.19/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v2.1.19/linux/net/ax25/ax25_in.c	Thu Dec 12 19:37:22 1996
+++ linux/net/ax25/ax25_in.c	Thu Jan  2 15:13:28 1997
@@ -1,10 +1,10 @@
 /*
- *	AX.25 release 033
+ *	AX.25 release 035
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -35,6 +35,7 @@
  *	AX.25 032	Darryl(G7LED)	AX.25 segmentation fixed.
  *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
  *					Modularisation changes.
+ *	AX.25 035	Hans(PE1AYX)	Fixed interface to IP layer.
  */
 
 #include <linux/config.h>
@@ -161,7 +162,10 @@
 #ifdef CONFIG_INET
 	if (pid == AX25_P_IP) {
 		skb_pull(skb, 1);	/* Remove PID */
-		skb->h.raw = skb->data;
+		skb->h.raw    = skb->data;
+		skb->nh.raw   = skb->data;
+		skb->dev      = ax25->device;
+		skb->pkt_type = PACKET_HOST;
 		ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
 		return 1;
 	}
diff -u --recursive --new-file v2.1.19/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.19/linux/net/ax25/ax25_out.c	Wed Dec 18 15:59:14 1996
+++ linux/net/ax25/ax25_out.c	Thu Jan  2 15:13:28 1997
@@ -1,10 +1,10 @@
 /*
- *	AX.25 release 033
+ *	AX.25 release 035
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v2.1.19/linux/net/ax25/ax25_route.c	Fri Nov 22 18:28:23 1996
+++ linux/net/ax25/ax25_route.c	Thu Jan  2 15:13:28 1997
@@ -1,10 +1,10 @@
 /*
- *	AX.25 release 033
+ *	AX.25 release 035
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v2.1.19/linux/net/ax25/ax25_subr.c	Wed Dec 18 15:59:14 1996
+++ linux/net/ax25/ax25_subr.c	Thu Jan  2 15:13:28 1997
@@ -1,10 +1,10 @@
 /*
- *	AX.25 release 033
+ *	AX.25 release 035
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.3.61 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v2.1.19/linux/net/ax25/ax25_timer.c	Thu Dec 12 17:02:47 1996
+++ linux/net/ax25/ax25_timer.c	Thu Jan  2 15:13:28 1997
@@ -1,10 +1,10 @@
 /*
- *	AX.25 release 033
+ *	AX.25 release 035
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.1.19/linux/net/core/datagram.c	Thu Dec 12 19:37:22 1996
+++ linux/net/core/datagram.c	Thu Jan  2 15:13:28 1997
@@ -188,8 +188,9 @@
 }
 
 /*
- *	Datagram select: Again totally generic. Moved from udp.c
- *	Now does seqpacket.
+ *	Datagram select: Again totally generic. This also handles
+ *	sequenced packet sockets providing the socket receive queue
+ *	is only ever holding data ready to receive.
  */
 
 int datagram_select(struct socket *sock, int sel_type, select_table *wait)
diff -u --recursive --new-file v2.1.19/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.19/linux/net/core/dev.c	Sun Dec 22 16:37:43 1996
+++ linux/net/core/dev.c	Thu Jan  2 15:13:28 1997
@@ -78,9 +78,7 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <net/br.h>
-#ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
-#endif
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
@@ -91,14 +89,30 @@
 /*
  *	The list of devices, that are able to output.
  */
+
 static struct device *dev_up_base;
 
 /*
  *	The list of packet types we will receive (as opposed to discard)
  *	and the routines to invoke.
+ *
+ *	Why 16. Because with 16 the only overlap we get on a hash of the
+ *	low nibble of the protocol value is RARP/SNAP/X.25. 
+ *
+ *		0800	IP
+ *		0001	802.3
+ *		0002	AX.25
+ *		0004	802.2
+ *		8035	RARP
+ *		0005	SNAP
+ *		0805	X.25
+ *		0806	ARP
+ *		8137	IPX
+ *		0009	Localtalk
+ *		86DD	IPv6
  */
 
-struct packet_type *ptype_base[16];
+struct packet_type *ptype_base[16];		/* 16 way hashed list */
 struct packet_type *ptype_all = NULL;		/* Taps */
 
 /*
@@ -243,6 +257,7 @@
 	/*
 	 *	Call device private open method
 	 */
+	 
 	if (dev->open) 
   		ret = dev->open(dev);
 
@@ -259,11 +274,12 @@
 		 */
 		dev_mc_upload(dev);
 		notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
-#ifdef CONFIG_NET_ALIAS
-		if (!net_alias_is(dev) || dev->tx_queue_len)
-#else
-		if (dev->tx_queue_len)
-#endif
+		/* 
+		 *	FIXME: This logic was wrong before. Now its
+		 *	obviously so. I think the change here (removing the
+		 *	! on the net_alias_is) is right. ANK ??
+		 */
+		if (net_alias_is(dev) || dev->tx_queue_len)
 		{
 			cli();
 			dev->next_up = dev_up_base;
@@ -301,6 +317,7 @@
 	/*
 	 *	Tell people we are going down
 	 */
+	 
 	notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
 	/*
 	 *	Flush the multicast chain
@@ -318,6 +335,10 @@
 		ct++;
 	}
 
+	/*
+	 *	The device is no longer up. Drop it from the list.
+	 */
+	 
 	devp = &dev_up_base;
 	while (*devp)
 	{
@@ -348,6 +369,34 @@
 }
 
 /*
+ *	Support routine. Sends outgoing frames to any network
+ *	taps currently in use.
+ */
+
+static void queue_xmit_nit(struct sk_buff *skb, struct device *dev)
+{
+	struct packet_type *ptype;
+	get_fast_time(&skb->stamp);
+
+	for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) 
+	{
+		/* Never send packets back to the socket
+		 * they originated from - MvS (miquels@drinkel.ow.org)
+		 */
+		if ((ptype->dev == dev || !ptype->dev) &&
+			((struct sock *)ptype->data != skb->sk))
+		{
+			struct sk_buff *skb2;
+			if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+				break;
+			skb2->mac.raw = skb2->data;
+			skb2->nh.raw = skb2->h.raw = skb2->data + dev->hard_header_len;
+			ptype->func(skb2, skb->dev, ptype);
+		}
+	}
+}
+ 
+/*
  *	Send (or queue for sending) a packet. 
  *
  *	IMPORTANT: When this is called to resend frames. The caller MUST
@@ -403,41 +452,30 @@
 	list = dev->buffs + pri;
 
 	save_flags(flags);
-	/* if this isn't a retransmission, use the first packet instead... */
-	if (!retransmission) {
-		if (skb_queue_len(list)) {
-			/* avoid overrunning the device queue.. */
-			if (skb_queue_len(list) > dev->tx_queue_len) {
-				dev_kfree_skb(skb, FREE_WRITE);
-				return;
-			}
-		}
 
-		/* copy outgoing packets to any sniffer packet handlers */
-		if (dev_nit) {
-			struct packet_type *ptype;
-
-			get_fast_time(&skb->stamp);
+	/*
+	 *	If this isn't a retransmission, use the first packet instead.
+	 *	Note: We don't do strict priority ordering here. We will in
+	 *	fact kick the queue that is our priority. The dev_tint reload
+	 *	does strict priority queueing. In effect what we are doing here
+	 *	is to add some random jitter to the queues and to do so by
+	 *	saving clocks. Doing a perfect priority queue isn't a good idea
+	 *	as you get some fascinating timing interactions.
+	 */
 
-			for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) 
-			{
-				/* Never send packets back to the socket
-				 * they originated from - MvS (miquels@drinkel.ow.org)
-				 */
-				if ((ptype->dev == dev || !ptype->dev) &&
-				   ((struct sock *)ptype->data != skb->sk))
-				{
-					struct sk_buff *skb2;
-					if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
-						break;
-					skb2->mac.raw = skb2->data;
-					skb2->nh.raw =
-					skb2->h.raw = skb2->data + dev->hard_header_len;
-					ptype->func(skb2, skb->dev, ptype);
-				}
-			}
+	if (!retransmission) 
+	{
+		/* avoid overrunning the device queue.. */
+		if (skb_queue_len(list) > dev->tx_queue_len) 
+		{
+			dev_kfree_skb(skb, FREE_WRITE);
+			return;
 		}
 
+		/* copy outgoing packets to any sniffer packet handlers */
+		if (dev_nit) 
+			queue_xmit_nit(skb,dev);
+
 		if (skb_queue_len(list)) {
 			cli();
 			__skb_queue_tail(list, skb);
@@ -461,13 +499,16 @@
 	restore_flags(flags);
 }
 
+/*
+ *	Entry point for transmitting frames.
+ */
+ 
 int dev_queue_xmit(struct sk_buff *skb)
 {
 	struct device *dev = skb->dev;
 
 	start_bh_atomic();
 
-
 #if CONFIG_SKB_CHECK 
 	IS_SKB(skb);
 #endif    
@@ -477,13 +518,23 @@
 	 *	This can cover all protocols and technically not just ARP either.
 	 */
 	 
-	if (!skb->arp) {
-		if (dev->rebuild_header) {
-			if (dev->rebuild_header(skb)) {
+	if (!skb->arp) 
+	{
+		/*
+		 *	FIXME: we should make the printk for no rebuild
+		 *	header a default rebuild_header routine and drop
+		 *	this call. Similarly we should make hard_header
+		 *	have a default NULL operation not check conditions.
+		 */
+		if (dev->rebuild_header) 
+		{
+			if (dev->rebuild_header(skb)) 
+			{
 				end_bh_atomic();
 				return 0;
 			}
-		} else
+		} 
+		else
 			printk("%s: !skb->arp & !rebuild_header!\n", dev->name);
 	}
 
@@ -495,16 +546,18 @@
 	 *
 	 */
 
-#ifdef CONFIG_NET_ALIAS
 	if (net_alias_is(dev))
 	  	skb->dev = dev = net_alias_main_dev(dev);
-#endif
 
 	do_dev_queue_xmit(skb, dev, skb->priority);
 	end_bh_atomic();
 	return 0;
 }
 
+/*
+ *	Fast path for loopback frames.
+ */
+ 
 void dev_loopback_xmit(struct sk_buff *skb)
 {
 	struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
@@ -521,8 +574,7 @@
 
 /*
  *	Receive a packet from a device driver and queue it for the upper
- *	(protocol) levels.  It always succeeds. This is the recommended 
- *	interface to use.
+ *	(protocol) levels.  It always succeeds. 
  */
 
 void netif_rx(struct sk_buff *skb)
@@ -582,7 +634,8 @@
 
 	for (dev = dev_up_base; dev != NULL; dev = dev->next_up)
 	{
-		if (dev->flags != 0 && !dev->tbusy) {
+		if (dev->flags != 0 && !dev->tbusy) 
+		{
 			/*
 			 *	Kick the device
 			 */
@@ -636,7 +689,8 @@
 	 *	disabling interrupts.
 	 */
 
-	while (!skb_queue_empty(&backlog)) {
+	while (!skb_queue_empty(&backlog)) 
+	{
 		struct sk_buff * skb = backlog.next;
 
 		/*
@@ -658,8 +712,6 @@
 			continue;
 		}
 		
-		
-
 #ifdef CONFIG_BRIDGE
 
 		/*
@@ -783,9 +835,7 @@
 	 *	One last output flush.
 	 */
 
-#ifdef XMIT_AFTER	 
 	dev_transmit();
-#endif
 }
 
 
@@ -804,12 +854,11 @@
 	 * aliases do not transmit (for now :) )
 	 */
 
-#ifdef CONFIG_NET_ALIAS
 	if (net_alias_is(dev)) {
 		printk("net alias %s transmits\n", dev->name);
 		return;
 	}
-#endif
+
 	head = dev->buffs;
 	save_flags(flags);
 	cli();
@@ -848,7 +897,7 @@
 
 /*
  *	Perform a SIOCGIFCONF call. This structure will change
- *	size shortly, and there is nothing I can do about it.
+ *	size eventually, and there is nothing I can do about it.
  *	Thus we will need a 'compatibility mode'.
  */
 
@@ -1426,6 +1475,7 @@
 extern int pt_init(void);
 extern int sm_init(void);
 extern int baycom_init(void);
+extern int lapbeth_init(void);
 
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_net_dev = {
@@ -1496,6 +1546,9 @@
 #endif
 #if defined(CONFIG_SOUNDMODEM)
 	sm_init();
+#endif
+#if defined(CONFIG_LAPBETHER)
+	lapbeth_init();
 #endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
diff -u --recursive --new-file v2.1.19/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v2.1.19/linux/net/core/dev_mcast.c	Thu Dec 12 19:37:22 1996
+++ linux/net/core/dev_mcast.c	Thu Jan  2 15:26:27 1997
@@ -23,7 +23,6 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -43,19 +42,17 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/arp.h>
-#ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
-#endif
 
 
 /*
- *	Device multicast list maintenance. This knows about such little matters as promiscuous mode and
- *	converting from the list to the array the drivers use. At least until I fix the drivers up.
+ *	Device multicast list maintenance. 
  *
- *	This is used both by IP and by the user level maintenance functions. Unlike BSD we maintain a usage count
- *	on a given multicast address so that a casual user application can add/delete multicasts used by protocols
- *	without doing damage to the protocols when it deletes the entries. It also helps IP as it tracks overlapping
- *	maps.
+ *	This is used both by IP and by the user level maintenance functions. 
+ *	Unlike BSD we maintain a usage count on a given multicast address so 
+ *	that a casual user application can add/delete multicasts used by 
+ *	protocols without doing damage to the protocols when it deletes the
+ *	entries. It also helps IP as it tracks overlapping maps.
  */
  
 
@@ -72,9 +69,13 @@
 	if(!(dev->flags&IFF_UP))
 		return;
 
-#ifdef CONFIG_NET_ALIAS
+	/*
+	 *	An aliased device should end up with the combined
+	 *	multicast list of all its aliases. 
+	 *	[Check this is still ok -AC]
+	 */
+	 
 	dev = net_alias_main_dev(dev);
-#endif
 		
 	/*
 	 *	Devices with no set multicast don't get set 
@@ -93,19 +94,29 @@
 void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
 {
 	struct dev_mc_list **dmi;
-#ifdef CONFIG_NET_ALIAS
 	dev = net_alias_main_dev(dev);
-#endif
+
 	for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
 	{
+		/*
+		 *	Find the entry we want to delete. The device could
+		 *	have variable length entries so check these too.
+		 */
 		if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
 		{
 			struct dev_mc_list *tmp= *dmi;
 			if(--(*dmi)->dmi_users && !all)
 				return;
+			/*
+			 *	Last user. So delete the entry.
+			 */
 			*dmi=(*dmi)->next;
 			dev->mc_count--;
 			kfree_s(tmp,sizeof(*tmp));
+			/*
+			 *	We have altered the list, so the card
+			 *	loaded filter is now wrong. Fix it
+			 */
 			dev_mc_upload(dev);
 			return;
 		}
@@ -119,9 +130,9 @@
 void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
 {
 	struct dev_mc_list *dmi;
-#ifdef CONFIG_NET_ALIAS
+
 	dev = net_alias_main_dev(dev);
-#endif
+
 	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
 	{
 		if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
@@ -149,10 +160,8 @@
 
 void dev_mc_discard(struct device *dev)
 {
-#ifdef CONFIG_NET_ALIAS
 	if (net_alias_is(dev))
 		return;
-#endif
 	while(dev->mc_list!=NULL)
 	{
 		struct dev_mc_list *tmp=dev->mc_list;
diff -u --recursive --new-file v2.1.19/linux/net/core/firewall.c linux/net/core/firewall.c
--- v2.1.19/linux/net/core/firewall.c	Mon Dec 30 15:39:16 1996
+++ linux/net/core/firewall.c	Thu Jan  2 15:13:28 1997
@@ -29,6 +29,10 @@
 	 *	Don't allow two people to adjust at once.
 	 */
 
+	/*
+	 *	FIXME: Swap for a kernel semaphore object
+	 */
+	 
 	while(firewall_lock)
 		schedule();
 	firewall_lock=1;
diff -u --recursive --new-file v2.1.19/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.19/linux/net/core/neighbour.c	Thu Dec 12 19:37:24 1996
+++ linux/net/core/neighbour.c	Thu Jan  2 14:07:39 1997
@@ -137,17 +137,17 @@
 	if (neigh)
 	{
 		do {
-			if (memcmp(&neigh->primary_key, pkey, key_len) == 0)
+			if (memcmp(neigh->primary_key, pkey, key_len) == 0)
 			{
 				if (!dev || dev == neigh->dev)
-					break;
+					return neigh;
 			}
 			neigh = neigh->next;
 			
 		} while (neigh != head);
 	}
 
-	return neigh;
+	return NULL;
 }
 
 /*
diff -u --recursive --new-file v2.1.19/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v2.1.19/linux/net/core/net_alias.c	Thu Dec 12 19:37:24 1996
+++ linux/net/core/net_alias.c	Thu Jan  2 15:13:28 1997
@@ -51,7 +51,7 @@
 #endif
 
 /*
- * Only allow the following flags to pass from main device to aliases
+ *	Only allow the following flags to pass from main device to aliases
  */
 
 #define  NET_ALIAS_IFF_MASK   (IFF_UP|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MULTICAST)
@@ -61,7 +61,6 @@
 static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa);
 static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias);
 
-
 static int net_alias_devinit(struct device *dev);
 static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev);
 static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa);
@@ -71,54 +70,53 @@
 static void net_alias_free(struct device *dev);
 					   
 /*
- * net_alias_type base array, will hold net_alias_type obj hashed list heads.
+ *	net_alias_type base array, will hold net_alias_type obj hashed list 
+ *	heads.
  */
 
 struct net_alias_type *nat_base[16];
 
 
 /*
- * get net_alias_type ptr by type
+ *	Get net_alias_type ptr by type
  */
 
-static __inline__ struct net_alias_type *
-nat_getbytype(int type)
+extern __inline__ struct net_alias_type *nat_getbytype(int type)
 {
-  struct net_alias_type *nat;
-  for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
-  {
-    if (nat->type == type) return nat;
-  }
-  return NULL;
+	struct net_alias_type *nat;
+	for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
+	{
+		if (nat->type == type) 
+			return nat;
+	}
+	return NULL;
 }
 
 
 /*
- * get addr32 representation (pre-hashing) of address.
- * if NULL nat->get_addr32, assume sockaddr_in struct (IP-ish).
+ *	Get addr32 representation (pre-hashing) of address.
+ *	If NULL nat->get_addr32, assume sockaddr_in struct (IP-ish).
  */
 
-static __inline__ __u32
-nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
+extern __inline__ __u32 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
 {
-  if (nat->get_addr32)
-    return nat->get_addr32(nat, sa);
-  else
-    return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
+	if (nat->get_addr32)
+		return nat->get_addr32(nat, sa);
+	else
+		return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
 }
 
 
 /*
- * hashing code for alias_info->hash_tab entries
- * 4 bytes -> 1/2 byte using xor complemented by af
+ *	Hashing code for alias_info->hash_tab entries
+ *	4 bytes -> 1/2 byte using xor complemented by af
  */
 
-static __inline__ unsigned
-HASH(__u32 addr, int af)
+extern __inline__ unsigned HASH(__u32 addr, int af)
 {
-  unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
-  tmp ^= (tmp>>8);                  /* 2 -> 1 */
-  return (tmp^(tmp>>4)^af) & 0x0f;	    /* 1 -> 1/2 */
+	unsigned tmp = addr ^ (addr>>16); 	/* 4 -> 2 */
+	tmp ^= (tmp>>8);                  	/* 2 -> 1 */
+	return (tmp^(tmp>>4)^af) & 0x0f;	/* 1 -> 1/2 */
 }
 
 
@@ -129,969 +127,1014 @@
  * address to a hash code.
  */
 
-static __inline__ int
-nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
+extern __inline__ int nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
 {
-  return HASH(nat_addr32(nat,sa), sa->sa_family);
+	return HASH(nat_addr32(nat,sa), sa->sa_family);
 }
 
 
 /*
- * change net_alias_type number of attachments (bindings)
+ *	Change net_alias_type number of attachments (bindings)
  */
 
-static int
-nat_attach_chg(struct net_alias_type *nat, int delta)
+static int nat_attach_chg(struct net_alias_type *nat, int delta)
 {
-  unsigned long flags;
-  int n_at;
-  if (!nat) return -1;
-  save_flags(flags);
-  cli();
-  n_at = nat->n_attach + delta;
-  if (n_at < 0)
-  {
-    restore_flags(flags);
-    printk(KERN_WARNING "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n",
-	   nat->type);
-    return -1;
-  }
-  nat->n_attach = n_at;
-  restore_flags(flags);
-  return 0;
+	unsigned long flags;
+	int n_at;
+	if (!nat)
+		return -1;
+	save_flags(flags);
+	cli();
+	n_at = nat->n_attach + delta;
+	if (n_at < 0)
+	{
+		restore_flags(flags);
+		printk(KERN_WARNING 
+			"net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n",
+				nat->type);
+		return -1;
+	}
+	nat->n_attach = n_at;
+	restore_flags(flags);
+	return 0;
 }
 
 
 /*
- * bind alias to its type (family) object and call initialization hook
+ *	Bind alias to its type (family) object and call initialization hook
  */
 
-static __inline__ int
-nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa)
+extern __inline__ int nat_bind(struct net_alias_type *nat,
+			struct net_alias *alias, struct sockaddr *sa)
 {
-  if (nat->alias_init_1) nat->alias_init_1(nat, alias, sa);
-  return nat_attach_chg(nat, +1);
+	if (nat->alias_init_1) 
+		nat->alias_init_1(nat, alias, sa);
+	return nat_attach_chg(nat, +1);
 }
 
 
 /*
- * unbind alias from type object and call alias destructor
+ *	Unbind alias from type object and call alias destructor
  */
 
-static __inline__ int
-nat_unbind(struct net_alias_type *nat, struct net_alias *alias)
+extern __inline__ int nat_unbind(struct net_alias_type *nat,
+		 struct net_alias *alias)
 {
-  if (nat->alias_done_1) nat->alias_done_1(nat, alias);
-  return nat_attach_chg(nat, -1);
+	if (nat->alias_done_1)
+		nat->alias_done_1(nat, alias);
+	return nat_attach_chg(nat, -1);
 }
 
 
 /*
- * compare device address with given. if NULL nat->dev_addr_chk,
- * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
+ *	Compare device address with given. if NULL nat->dev_addr_chk,
+ *	compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
  */
 
 static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat,
 				   struct device *dev, struct sockaddr *sa)
 {
-  if (nat->dev_addr_chk)
-    return nat->dev_addr_chk(nat, dev, sa);
-  else
-    return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
+	if (nat->dev_addr_chk)
+		return nat->dev_addr_chk(nat, dev, sa);
+	else
+		return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
 }
 
 
 /*
- * alias device init()
- * do nothing.
+ *	Alias device init()
+ *	do nothing.
  */
 
-static int 
-net_alias_devinit(struct device *dev)
+static int net_alias_devinit(struct device *dev)
 {
 #ifdef ALIAS_USER_LAND_DEBUG
-  printk("net_alias_devinit(%s) called.\n", dev->name);
+	printk("net_alias_devinit(%s) called.\n", dev->name);
 #endif
-  return 0;
+	return 0;
 }
 
 
 /*
- * hard_start_xmit() should not be called.
- * ignore ... but shout!.
+ *	Hard_start_xmit() should not be called.
+ *	ignore ... but shout!.
  */
 
-static int
-net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev)
+static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev)
 {
-  printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name);
-  dev_kfree_skb(skb, FREE_WRITE);
-  return 0;
+	printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name);
+	dev_kfree_skb(skb, FREE_WRITE);
+	return 0;
 }
 
 
-static int
-net_alias_open(struct device * dev)
+static int net_alias_open(struct device * dev)
 {
-  return 0;
+	return 0;
 }
 
-static int
-net_alias_close(struct device * dev)
+static int net_alias_close(struct device * dev)
 {
-  return 0;
+	return 0;
 }
 
 /*
  * setups a new (alias) device 
  */
 
-static int
-net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat,
-		struct sockaddr *sa)
-{
-  struct device *main_dev;
-  struct device *dev;
-  int family;
-  int i;
-
-  /*
-   *
-   * generic device setup based on main_dev info
-   *
-   * FIXME: is NULL bitwise 0 for all Linux platforms?
-   */
-  
-  main_dev = alias->main_dev;
-  dev = &alias->dev;
-  memset(dev, '\0', sizeof(struct device));
-  family = (sa)? sa->sa_family : main_dev->family;
-
-  dev->alias_info = NULL;	/* no aliasing recursion */
-  dev->my_alias = alias;	/* point to alias */
-  dev->name = alias->name;
-  dev->type = main_dev->type;
-  dev->open = net_alias_open;
-  dev->stop = net_alias_close;
-  dev->hard_header_len = main_dev->hard_header_len;
-  memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
-  memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
-  dev->addr_len = main_dev->addr_len;
-  dev->init = net_alias_devinit;
-  dev->hard_start_xmit = net_alias_hard_start_xmit;
-  dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP;
-
-  /*
-   * only makes sense if same family
-   */
-  
-  if (family == main_dev->family)
-  {
-    dev->metric = main_dev->metric;
-    dev->mtu = main_dev->mtu;
-    dev->pa_alen = main_dev->pa_alen;
-    dev->hard_header = main_dev->hard_header;
-    dev->hard_header_cache = main_dev->hard_header_cache;
-    dev->header_cache_update = main_dev->header_cache_update;
-    dev->rebuild_header = main_dev->rebuild_header;
-  }
-  
-  /*
-   *	Fill in the generic fields of the device structure.
-   *    not actually used, avoids some dev.c #ifdef's
-   */
-  
-  for (i = 0; i < DEV_NUMBUFFS; i++)
-    skb_queue_head_init(&dev->buffs[i]);
-  
-  dev->family = family;
-  return 0;
-}
-
-
-/*
- * slow alias find (parse the whole hash_tab)
- * returns: alias' pointer address
- */
-
-static struct net_alias **
-net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias)
-{
-  unsigned idx, n_aliases;
-  struct net_alias **aliasp;
-
-  /*
-   * for each alias_info's hash_tab entry, for every alias ...
-   */
-  
-  n_aliases = alias_info->n_aliases;
-  for (idx=0; idx < 16 ; idx++)
-    for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next)
-      if (*aliasp == alias)
-	return aliasp;
-      else
-	if (--n_aliases == 0) break; /* faster give up */
-  return NULL;
-}
-
-
-/*
- * create alias device for main_dev with given slot num.
- * if sa==NULL will create a same_family alias device 
- */
-
-static struct device *
-net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data)
-{
-  struct net_alias_info *alias_info;
-  struct net_alias *alias, **aliasp;
-  struct net_alias_type *nat;
-  struct device *dev;
-  unsigned long flags;
-  int family;
-  __u32 addr32;
-  
-  /* FIXME: lock */
-  alias_info = main_dev->alias_info;
-
-  /*
-   * if NULL address given, take family from main_dev
-   */
-  
-  family = (sa)? sa->sa_family : main_dev->family;
-  
-  /*
-   * check if wanted family has a net_alias_type object registered
-   */
+static int net_alias_devsetup(struct net_alias *alias, 
+	struct net_alias_type *nat, struct sockaddr *sa)
+{
+	struct device *main_dev;
+	struct device *dev;
+	int family;
+	int i;
+
+	/*
+	 *
+	 * generic device setup based on main_dev info
+	 *
+	 * FIXME: is NULL bitwise 0 for all Linux platforms?
+	 */
   
-  nat = nat_getbytype(family);
-  if (!nat) {
-#ifdef CONFIG_KERNELD
-    char modname[20];
-    sprintf (modname,"netalias-%d", family);
-    request_module(modname);
+	main_dev = alias->main_dev;
+	dev = &alias->dev;
+	memset(dev, '\0', sizeof(struct device));
+	family = (sa)? sa->sa_family : main_dev->family;
+
+	dev->alias_info = NULL;	/* no aliasing recursion */
+	dev->my_alias = alias;	/* point to alias */
+	dev->name = alias->name;
+	dev->type = main_dev->type;
+	dev->open = net_alias_open;
+	dev->stop = net_alias_close;
+	dev->hard_header_len = main_dev->hard_header_len;
+	memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
+	memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
+	dev->addr_len = main_dev->addr_len;
+	dev->init = net_alias_devinit;
+	dev->hard_start_xmit = net_alias_hard_start_xmit;
+	dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP;
 
-    nat = nat_getbytype(family);
-    if (!nat) {
+	/*
+	 *	Only makes sense if same family (arguable)
+	 */
+  
+	if (family == main_dev->family)
+	{
+		dev->metric = main_dev->metric;
+		dev->mtu = main_dev->mtu;
+		dev->pa_alen = main_dev->pa_alen;
+		dev->hard_header = main_dev->hard_header;
+		dev->hard_header_cache = main_dev->hard_header_cache;
+		dev->header_cache_update = main_dev->header_cache_update;
+		dev->rebuild_header = main_dev->rebuild_header;
+	}
+  
+	/*
+	 *	Fill in the generic fields of the device structure.
+	 *	not actually used, avoids some dev.c #ifdef's
+	 */
+  
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+ 		skb_queue_head_init(&dev->buffs[i]);
+  
+	dev->family = family;
+	return 0;
+}
+
+
+/*
+ *	Slow alias find (parse the whole hash_tab)
+ *	returns: alias' pointer address
+ */
+
+static struct net_alias **net_alias_slow_findp(struct net_alias_info 
+		*alias_info, struct net_alias *alias)
+{
+	unsigned idx, n_aliases;
+	struct net_alias **aliasp;
+
+	/*
+	 *	For each alias_info's hash_tab entry, for every alias ...
+	 */
+  
+	n_aliases = alias_info->n_aliases;
+	for (idx=0; idx < 16 ; idx++)
+	{
+		for (aliasp = &alias_info->hash_tab[idx];*aliasp;
+			aliasp = &(*aliasp)->next)
+		{
+			if (*aliasp == alias)
+				return aliasp;
+			else
+				if (--n_aliases == 0)
+					break; /* faster give up */
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ *	Create alias device for main_dev with given slot num.
+ *	if sa==NULL will create a same_family alias device.
+ */
+
+static struct device *net_alias_dev_create(struct device *main_dev, int slot, 
+	int *err, struct sockaddr *sa, void *data)
+{
+	struct net_alias_info *alias_info;
+	struct net_alias *alias, **aliasp;
+	struct net_alias_type *nat;
+	struct device *dev;
+	unsigned long flags;
+	int family;
+	__u32 addr32;
+ 
+	/* FIXME: lock */
+
+	alias_info = main_dev->alias_info;
+
+	/*
+	 *	If NULL address given, take family from main_dev
+	 */
+  
+	family = (sa)? sa->sa_family : main_dev->family;
+  
+	/*
+	 *	Check if wanted family has a net_alias_type object registered
+	 */
+  
+	nat = nat_getbytype(family);
+	if (!nat) 
+	{
+#ifdef CONFIG_KERNELD
+		char modname[20];
+		sprintf (modname,"netalias-%d", family);
+		request_module(modname);
+
+		nat = nat_getbytype(family);
+		if (!nat) 
+		{
 #endif
-      printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n",
-             main_dev->name, slot, family);
-      /* *err = -EAFNOSUPPORT; */
-      *err = -EINVAL;
-      return NULL;
+			printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n",
+				main_dev->name, slot, family);
+			/* *err = -EAFNOSUPPORT; */
+			*err = -EINVAL;
+			return NULL;
 #ifdef CONFIG_KERNELD
-    }
+		}
 #endif
-  }
+	}
   
-  /*
-   * do not allow creation over downed devices
-   */
+	/*
+	 *	Do not allow creation over downed devices
+	 */
 
-  *err = -EIO;
+	*err = -EIO;
   
-  if (! (main_dev->flags & IFF_UP) )
-    return NULL;
+	if (! (main_dev->flags & IFF_UP) )
+		return NULL;
   
-  /*
-   * if first alias, must also create alias_info
-   */
+	/*
+	 *	If first alias, must also create alias_info
+	 */
       
-  *err = -ENOMEM;
-
-  if (!alias_info)
-  { 
-    alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
-    if (!alias_info) return NULL; /* ENOMEM */
-    memset(alias_info, 0, sizeof(struct net_alias_info));
-  }
-  
-  if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
-    return NULL;		/* ENOMEM */
+	*err = -ENOMEM;
 
-  /*
-   * FIXME: is NULL bitwise 0 for all Linux platforms?
-   */
+	if (!alias_info)
+	{ 
+		alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
+		if (!alias_info)
+			return NULL; /* ENOMEM */
+		memset(alias_info, 0, sizeof(struct net_alias_info));
+	}
+  
+	if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
+		return NULL;		/* ENOMEM */
+
+	memset(alias, 0, sizeof(struct net_alias));
+	alias->slot = slot;
+	alias->main_dev = main_dev;
+	alias->nat = nat;
+	alias->next = NULL;
+	alias->data = data;
+	sprintf(alias->name, "%s:%d", main_dev->name, slot);
   
-  memset(alias, 0, sizeof(struct net_alias));
-  alias->slot = slot;
-  alias->main_dev = main_dev;
-  alias->nat = nat;
-  alias->next = NULL;
-  alias->data = data;
-  sprintf(alias->name, "%s:%d", main_dev->name, slot);
-  
-  /*
-   * initialise alias' device structure
-   */
+	/*
+	 *	Initialise alias' device structure
+	 */
   
-  net_alias_devsetup(alias, nat, sa);
+	net_alias_devsetup(alias, nat, sa);
 
-  dev = &alias->dev;
+	dev = &alias->dev;
   
-  save_flags(flags);
-  cli();
+	save_flags(flags);
+	cli();
 
-  /*
-   * bind alias to its object type
-   * nat_bind calls nat->alias_init_1
-   */
+	/*
+	 *	bind alias to its object type
+	 *	nat_bind calls nat->alias_init_1
+	 */
   
-  nat_bind(nat, alias, sa);
+	nat_bind(nat, alias, sa);
 
-  /*
-   * if no address passed, take from device (could have been
-   * set by nat->alias_init_1)
-   */
+	/*
+	 *	If no address passed, take from device (could have been
+	 *	set by nat->alias_init_1)
+	 */
   
-  addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr;
+	addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr;
   
-  /*
-   * store hash key in alias: will speed-up rehashing and deletion
-   */
+	/*
+	 *	Store hash key in alias: will speed-up rehashing and deletion
+	 */
   
-  alias->hash = HASH(addr32, family);
+	alias->hash = HASH(addr32, family);
 
-  /*
-   * insert alias in hashed linked list
-   */
+	/*
+	 *	Insert alias in hashed linked list
+	 */
   
-  aliasp = &alias_info->hash_tab[alias->hash];
-  alias->next = *aliasp;	
-  *aliasp = alias;
+	aliasp = &alias_info->hash_tab[alias->hash];
+	alias->next = *aliasp;	
+	*aliasp = alias;
 
-  /*
-   * if first alias ...
-   */
+	/*
+	 *	If first alias ...
+	 */
   
-  if (!alias_info->n_aliases++)
-  {
-    alias_info->taildev = main_dev;
-    main_dev->alias_info = alias_info;
-  }
+	if (!alias_info->n_aliases++)
+	{
+		alias_info->taildev = main_dev;
+		main_dev->alias_info = alias_info;
+	}
   
-  /*
-   * add device at tail (just after last main_dev alias)
-   */
+	/*
+	 * add device at tail (just after last main_dev alias)
+	 */
   
-  dev->next = alias_info->taildev->next;
-  alias_info->taildev->next = dev;
-  alias_info->taildev = dev;
-  restore_flags(flags);
-  return dev;
+	dev->next = alias_info->taildev->next;
+	alias_info->taildev->next = dev;
+	alias_info->taildev = dev;
+	restore_flags(flags);
+	return dev;
 }
 
 
 /*
- * delete one main_dev alias (referred by its slot num)
+ *	Delete one main_dev alias (referred by its slot num)
  */
 
-static struct device *
-net_alias_dev_delete(struct device *main_dev, int slot, int *err)
+static struct device *net_alias_dev_delete(struct device *main_dev, int slot, 
+	int *err)
 {
-  struct net_alias_info *alias_info;
-  struct net_alias *alias, **aliasp;
-  struct device *dev;
-  unsigned n_aliases;
-  unsigned long flags;
-  struct net_alias_type *nat;
-  struct device *prevdev;
-  
-  /* FIXME: lock */
-  *err = -ENODEV;
+	struct net_alias_info *alias_info;
+	struct net_alias *alias, **aliasp;
+	struct device *dev;
+	unsigned n_aliases;
+	unsigned long flags;
+	struct net_alias_type *nat;
+	struct device *prevdev;
+
+	/* FIXME: lock */
+	*err = -ENODEV;
   
-  if (main_dev == NULL) return NULL;
+	if (main_dev == NULL) 
+		return NULL;
 
-  /*
-   * does main_dev have aliases?
-   */
+	/*
+	 *	Does main_dev have aliases?
+	 */
   
-  alias_info = main_dev->alias_info;
-  if (!alias_info) return NULL;	/* ENODEV */
+	alias_info = main_dev->alias_info;
+	if (!alias_info) 
+		return NULL;	/* ENODEV */
   
-  n_aliases = alias_info->n_aliases;
+	n_aliases = alias_info->n_aliases;
   
-  /*
-   * find device that holds the same slot number (could also
-   * be strcmp() ala dev_get).
-   */
+	/*
+	 *	Find device that holds the same slot number (could also
+	 *	be strcmp() ala dev_get).
+	 */
   
-  for (prevdev=main_dev, alias = NULL;prevdev->next && n_aliases; prevdev = prevdev->next)
-  {
-    if (!(alias = prevdev->next->my_alias))
-    {
-      printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n");
-      continue;			/* or should give up? */
-    }
-    if (alias->slot == slot) break;
-    alias = NULL;
-    n_aliases--;
-  }
+	for (prevdev=main_dev, alias = NULL;
+		prevdev->next && n_aliases; prevdev = prevdev->next)
+	{
+		if (!(alias = prevdev->next->my_alias))
+		{
+			printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n");
+			continue;			/* or should give up? */
+		}
+		if (alias->slot == slot)
+			break;
+		alias = NULL;
+		n_aliases--;
+	}
   
-  if (!alias) return NULL;	/* ENODEV */
+	if (!alias)
+		return NULL;	/* ENODEV */
   
-  dev = &alias->dev;
+	dev = &alias->dev;
   
-  /*
-   * find alias hashed entry
-   */
+	/*
+	 *	Find alias hashed entry
+	 */
   
-  for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
-    if(*aliasp == alias) break;
+	for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; 
+		aliasp = &(*aliasp)->next)
+	{
+		if(*aliasp == alias) 
+			break;
+	}
 
-  /*
-   * if not found (???), try a full search
-   */
-  
-  if (*aliasp != alias)
-    if ((aliasp = net_alias_slow_findp(alias_info, alias)))
-      printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name);
-    else
-    {
-      printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name);
-      return NULL;		/* ENODEV */
-    }
+	/*
+	 *	If not found (???), try a full search
+	 */
   
-  nat = alias->nat;
+	if (*aliasp != alias)
+	{
+		if ((aliasp = net_alias_slow_findp(alias_info, alias)))
+			printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name);
+		else
+		{
+			printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name);
+			return NULL;		/* ENODEV */
+		}
+	}
+	nat = alias->nat;
 
-  save_flags(flags);
-  cli();
+	save_flags(flags);
+	cli();
   
-  /*
-   * unbind alias from alias_type obj.
-   */
+	/*
+	 *	Unbind alias from alias_type obj.
+	 */
   
-  nat_unbind(nat, alias);
+	nat_unbind(nat, alias);
   
-  /*
-   * is alias at tail?
-   */
+	/*
+	 *	Is alias at tail?
+	 */
   
-  if ( dev == alias_info->taildev )
-    alias_info->taildev = prevdev;
+	if ( dev == alias_info->taildev )
+		alias_info->taildev = prevdev;
   
-  /*
-   * unlink and close device
-   */
-  prevdev->next = dev->next;
-  dev_close(dev);
+	/*
+	 *	Unlink and close device
+	 */
+	prevdev->next = dev->next;
+	dev_close(dev);
   
-  /*
-   * unlink alias
-   */
+	/*
+	 *	Unlink alias
+	 */
   
-  *aliasp = (*aliasp)->next;
+	*aliasp = (*aliasp)->next;
+	if (--alias_info->n_aliases == 0) /* last alias */
+		main_dev->alias_info = NULL;
 
-  if (--alias_info->n_aliases == 0) /* last alias */
-    main_dev->alias_info = NULL;
-  restore_flags(flags);
+	restore_flags(flags);
 
-  /*
-   * now free structures
-   */
+	/*
+	 *	Now free structures
+	 */
   
-  kfree_s(alias, sizeof(struct net_alias));
-  if (main_dev->alias_info == NULL)
-    kfree_s(alias_info, sizeof(struct net_alias_info));
+	kfree_s(alias, sizeof(struct net_alias));
+	if (main_dev->alias_info == NULL)
+		kfree_s(alias_info, sizeof(struct net_alias_info));
 
-  /*
-   * deletion ok (*err=0), NULL device returned.
-   */
+	/*
+	 *	Deletion ok (*err=0), NULL device returned.
+	 */
   
-  *err = 0;
-  return NULL;
+	*err = 0;
+	return NULL;
 }
 
 /*
- * free all main device aliasing stuff
- * will be called on dev_close(main_dev)
+ *	Free all main device aliasing stuff
+ *	will be called on dev_close(main_dev)
  */
 
-static void
-net_alias_free(struct device *main_dev)
+static void net_alias_free(struct device *main_dev)
 {
-  struct net_alias_info *alias_info;
-  struct net_alias *alias;
-  struct net_alias_type *nat;
-  struct device *dev;
-  unsigned long flags;
+	struct net_alias_info *alias_info;
+	struct net_alias *alias;
+	struct net_alias_type *nat;
+	struct device *dev;
+	unsigned long flags;
 
-  /*
-   * do I really have aliases?
-   */
+	/*
+	 *	Do I really have aliases?
+	 */
   
-  if (!(alias_info = main_dev->alias_info))    return;
+	if (!(alias_info = main_dev->alias_info))
+		return;
 
-  /*
-   * fast device link "short-circuit": set main_dev->next to
-   * device after last alias
-   */
+	/*
+	 *	Fast device link "short-circuit": set main_dev->next to
+	 *	device after last alias
+	 */
   
-  save_flags(flags);
-  cli();
+	save_flags(flags);
+	cli();
   
-  dev =  main_dev->next;
-  main_dev->next = alias_info->taildev->next;
-  main_dev->alias_info = NULL;
-  alias_info->taildev->next = NULL;
+	dev =  main_dev->next;
+	main_dev->next = alias_info->taildev->next;
+	main_dev->alias_info = NULL;
+	alias_info->taildev->next = NULL;
   
-  restore_flags(flags);
+	restore_flags(flags);
 
-  /*
-   * loop over alias devices, free and dev_close()
-   */
-  
-  while (dev)
-  {
-    if (net_alias_is(dev))
-    {
-      alias = dev->my_alias;
-      if (alias->main_dev == main_dev)
-      {
 	/*
-	 * unbind alias from alias_type object
+	 *	Loop over alias devices, free and dev_close()
 	 */
-	
-	nat = alias->nat;
-	if (nat)
+  
+	while (dev)
 	{
-	  nat_unbind(nat, alias);
-	} /*  else error/printk ??? */
+		if (net_alias_is(dev))
+		{
+			alias = dev->my_alias;
+			if (alias->main_dev == main_dev)
+			{
+				/*
+				 * unbind alias from alias_type object
+				 */
+				nat = alias->nat;
+				if (nat)
+				{
+					nat_unbind(nat, alias);
+				} /*  else error/printk ??? */
+			
+				dev_close(dev);
+				dev = dev->next;
 	
-	dev_close(dev);
-	dev = dev->next;
-	
-	kfree_s(alias, sizeof(struct net_alias));
-	continue;
-      }
-      else
-	printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n",
-	       main_dev->name, alias->name);
-    }
-    else
-      printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n",
-	     main_dev->name);
-    dev = dev->next;
-  }
-  
-  kfree_s(alias_info, sizeof(alias_info));
-  return;
+				kfree_s(alias, sizeof(struct net_alias));
+				continue;
+			}
+			else
+				printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n",
+					main_dev->name, alias->name);
+		}
+		else
+		{
+			printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n",
+				main_dev->name);
+		}
+		dev = dev->next;
+	}
+	  
+	kfree_s(alias_info, sizeof(alias_info));
+	return;
 }
 
 /*
- * dev_get() with added alias naming magic.
+ *	dev_get() with added alias naming magic.
  */
 
-struct device *
-net_alias_dev_get(char *dev_name, int aliasing_ok, int *err,
+struct device *net_alias_dev_get(char *dev_name, int aliasing_ok, int *err,
 		  struct sockaddr *sa, void *data)
 {
-  struct device *dev;
-  char *sptr,*eptr;
-  int slot = 0;
-  int delete = 0;
-  
-  *err = -ENODEV;
-  if ((dev=dev_get(dev_name)))
-    return dev;
+	struct device *dev;
+	char *sptr,*eptr;
+	int slot = 0;
+	int delete = 0;
+  
+	*err = -ENODEV;
+	if ((dev=dev_get(dev_name)))
+		return dev;
 
-  /*
-   * want alias naming magic?
-   */
+	/*
+	 *	Want alias naming magic?
+	 */
   
-  if (!aliasing_ok) return NULL;
+	if (!aliasing_ok) 
+		return NULL;
 
-  if (!dev_name || !*dev_name)
-    return NULL;
-  
-  /*
-   * find the first ':' , must be followed by, at least, 1 char
-   */
+	if (!dev_name || !*dev_name)
+		return NULL;
   
-  for (sptr=dev_name ; *sptr ; sptr++) if(*sptr==':') break;
-  if (!*sptr || !*(sptr+1))
-    return NULL;
-  
-  /*
-   * seems to be an alias name, fetch main device
-   */
+	/*
+	 *	Find the first ':' , must be followed by, at least, 1 char
+	 */
+
+	sptr=strchr(dev_name,':');  
+	if (sptr==NULL || !sptr[1])
+		return NULL;  
+
+#if 0
+	for (sptr=dev_name ; *sptr ; sptr++) 
+		if(*sptr==':')
+			break;
+	if (!*sptr || !*(sptr+1))
+		return NULL;
+#endif
+	/*
+	 *	Seems to be an alias name, fetch main device
+	 */
   
-  *sptr='\0';
-  if (!(dev=dev_get(dev_name)))
-    return NULL;
-  *sptr++=':';
+	*sptr='\0';
+	if (!(dev=dev_get(dev_name)))
+		return NULL;
+	*sptr++=':';
   
-  /*
-   * fetch slot number
-   */
+	/*
+	 *	Fetch slot number
+	 */
   
-  slot = simple_strtoul(sptr,&eptr,10);
-  if (slot >= NET_ALIAS_MAX_SLOT)
-    return NULL;
+	slot = simple_strtoul(sptr,&eptr,10);
+	if (slot >= NET_ALIAS_MAX_SLOT)
+		return NULL;
 
-  /*
-   * if last char is '-', it is a deletion request
-   */
+	/*
+	 *	If last char is '-', it is a deletion request
+	 */
   
-  if (eptr[0] == '-' && !eptr[1] ) delete++;
-  else if (eptr[0])
-    return NULL;
+	if (eptr[0] == '-' && !eptr[1] ) 
+		delete++;
+	else if (eptr[0])
+		return NULL;
   
-  /*
-   * well... let's work.
-   */
+	/*
+	 *	Well... let's work.
+	 */
   
-  if (delete)
-    return net_alias_dev_delete(dev, slot, err);
-  else
-    return net_alias_dev_create(dev, slot, err, sa, data);
+	if (delete)
+		return net_alias_dev_delete(dev, slot, err);
+	else
+		return net_alias_dev_create(dev, slot, err, sa, data);
 }
 
 
 /*
- * rehash alias device with address supplied. 
+ *	Rehash alias device with address supplied. 
  */
 
-int
-net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
+int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
 {
-  struct net_alias_info *alias_info;
-  struct net_alias *alias, **aliasp;
-  struct device *main_dev;
-  unsigned long flags;
-  struct net_alias_type *o_nat, *n_nat;
-  unsigned n_hash;
+	struct net_alias_info *alias_info;
+	struct net_alias *alias, **aliasp;
+	struct device *main_dev;
+	unsigned long flags;
+	struct net_alias_type *o_nat, *n_nat;
+	unsigned n_hash;
 
-  /*
-   * defensive ...
-   */
+	/*
+	 *	Defensive ...
+	 */
   
-  if (dev == NULL) return -1;
-  if ( (alias = dev->my_alias) == NULL ) return -1;
+	if (dev == NULL)
+		return -1;
+	if ( (alias = dev->my_alias) == NULL ) 
+		return -1;
   
-  if (!sa)
-  {
-    printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n");
-    return -1;
-  }
+	if (!sa)
+	{
+		printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n");
+		return -1;
+	}
 
-  /*
-   * defensive. should not happen.
-   */
+	/*
+	 *	Defensive. should not happen.
+	 */
 
-  if ( (main_dev = alias->main_dev) == NULL )
-  {
-    printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name);
-    return -1;
-  }
+	if ( (main_dev = alias->main_dev) == NULL )
+	{
+		printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name);
+		return -1;
+	}
 
-  /*
-   * defensive. should not happen.
-   */
+	/*
+	 *	Defensive. should not happen.
+	 */
 
-  if (!(alias_info=main_dev->alias_info))
-  {
-    printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name);
-    return -1;
-  }
+	if (!(alias_info=main_dev->alias_info))
+	{
+		printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name);
+		return -1;
+	}
   
-  /*
-   * will the request also change device family?
-   */
+	/*
+	 *	Will the request also change device family?
+	 */
   
-  o_nat = alias->nat;
-  if (!o_nat)
-  {
-    printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name);
-    return -1;
-  }
+	o_nat = alias->nat;
+	if (!o_nat)
+	{
+		printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name);
+		return -1;
+	}
 
-  /*
-   * point to new alias_type obj.
-   */
-  
-  if (o_nat->type == sa->sa_family)
-    n_nat = o_nat;
-  else
-  {
-    n_nat = nat_getbytype(sa->sa_family);
-    if (!n_nat)
-    {
-      printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family);
-      return -1;
-    }
-  }
+	/*
+	 *	Point to new alias_type obj.
+	 */
+ 
+	if (o_nat->type == sa->sa_family)
+		n_nat = o_nat;
+	else
+	{
+		n_nat = nat_getbytype(sa->sa_family);
+		if (!n_nat)
+		{
+			printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family);
+			return -1;
+		}
+	}
   
-  /*
-   * new hash key. if same as old AND same type (family) return;
-   */
+	/*
+	 *	New hash key. if same as old AND same type (family) return;
+	 */
   
-  n_hash = nat_hash_key(n_nat, sa);
-  if (n_hash == alias->hash && o_nat == n_nat )
-    return 0;
+	n_hash = nat_hash_key(n_nat, sa);
+	if (n_hash == alias->hash && o_nat == n_nat )
+		return 0;
 
-  /*
-   * find alias in hashed list
-   */
-  
-  for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
-    if (*aliasp == alias) break;
-  
-  /*
-   * not found (???). try a full search
-   */
+	/*
+	 *	Find alias in hashed list
+	 */
   
-  if(!*aliasp)
-    if ((aliasp = net_alias_slow_findp(alias_info, alias)))
-      printk(KERN_WARNING "net_alias_rehash(%s): bad hashing recovered\n", alias->name);
-    else
-    {
-      printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name);
-      return -1;
-    }
+	for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; 
+		aliasp = &(*aliasp)->next)
+	{
+		if (*aliasp == alias) 
+			break;
+	}  
+
+	/*
+	 *	Not found (???). try a full search
+	 */
   
-  save_flags(flags);
-  cli();
+	if(!*aliasp)
+	{
+		if ((aliasp = net_alias_slow_findp(alias_info, alias)))
+		{
+			printk(KERN_WARNING 
+				"net_alias_rehash(%s): bad hashing recovered\n", alias->name);
+		}
+		else
+		{
+			printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name);
+			return -1;
+		}
+  	}
+
+	save_flags(flags);
+	cli();
   
-  /*
-   * if type (family) changed, unlink from old type object (o_nat)
-   * will call o_nat->alias_done_1()
-   */
+	/*
+	 *	If type (family) changed, unlink from old type object (o_nat)
+	 *	Will call o_nat->alias_done_1()
+	 */
   
-  if (o_nat != n_nat)
-    nat_unbind(o_nat, alias);
+	if (o_nat != n_nat)
+		nat_unbind(o_nat, alias);
 
-  /*
-   * if diff hash key, change alias position in hashed list
-   */
+	/*
+	 *	If diff hash key, change alias position in hashed list
+	 */
   
-  if (n_hash != alias->hash)
-  {
-    *aliasp = (*aliasp)->next;
-    alias->hash = n_hash;
-    aliasp = &alias_info->hash_tab[n_hash];
-    alias->next = *aliasp;
-    *aliasp = alias;
-  }
+	if (n_hash != alias->hash)
+	{
+		*aliasp = (*aliasp)->next;
+		alias->hash = n_hash;
+		aliasp = &alias_info->hash_tab[n_hash];
+		alias->next = *aliasp;
+		*aliasp = alias;
+	}
   
-  /*
-   * if type (family) changed link to new type object (n_nat)
-   * will call n_nat->alias_init_1()
-   */
+	/*
+	 *	If type (family) changed link to new type object (n_nat)
+	 *	will call n_nat->alias_init_1()
+	 */
   
-  if (o_nat != n_nat)
-    nat_bind(n_nat, alias, sa);
+	if (o_nat != n_nat)
+		nat_bind(n_nat, alias, sa);
   
-  restore_flags(flags);
-  return 0;
+	restore_flags(flags);
+	return 0;
 }
 
 
 
 
 /*
- *  implements /proc/net/alias_types entry
- *  shows net_alias_type objects registered.
+ *	Implements /proc/net/alias_types entry
+ *	Shows net_alias_type objects registered.
  */
 
 int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
 {
-  off_t pos=0, begin=0;
-  int len=0;
-  struct net_alias_type *nat;
-  unsigned idx;
-  len=sprintf(buffer,"type    name            n_attach\n");
-  for (idx=0 ; idx < 16 ; idx++)
-    for (nat = nat_base[idx]; nat ; nat = nat->next)
-    {
-      len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
-		     nat->type, nat->name,nat->n_attach);
-      pos=begin+len;
-      if(pos<offset)
-      {
-	len=0;
-	begin=pos;
-      }
-      if(pos>offset+length)
-	break;
-    }
-  *start=buffer+(offset-begin);
-  len-=(offset-begin);
-  if(len>length)
-    len=length;	
-  return len;
+	off_t pos=0, begin=0;
+	int len=0;
+	struct net_alias_type *nat;
+	unsigned idx;
+	len=sprintf(buffer,"type    name            n_attach\n");
+	for (idx=0 ; idx < 16 ; idx++)
+	{
+		for (nat = nat_base[idx]; nat ; nat = nat->next)
+		{
+			len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
+				nat->type, nat->name,nat->n_attach);
+			pos=begin+len;
+			if(pos<offset)
+			{
+				len=0;
+				begin=pos;
+			}
+			if(pos>offset+length)
+				break;
+		}
+	}
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;	
+	return len;
 }
 
 
 /*
- *  implements /proc/net/aliases entry, shows alias devices.
- *   calls alias nat->alias_print_1 if not NULL and formats everything
- *   to a fixed rec. size without using local (stack) buffers
+ *	Implements /proc/net/aliases entry, shows alias devices.
+ *	calls alias nat->alias_print_1 if not NULL and formats everything
+ *	to a fixed rec. size without using local (stack) buffers
  *
  */
 
 #define NET_ALIASES_RECSIZ 64
-int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+
+int net_alias_getinfo(char *buffer, char **start, off_t offset, 
+	int length, int dummy)
 {
-  off_t pos=0, begin=0;
-  int len=0;
-  int dlen;
-  struct net_alias_type *nat;
-  struct net_alias *alias;
-  struct device *dev;
-
-  len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device           family address");
-  for (dev = dev_base; dev ; dev = dev->next)
-    if (net_alias_is(dev))
-    {
-      alias = dev->my_alias;
-      nat = alias->nat;
-      dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family);
+	off_t pos=0, begin=0;
+	int len=0;
+	int dlen;
+	struct net_alias_type *nat;
+	struct net_alias *alias;
+	struct device *dev;
+
+	len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device           family address");
+	for (dev = dev_base; dev ; dev = dev->next)
+	{
+		if (net_alias_is(dev))
+		{
+			alias = dev->my_alias;
+			nat = alias->nat;
+			dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family);
       
-      /*
-       * call alias_type specific print function.
-       */
+			/*
+			 *	Call alias_type specific print function.
+			 */
       
-      if (nat->alias_print_1)
-	dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
-      else
-	dlen += sprintf(buffer+len+dlen, "-");
-
-      /*
-       * fill with spaces if needed 
-       */
+			if (nat->alias_print_1)
+				dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
+			else
+				dlen += sprintf(buffer+len+dlen, "-");
+		
+			/*
+			 *	Fill with spaces if needed 
+			 */
       
-      if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
-      /*
-       * truncate to NET_ALIASES_RECSIZ
-       */
+			if (dlen < NET_ALIASES_RECSIZ)
+				memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
+				
+			/*
+			 *	Truncate to NET_ALIASES_RECSIZ
+			 */
       
-      len += NET_ALIASES_RECSIZ;
-      buffer[len-1] = '\n';
+			len += NET_ALIASES_RECSIZ;
+			buffer[len-1] = '\n';
       
-      pos=begin+len;
-      if(pos<offset)
-      {
-	len=0;
-	begin=pos;
-      }
-      if(pos>offset+length)
-	break;
-    }
-  *start=buffer+(offset-begin);
-  len-=(offset-begin);
-  if(len>length)
-    len=length;	
-  return len;
+			pos=begin+len;
+			if(pos<offset)
+			{
+				len=0;
+				begin=pos;
+			}
+			if(pos>offset+length)
+				break;
+		}
+	}
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;	
+	return len;
 }
 
 
 /*
- * notifier for devices events
+ *	Notifier for devices events
  */
 
 int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-  struct device *dev = ptr;
+	struct device *dev = ptr;
 
-  if (event == NETDEV_DOWN)
-  {
+	if (event == NETDEV_DOWN)
+	{
 #ifdef ALIAS_USER_LAND_DEBUG
-    printk("net_alias: NETDEV_DOWN for %s received\n", dev->name);
+		printk("net_alias: NETDEV_DOWN for %s received\n", dev->name);
 #endif
-    if (net_alias_has(dev))
-      net_alias_free(dev);
-  }
+		if (net_alias_has(dev))
+			net_alias_free(dev);
+	}
 
-  if (event == NETDEV_UP)
-  {
+	if (event == NETDEV_UP)
+	{
 #ifdef ALIAS_USER_LAND_DEBUG
-    printk("net_alias: NETDEV_UP for %s received\n", dev->name);
+		printk("net_alias: NETDEV_UP for %s received\n", dev->name);
 #endif
-    dev->alias_info = 0;
-  }
+		dev->alias_info = 0;
+	}
 
-  return NOTIFY_DONE;
+	return NOTIFY_DONE;
 }
 
 
 /*
- * device aliases address comparison workhorse
- * no checks for nat and alias_info, must be !NULL
+ *	Device aliases address comparison workhorse
+ *	No checks for nat and alias_info, must be !NULL
  */
 
-static __inline__ struct device *
-nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
+extern __inline__ struct device *nat_addr_chk(struct net_alias_type *nat, 
+	struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
 {
-  struct net_alias *alias;
-  for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
-      alias; alias = alias->next)
-  {
-    if (alias->dev.family != sa->sa_family) continue;
+	struct net_alias *alias;
+	for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
+		alias; alias = alias->next)
+	{
+		if (alias->dev.family != sa->sa_family) 
+			continue;
 
-    /*
-     * nat_dev_addr_chk_1 will call type specific address cmp function.
-     */
+		/*
+		 *	Nat_dev_addr_chk_1 will call type specific address 
+		 *	cmp function.
+		 */
     
-    if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
-	nat_dev_addr_chk_1(nat,&alias->dev,sa))
-      return &alias->dev;
-  }
-  return NULL;
+		if (alias->dev.flags & flags_on && 
+				!(alias->dev.flags & flags_off) && 
+				nat_dev_addr_chk_1(nat,&alias->dev,sa))
+			return &alias->dev;
+	}
+	return NULL;
 }
 
 /*
- * nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
- * note that nat pointer is ignored because of static comparison.
+ *	Nat_addr_chk enough for protocols whose addr is (fully) stored at 
+ *	pa_addr. Note that nat pointer is ignored because of static comparison.
  */
 
-static __inline__ struct device *
-nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off)
+extern __inline__ struct device *nat_addr_chk32(struct net_alias_type *nat, 
+	struct net_alias_info *alias_info, int family, __u32 addr32, 
+	int flags_on, int flags_off)
 {
-  struct net_alias *alias;
-  for (alias=alias_info->hash_tab[HASH(addr32,family)];
-       alias; alias=alias->next)
-  {
-    if (alias->dev.family != family) continue;
-    
-    /*
-     * "hard" (static) comparison between addr32 and pa_addr.
-     */
+	struct net_alias *alias;
+	for (alias=alias_info->hash_tab[HASH(addr32,family)];
+		alias; alias=alias->next)
+	{
+		if (alias->dev.family != family)
+			continue;
+	/*
+	 *	"hard" (static) comparison between addr32 and pa_addr.
+	 */
     
-    if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
-	addr32 == alias->dev.pa_addr)
-      return &alias->dev;
-  }
-  return NULL;
+	if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
+			addr32 == alias->dev.pa_addr)
+		return &alias->dev;
+	}
+	return NULL;
 }
 
 /*
- * returns alias device with specified address AND flags_on AND flags_off,
- * else NULL.
- * intended for main devices.
+ *	Returns alias device with specified address AND flags_on AND flags_off,
+ *	else NULL.
+ *	Intended for main devices.
  */
 
-struct device *
-net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int flags_off)
+struct device *net_alias_dev_chk(struct device *main_dev, 
+	struct sockaddr *sa,int flags_on, int flags_off)
 {
-  struct net_alias_info *alias_info = main_dev->alias_info;
-  struct net_alias_type *nat;
+	struct net_alias_info *alias_info = main_dev->alias_info;
+	struct net_alias_type *nat;
   
-  /*
-   * only if main_dev has aliases
-   */
+	/*
+	 *	Only if main_dev has aliases
+	 */
 
-  if (!alias_info) return NULL;
+	if (!alias_info)
+		return NULL;
   
-  /*
-   * get alias_type object for sa->sa_family.
-   */
+	/*
+	 *	Get alias_type object for sa->sa_family.
+	 */
   
-  nat = nat_getbytype(sa->sa_family);
-  if (!nat)
-    return NULL;
+	nat = nat_getbytype(sa->sa_family);
+	if (!nat)
+		return NULL;
 
-  return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off);
+	return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off);
 }
 
 /*
@@ -1099,180 +1142,199 @@
  * at pa_addr.
  */
 
-struct device *
-net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32,
-		int flags_on, int flags_off)
+struct device *net_alias_dev_chk32(struct device *main_dev, int family, 
+	__u32 addr32, int flags_on, int flags_off)
 {
-  struct net_alias_info *alias_info = main_dev->alias_info;
+	struct net_alias_info *alias_info = main_dev->alias_info;
   
-  /*
-   * only if main_dev has aliases
-   */
+	/*
+	 * only if main_dev has aliases
+	 */
 
-  if (!alias_info) return NULL;
-  
-  return nat_addr_chk32(NULL, alias_info, family, addr32, flags_on, flags_off);
+	if (!alias_info)
+		return NULL;
+	return nat_addr_chk32(NULL, alias_info, family, addr32, 
+		flags_on, flags_off);
 }
 
 
 /*
- * select closest (main or alias) device to <src,dst> addresses given. if no
- * further info is available, return main_dev (for easier calling arrangement).
+ *	Select closest (main or alias) device to <src,dst> addresses given. If
+ *	there is no further info available, return main_dev (for easier 
+ *	calling arrangement).
  *
- * Should be called early at xxx_rcv() time for device selection
+ *	Should be called early at xxx_rcv() time for device selection
  */
 
-struct device *
-net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
+struct device *net_alias_dev_rcv_sel(struct device *main_dev, 
+	struct sockaddr *sa_src, struct sockaddr *sa_dst)
 {
-  int family;
-  struct net_alias_type *nat;
-  struct net_alias_info *alias_info;
-  struct device *dev;
+	int family;
+	struct net_alias_type *nat;
+	struct net_alias_info *alias_info;
+	struct device *dev;
   
-  if (main_dev == NULL) return NULL;
+	if (main_dev == NULL)
+		return NULL;
+
+	/*
+	 *	If not aliased, don't bother any more
+	 */
 
-  /*
-   * if not aliased, don't bother any more
-   */
+	if ((alias_info = main_dev->alias_info) == NULL)
+		return main_dev;
 
-  if ((alias_info = main_dev->alias_info) == NULL)
-    return main_dev;
+	/*
+	 *	Find out family
+	 */
 
-  /*
-   * find out family
-   */
+	family = (sa_src)? sa_src->sa_family : 
+		((sa_dst)? sa_dst->sa_family : AF_UNSPEC);
 
-  family = (sa_src)? sa_src->sa_family : ((sa_dst)? sa_dst->sa_family : AF_UNSPEC);
-  if (family == AF_UNSPEC) return main_dev;
+	if (family == AF_UNSPEC)
+		return main_dev;
 
-  /*
-   * get net_alias_type object for this family
-   */
+	/*
+	 *	Get net_alias_type object for this family
+	 */
 
-  if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
+	if ( (nat = nat_getbytype(family)) == NULL )
+		return main_dev;
   
-  /*
-   * first step: find out if dst addr is main_dev's or one of its aliases'
-   */
+	/*
+	 *	First step: find out if dst addr is main_dev's or one of its 
+	 *	aliases'
+	 */
 
-  if (sa_dst)
-  {
-    if (nat_dev_addr_chk_1(nat, main_dev,sa_dst))
-      return main_dev;
+	if (sa_dst)
+	{
+		if (nat_dev_addr_chk_1(nat, main_dev,sa_dst))
+			return main_dev;
 
-    dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
+		dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
 
-    if (dev != NULL) return dev;
-  }
+		if (dev != NULL)
+			return dev;
+	}
 
-  /*
-   * second step: find the rcv addr 'closest' alias through nat method call
-   */
+	/*
+	 *	Second step: find the rcv addr 'closest' alias through nat 
+	 *	method call
+	 */
 
-  if ( sa_src == NULL || nat->dev_select == NULL) return main_dev;
-  dev = nat->dev_select(nat, main_dev, sa_src);
+	if ( sa_src == NULL || nat->dev_select == NULL) 
+		return main_dev;
 
-  if (dev == NULL || dev->family != family) return main_dev;
+	dev = nat->dev_select(nat, main_dev, sa_src);
+
+	if (dev == NULL || dev->family != family)
+		return main_dev;
   
-  /*
-   * dev ok only if it is alias of main_dev
-   */
+	/*
+	 *	Dev ok only if it is alias of main_dev
+	 */
   
-  dev = net_alias_is(dev)?
-    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+	dev = net_alias_is(dev)?
+		( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
 
-  /*
-   * do not return NULL.
-   */
+	/*
+	 *	Do not return NULL.
+	 */
   
-  return (dev)? dev : main_dev;
+	return (dev)? dev : main_dev;
 
 }
 
 /*
- * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ *	dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
  */
 
-struct device *
-net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst)
+struct device *net_alias_dev_rcv_sel32(struct device *main_dev, int family,
+	__u32 src, __u32 dst)
 {
-  struct net_alias_type *nat;
-  struct net_alias_info *alias_info;
-  struct sockaddr_in sin_src;
-  struct device *dev;
+	struct net_alias_type *nat;
+	struct net_alias_info *alias_info;
+	struct sockaddr_in sin_src;
+	struct device *dev;
   
-  if (main_dev == NULL) return NULL;
+	if (main_dev == NULL) 
+		return NULL;
 
-  /*
-   * if not aliased, don't bother any more
-   */
+	/*
+	 *	If not aliased, don't bother any more
+	 */
 
-  if ((alias_info = main_dev->alias_info) == NULL)
-    return main_dev;
+	if ((alias_info = main_dev->alias_info) == NULL)
+		return main_dev;
 
-  /*
-   * early return if dst is main_dev's address
-   */
+	/*
+	 *	Early return if dst is main_dev's address
+	 */
   
-  if (dst == main_dev->pa_addr)
-    return main_dev;
+	if (dst == main_dev->pa_addr)
+		return main_dev;
   
-  if (family == AF_UNSPEC) return main_dev;
+	if (family == AF_UNSPEC)
+		return main_dev;
 
-  /*
-   * get net_alias_type object for this family
-   */
+	/*
+	 * 	Get net_alias_type object for this family
+	 */
 
-  if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
+	if ( (nat = nat_getbytype(family)) == NULL )
+		return main_dev;
   
-  /*
-   * first step: find out if dst address one of main_dev aliases'
-   */
+	/*
+	 *	First step: find out if dst address one of main_dev aliases'
+	 */
   
-  if (dst)
-  {
-    dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
-    if (dev) return dev;
-  }
+	if (dst)
+	{
+		dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
+		if (dev) 
+			return dev;
+	}
   
-  /*
-   * second step: find the rcv addr 'closest' alias through nat method call
-   */
+	/*
+	 *	Second step: find the rcv addr 'closest' alias through nat
+	 *	method call
+	 */
 
-  if ( src == 0 || nat->dev_select == NULL) return main_dev;
+	if ( src == 0 || nat->dev_select == NULL) 
+		return main_dev;
 
-  sin_src.sin_family = family;
-  sin_src.sin_addr.s_addr = src;
+	sin_src.sin_family = family;
+	sin_src.sin_addr.s_addr = src;
     
-  dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
+	dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
 
-  if (dev == NULL || dev->family != family) return main_dev;
+	if (dev == NULL || dev->family != family)
+		return main_dev;
   
-  /*
-   * dev ok only if it is alias of main_dev
-   */
+	/*
+	 *	Dev ok only if it is alias of main_dev
+	 */
   
-  dev = net_alias_is(dev)?
-    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+	dev = net_alias_is(dev)?
+		( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
 
-  /*
-   * do not return NULL.
-   */
-  
-  return (dev)? dev : main_dev;
+	/*
+	 *	Do not return NULL.
+	 */
   
+	return (dev)? dev : main_dev;
 }
 
 
 /*
- * device event hook
+ *	Device event hook
  */
 
-static struct notifier_block net_alias_dev_notifier = {
-  net_alias_device_event,
-  NULL,
-  0
+static struct notifier_block net_alias_dev_notifier = 
+{
+	net_alias_device_event,
+	NULL,
+	0
 };
 
 #ifndef ALIAS_USER_LAND_DEBUG
@@ -1293,93 +1355,95 @@
 #endif
 
 /*
- * net_alias initialisation
- * called from net_dev_init().
+ *	Net_alias initialisation called from net_dev_init().
  */
 
 void net_alias_init(void)
 {
 
-  /*
-   * register dev events notifier
-   */
-  
-  register_netdevice_notifier(&net_alias_dev_notifier);
-
-  /*
-   * register /proc/net entries
-   */
+	/*
+	 *	Register device events notifier
+	 */
+  
+	register_netdevice_notifier(&net_alias_dev_notifier);
+
+	/*
+	 *	Register /proc/net entries
+	 */
   
 #ifndef ALIAS_USER_LAND_DEBUG
 #ifdef CONFIG_PROC_FS
-  proc_net_register(&proc_net_alias_types);
-  proc_net_register(&proc_net_aliases);
+	proc_net_register(&proc_net_alias_types);
+	proc_net_register(&proc_net_aliases);
 #endif
 #endif
   
 }
 
 /*
- * net_alias type object registering func.
+ *	Net_alias type object registering func.
  */
+
 int register_net_alias_type(struct net_alias_type *nat, int type)
 {
-  unsigned hash;
-  unsigned long flags;
-  if (!nat)
-  {
-    printk(KERN_ERR "register_net_alias_type(): NULL arg\n");
-    return -EINVAL;
-  }
-  nat->type = type;
-  nat->n_attach = 0;
-  hash = nat->type & 0x0f;
-  save_flags(flags);
-  cli();
-  nat->next = nat_base[hash];
-  nat_base[hash] = nat;
-  restore_flags(flags);
-  return 0;
+	unsigned hash;
+	unsigned long flags;
+	if (!nat)
+	{
+		printk(KERN_ERR "register_net_alias_type(): NULL arg\n");
+		return -EINVAL;
+	}
+	nat->type = type;
+	nat->n_attach = 0;
+	hash = nat->type & 0x0f;
+	save_flags(flags);
+	cli();
+	nat->next = nat_base[hash];
+	nat_base[hash] = nat;
+	restore_flags(flags);
+	return 0;
 }
 
 /*
- * net_alias type object unreg.
+ *	Net_alias type object unreg.
  */
+ 
 int unregister_net_alias_type(struct net_alias_type *nat)
 {
-  struct net_alias_type **natp;
-  unsigned hash;
-  unsigned long flags;
-  
-  if (!nat)
-  {
-    printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n");
-    return -EINVAL;
-  }
-
-  /*
-   * only allow unregistration if it has no attachments
-   */
-  if (nat->n_attach)
-  {
-    printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n",
-	   nat->n_attach);
-    return -EINVAL;
-  }
-  hash = nat->type & 0x0f;
-  save_flags(flags);
-  cli();
-  for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
-  {
-    if (nat==(*natp))
-    {
-      *natp = nat->next;
-      restore_flags(flags);
-      return 0;
-    }
-  }
-  restore_flags(flags);
-  printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type);
-  return -EINVAL;
+	struct net_alias_type **natp;
+	unsigned hash;
+	unsigned long flags;
+
+	if (!nat)
+	{
+		printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n");
+		return -EINVAL;
+	}
+
+	/*
+	 *	Only allow unregistration if it has no attachments
+	 */
+
+	if (nat->n_attach)
+	{
+		printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n",
+			nat->n_attach);
+		return -EINVAL;
+	}
+	hash = nat->type & 0x0f;
+	save_flags(flags);
+	cli();
+	for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
+	{
+		if (nat==(*natp))
+		{
+			*natp = nat->next;
+			restore_flags(flags);
+			return 0;
+		}
+	}
+	restore_flags(flags);
+	printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type);
+	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.19/linux/net/core/scm.c linux/net/core/scm.c
--- v2.1.19/linux/net/core/scm.c	Thu Dec 12 19:37:24 1996
+++ linux/net/core/scm.c	Thu Jan  2 15:13:28 1997
@@ -37,7 +37,8 @@
 
 
 /*
- *	Allow to send credentials, that user could set with setu(g)id.
+ *	Only allow a user to send credentials, that they could set with 
+ *	setu(g)id.
  */
 
 static __inline__ int scm_check_creds(struct ucred *creds)
@@ -54,8 +55,7 @@
 }
 
 
-static int
-scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
 {
 	int num;
 	struct scm_fp_list *fpl = *fplp;
@@ -124,7 +124,7 @@
 
 
 
-static __inline__ int not_one_bit(unsigned val)
+extern __inline__ int not_one_bit(unsigned val)
 {
 	return (val-1) & val;
 }
@@ -138,16 +138,18 @@
 	int acc_fd;
 	unsigned scm_flags=0;
 
-	for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) {
+	for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) 
+	{
 		if (kcm.cmsg_level != SOL_SOCKET)
 			continue;
 
 		err = -EINVAL;
 
 		/*
-		 * Temporary hack: no protocols except for AF_UNIX
-		 * undestand scm now.
+		 *	Temporary hack: no protocols except for AF_UNIX
+		 *	undestand scm now.
 		 */
+
 		if (sock->ops->family != AF_UNIX)
 			goto error;
 
@@ -285,7 +287,7 @@
 	scm->fp = NULL;
 }
 
-struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl)
+struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
 {
 	int i;
 	struct scm_fp_list *new_fpl;
diff -u --recursive --new-file v2.1.19/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.19/linux/net/core/skbuff.c	Thu Dec 12 19:37:24 1996
+++ linux/net/core/skbuff.c	Thu Jan  2 15:13:28 1997
@@ -87,7 +87,7 @@
 #if CONFIG_SKB_CHECK
 
 /*
- *	Debugging paranoia. Can go later when this crud stack works
+ *	Debugging paranoia. Used for debugging network stacks.
  */
 
 int skb_check(struct sk_buff *skb, int head, int line, char *file)
@@ -114,20 +114,6 @@
 				file,line);
 			return -1;
 		}
-#if 0
-		{
-		struct sk_buff *skb2 = skb->next;
-		int i = 0;
-		while (skb2 != skb && i < 5) {
-			if (skb_check(skb2, 0, line, file) < 0) {
-				printk("bad queue element in whole queue\n");
-				return -1;
-			}
-			i++;
-			skb2 = skb2->next;
-		}
-		}
-#endif
 		return 0;
 	}
 	if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
@@ -573,9 +559,15 @@
 
 #endif
 
+/**************************************************************************
+
+      Stuff below this point isn't debugging duplicates of the inlines
+      used for buffer handling
+      
+***************************************************************************/
+
 /*
- *	Free an sk_buff. This still knows about things it should
- *	not need to like protocols and sockets.
+ *	Free an sk_buff. Release anything attached to the buffer.
  */
 
 void __kfree_skb(struct sk_buff *skb)
@@ -603,7 +595,12 @@
 /*
  *	Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
  *	fields and also do memory statistics to find all the [BEEP] leaks.
+ *
+ *	Note: For now we put the header after the data to get better cache
+ *	usage. Once we have a good cache aware kmalloc this will cease
+ *	to be a good idea.
  */
+
 struct sk_buff *alloc_skb(unsigned int size,int priority)
 {
 	struct sk_buff *skb;
@@ -620,6 +617,11 @@
 		}
 	}
 
+	/*
+	 *	FIXME: We could do with an architecture dependant
+	 *	'alignment mask'.
+	 */
+	 
 	size=(size+15)&~15;		/* Allow for alignments. Make a multiple of 16 bytes */
 	len = size;
 	
@@ -684,7 +686,7 @@
  *	Free an skbuff by memory
  */
 
-static inline void __kfree_skbmem(struct sk_buff *skb)
+extern inline void __kfree_skbmem(struct sk_buff *skb)
 {
 	/* don't do anything if somebody still uses us */
 	if (atomic_dec_and_test(&skb->count)) {
diff -u --recursive --new-file v2.1.19/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.19/linux/net/core/sock.c	Thu Dec 12 19:37:24 1996
+++ linux/net/core/sock.c	Thu Jan  2 15:13:28 1997
@@ -367,6 +367,11 @@
   	return err;
 }
 
+/*
+ *	All socket objects are allocated here. This is for future
+ *	usage.
+ */
+ 
 struct sock *sk_alloc(int priority)
 {
 	struct sock *sk=(struct sock *)kmalloc(sizeof(*sk), priority);
@@ -381,6 +386,10 @@
 	kfree_s(sk,sizeof(*sk));
 }
 
+/*
+ *	Simple resource managers for sockets.
+ */
+ 
 void sock_wfree(struct sk_buff *skb)
 {
 	struct sock *sk = skb->sk;
@@ -498,7 +507,11 @@
 		}
 		
 		if(sk->shutdown&SEND_SHUTDOWN)
-		{
+		{	
+			/*
+			 *	FIXME: Check 1003.1g should we deliver
+			 *	a signal here ???
+			 */
 			*errcode=-EPIPE;
 			return NULL;
 		}
@@ -590,3 +603,101 @@
 	end_bh_atomic();
 #endif  
 }
+
+
+/*
+ *	Generic socket manager library. Most simpler socket families
+ *	use this to manage their socket lists. At some point we should
+ *	hash these. By making this generic we get the lot hashed for free.
+ */
+ 
+void sklist_remove_socket(struct sock **list, struct sock *sk)
+{
+	unsigned long flags;
+	struct sock *s;
+
+	save_flags(flags);
+	cli();
+
+	s= *list;
+	if(s==sk)
+	{
+		*list = s->next;
+		restore_flags(flags);
+		return;
+	}
+	while(s && s->next)
+	{
+		if(s->next==sk)
+		{
+			s->next=sk->next;
+			restore_flags(flags);
+			return;
+		}
+		s=s->next;
+	}
+	restore_flags(flags);
+}
+
+void sklist_insert_socket(struct sock **list, struct sock *sk)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	sk->next= *list;
+	*list=sk;
+	restore_flags(flags);
+}
+
+/*
+ *	This is only called from user mode. Thus it protects itself against
+ *	interrupt users but doesn't worry about being called during work.
+ *	Once it is removed from the queue no interrupt or bottom half will
+ *	touch it and we are (fairly 8-) ) safe.
+ */
+
+void sklist_destroy_socket(struct sock **list, struct sock *sk);
+
+/*
+ *	Handler for deferred kills.
+ */
+
+static void sklist_destroy_timer(unsigned long data)
+{
+	struct sock *sk=(struct sock *)data;
+	sklist_destroy_socket(NULL,sk);
+}
+
+/*
+ *	Destroy a socket. We pass NULL for a list if we know the
+ *	socket is not on a list.
+ */
+ 
+void sklist_destroy_socket(struct sock **list,struct sock *sk)
+{
+	struct sk_buff *skb;
+	if(list)
+		sklist_remove_socket(list, sk);
+
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+	{
+		kfree_skb(skb,FREE_READ);
+	}
+
+	if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead)
+	{
+		sk_free(sk);
+	}
+	else
+	{
+		/*
+		 *	Someone is using our buffers still.. defer
+		 */
+		init_timer(&sk->timer);
+		sk->timer.expires=jiffies+10*HZ;
+		sk->timer.function=sklist_destroy_timer;
+		sk->timer.data = (unsigned long)sk;
+		add_timer(&sk->timer);
+	}
+}
+
diff -u --recursive --new-file v2.1.19/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.19/linux/net/ipv4/devinet.c	Sun Dec 22 16:37:44 1996
+++ linux/net/ipv4/devinet.c	Thu Jan  2 15:13:28 1997
@@ -240,10 +240,6 @@
 
 			addr = (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr.s_addr;
 
-			if (addr == dev->pa_brdaddr) {
-				dev->ip_flags |= IFF_IP_BRD_OK;
-				return 0;
-			}
 			if (dev->flags & IFF_UP)
 				ip_rt_change_broadcast(dev, addr);
 			dev->pa_brdaddr = addr;
diff -u --recursive --new-file v2.1.19/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.19/linux/net/ipv4/tcp.c	Thu Dec 12 19:37:27 1996
+++ linux/net/ipv4/tcp.c	Thu Jan  2 14:07:39 1997
@@ -467,13 +467,31 @@
 
 static void tcp_close_pending (struct sock *sk)
 {
-	struct sk_buff *skb;
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct open_request *req;
 
-	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL)
-	{
-		tcp_close(skb->sk, 0);
-		kfree_skb(skb, FREE_READ);
-	}
+	req = tp->syn_wait_queue;
+
+	if (!req)
+		return;
+	
+	do {
+		struct open_request *iter;
+		
+		if (req->sk)
+			tcp_close(req->sk, 0);
+
+		iter = req;
+		req = req->dl_next;
+		
+		(*iter->class->destructor)(iter);
+		tcp_dec_slow_timer(TCP_SLT_SYNACK);
+		sk->ack_backlog--;
+		kfree(iter);
+
+	} while (req != tp->syn_wait_queue);
+
+	tp->syn_wait_queue = NULL;
 	return;
 }
 
@@ -945,8 +963,9 @@
 
 			actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
 
-			if (copy > actual_win && 
-			    (((long) actual_win) >= (sk->max_window >> 1)))
+			if (copy > actual_win &&
+			    (((long) actual_win) >= (sk->max_window >> 1))
+			    && actual_win)
 			{
 				copy = actual_win;
 			}
@@ -1172,12 +1191,15 @@
 {
 	struct sk_buff *skb;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	unsigned long pspace, rspace;
 
 	/*
 	 * NOTE! The socket must be locked, so that we don't get
 	 * a messed-up receive queue.
 	 */
 
+	pspace = sock_rspace(sk);
+	
 	while ((skb=skb_peek(&sk->receive_queue)) != NULL) {
 		if (!skb->used || skb->users>1)
 			break;
@@ -1192,14 +1214,16 @@
 	 *  else let tcp_data deal with the acking policy.
 	 */
 
-	if (sock_rspace(sk) > tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup) && 
+	rspace = sock_rspace(sk);
+
+	if ((rspace > pspace) &&
+	    (rspace > tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup)) && 
 	    (tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup) < sk->mss)) 
 	{
 		/* Send an ack right now. */
 		sk->delayed_acks++;
 		tcp_read_wakeup(sk);
-	} 
-	
+	}	
 } 
 
 
@@ -1747,7 +1771,7 @@
 got_new_connect:
 		tcp_synq_unlink(tp, req);
 		newsk = req->sk;
-		kfree(req);		
+		kfree(req);
 		sk->ack_backlog--;
 		error = 0;
 out:
@@ -1852,10 +1876,3 @@
 		tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
 	}
 }
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -c -o tcp.o tcp.c"
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.19/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.19/linux/net/ipv4/tcp_input.c	Thu Dec 12 19:37:30 1996
+++ linux/net/ipv4/tcp_input.c	Thu Jan  2 14:07:39 1997
@@ -567,10 +567,11 @@
 		kfree_skb(skb, FREE_WRITE);
 	}
 
-	if (acked && !sk->dead)
+	if (acked)
 	{
 		tp->retrans_head = NULL;
-		sk->write_space(sk);
+		if (!sk->dead)
+			sk->write_space(sk);
 	}
 	
 	return acked;
@@ -1744,8 +1745,6 @@
 			if (tp->snd_una == sk->write_seq) 
 			{
 				tcp_time_wait(sk);
-				if (!sk->dead) 
-					sk->state_change(sk);
 			}
 			break;
 
@@ -1812,7 +1811,7 @@
 		
 	case TCP_ESTABLISHED:
 		queued = tcp_data(skb, sk, len);
-		break;		
+		break;
 	}
 
 	/*
diff -u --recursive --new-file v2.1.19/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.19/linux/net/ipv6/addrconf.c	Thu Dec 12 17:02:47 1996
+++ linux/net/ipv6/addrconf.c	Thu Jan  2 14:07:39 1997
@@ -851,34 +851,47 @@
 int addrconf_set_dstaddr(void *arg)
 {
 	struct in6_ifreq ireq;
+	struct inet6_dev *idev;
 	struct device *dev;
-	int err;
+	int err = -EINVAL;
 
-	err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq));
-	
-	if (err)
-		return -EFAULT;
+	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+	{
+		err = -EFAULT;
+		goto err_exit;
+	}
+
+	idev = ipv6_dev_by_index(ireq.ifr6_ifindex);
+
+	if (idev == NULL)
+	{
+		err = -ENODEV;
+		goto err_exit;
+	}
 
-	dev = dev_get(ireq.devname);
+	dev = idev->dev;
 
 	if (dev->type == ARPHRD_SIT)
 	{
 		struct device *dev;
 		
-		if (!(ipv6_addr_type(&ireq.addr) & IPV6_ADDR_COMPATv4))
+		if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
 		{
 			return -EADDRNOTAVAIL;
 		}
 		
-		dev = sit_add_tunnel(ireq.addr.s6_addr32[3]);
+		dev = sit_add_tunnel(ireq.ifr6_addr.s6_addr32[3]);
 		
 		if (dev == NULL)
-			return -ENODEV;
-
-		return 0;
+		{
+			err = -ENODEV;
+		}
+		else
+			err = 0;
 	}
-	
-	return -EINVAL;
+
+err_exit:
+	return err;
 }
 
 /*
@@ -934,20 +947,17 @@
 	if (err)
 		return -EFAULT;
 
-	dev = dev_get(ireq.devname);
-
-	if (dev == NULL)
-		return -EINVAL;
-
-	in6_dev = ipv6_get_idev(dev);
+	in6_dev = ipv6_dev_by_index(ireq.ifr6_ifindex);
 
 	if (in6_dev == NULL)
 		return -EINVAL;
 
-	addr_type  = ipv6_addr_type(&ireq.addr);
+	dev = in6_dev->dev;
+	
+	addr_type  = ipv6_addr_type(&ireq.ifr6_addr);
 	addr_type &= IPV6_ADDR_SCOPE_MASK;
 	
-	ifp = ipv6_add_addr(in6_dev, &ireq.addr, addr_type);
+	ifp = ipv6_add_addr(in6_dev, &ireq.ifr6_addr, addr_type);
 
 	if (ifp == NULL)
 		return -ENOMEM;
@@ -959,12 +969,12 @@
 		struct in6_addr maddr;
 
 		/* join to solicited addr multicast group */
-		addrconf_addr_solict_mult(&ireq.addr, &maddr);
+		addrconf_addr_solict_mult(&ireq.ifr6_addr, &maddr);
 		ipv6_dev_mc_inc(dev, &maddr);
 	}
 
 
-	ifp->prefix_len = ireq.prefix_len;
+	ifp->prefix_len = ireq.ifr6_prefixlen;
 	ifp->flags |= ADDR_PERMANENT;
 
 	if (!(dev->flags & (IFF_NOARP|IFF_LOOPBACK)))
@@ -1150,7 +1160,7 @@
 
 	ifp = (struct inet6_ifaddr *) data;
 
-	if (ifp->probes-- == 0)
+	if (--ifp->probes == 0)
 	{
 		/*
 		 * DAD was successful
@@ -1415,9 +1425,3 @@
 
 	proc_unregister(&proc_net, iface_proc_entry.low_ino);
 }
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.19/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.19/linux/net/ipv6/ndisc.c	Thu Dec 12 19:37:31 1996
+++ linux/net/ipv6/ndisc.c	Thu Jan  2 14:07:39 1997
@@ -163,13 +163,19 @@
 	if (nd_tbl.tbl_lock == 1)
 	{
 		ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
+		ndisc_gc_timer.expires = now + nd_gc_interval;
+	}
+	else
+	{
+#if ND_DEBUG >= 2
+		printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
+#endif
+		ndisc_gc_timer.expires = now + HZ;
 	}
 	end_bh_atomic();
 	
 	neigh_table_unlock(&nd_tbl);
 	
-	ndisc_gc_timer.expires = now + HZ;
-	ndisc_gc_timer.expires = now + nd_gc_interval;
 	add_timer(&ndisc_gc_timer);
 }
 
@@ -178,19 +184,18 @@
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
         unsigned long now = jiffies;
 
-	if (ndn->ndn_refcnt == 0 &&
-	    ((ndn->ndn_nud_state == NUD_FAILED) ||
-	     ((ndn->ndn_nud_state == NUD_REACHABLE) &&
-	      (ndn->ndn_tstamp <= (now - nd_gc_staletime))
-	      )
-	     )
-	    )
+	if (ndn->ndn_refcnt == 0)
 	{
-		/*
-		 *	Release unused entries
-		 */
+		switch (ndn->ndn_nud_state) {
 		
-		return 1;
+		case NUD_REACHABLE:
+		case NUD_STALE:
+			if (now - ndn->ndn_tstamp < nd_gc_staletime)
+				break;
+		case NUD_FAILED:
+			return 1;
+		default:
+		}
 	}
 	return 0;
 }
@@ -198,17 +203,16 @@
 static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer)
 {
 	unsigned long now = jiffies;
-	unsigned long tval;
+	unsigned long tval = ~0UL;
 
 	ndn->ndn_expires = now + timer;
-	tval = del_timer(&ndisc_timer);
-
-	if (tval)
+	
+	if (del_timer(&ndisc_timer))
 	{
-		tval = min(tval, ndn->ndn_expires);
+		tval = ndisc_timer.expires;
 	}
-	else
-		tval = ndn->ndn_expires;
+
+	tval = min(tval, ndn->ndn_expires);
 
 	ndisc_timer.expires = tval;
 	add_timer(&ndisc_timer);
@@ -216,12 +220,15 @@
         
 static void ndisc_del_timer(struct nd_neigh *ndn)
 {
-	unsigned long tval;
+	unsigned long tval = ~0UL;
 
 	if (!(ndn->ndn_nud_state & NUD_IN_TIMER))
 		return;
 
-	tval = del_timer(&ndisc_timer);
+	if (del_timer(&ndisc_timer))
+	{
+		tval = ndisc_timer.expires;
+	}
 	
 	if (tval == ndn->ndn_expires)
 	{
@@ -362,6 +369,9 @@
 	if (neigh == NULL)
 	{
 		neigh = ndisc_new_neigh(dev, addr);
+
+		if (neigh == NULL)
+			return NULL;
 	}
 
 	neigh_table_unlock(&nd_tbl);
@@ -475,6 +485,7 @@
 	if (skb == NULL)
 	{
 		printk(KERN_DEBUG "send_na: alloc skb failed\n");
+		return;
 	}
 
 	if (ipv6_bld_hdr_2(sk, skb, dev, (struct neighbour *) ndn, 
@@ -631,6 +642,7 @@
 	if (skb == NULL)
 	{
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
+		return;
 	}
 
         if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
@@ -723,14 +735,14 @@
 		{
                         if (ndn->ndn_nud_state & NUD_IN_TIMER)
 			{
-				long time;
+				unsigned long time;
 
-				if ((ndn->ndn_expires - now) <= 0)
+				time = ndn->ndn_expires - now;
+
+				if ((long) time <= 0)
 				{
 					time = ndisc_event_timer(ndn);
 				}
-				else
-					time = ndn->ndn_expires - now;
 				
 				if (time)
 				{
@@ -744,7 +756,7 @@
 
 	if (ntimer != (~0UL))
 	{
-		ndisc_timer.expires = jiffies + ntimer;
+		ndisc_timer.expires = now + ntimer;
 		add_timer(&ndisc_timer);
 	}
 	
@@ -1854,10 +1866,3 @@
 	del_timer(&ndisc_timer);
 }
 #endif
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o ndisc.o ndisc.c"
- * c-file-style: "Linux"
- * End:
- */
diff -u --recursive --new-file v2.1.19/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.1.19/linux/net/ipv6/reassembly.c	Thu Dec 12 19:37:31 1996
+++ linux/net/ipv6/reassembly.c	Thu Jan  2 14:07:39 1997
@@ -57,10 +57,13 @@
 		      __u8 *nhptr,
 		      struct frag_hdr *fhdr)
 {
-	__u32	expires;
+	__u32	expires = jiffies + IPV6_FRAG_TIMEOUT;
 	int nh;
 
-	expires = del_timer(&fq->timer);
+	if (del_timer(&fq->timer))
+	{
+		expires = fq->timer.expires;
+	}
 
 	/*
 	 *	We queue the packet even if it's the last.
@@ -83,7 +86,7 @@
 		if ((nh = reasm_frag_1(fq, skb)))
 			return nh;
 	}
-		
+
 	fq->timer.expires = expires;
 	add_timer(&fq->timer);
 	
diff -u --recursive --new-file v2.1.19/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.19/linux/net/ipx/af_ipx.c	Mon Dec 30 15:39:17 1996
+++ linux/net/ipx/af_ipx.c	Thu Jan  2 15:13:28 1997
@@ -46,6 +46,7 @@
  *			Handles WIN95 discovery packets <Volker Lendecke>
  *	Revision 0.36:	Internal bump up for 2.1
  *	Revision 0.37:	Began adding POSIXisms.
+ *	Revision 0.38:  Asynchronous socket stuff made current.
  *
  *	Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
  *	pair. Also, now usage count is managed this way
@@ -115,13 +116,16 @@
 static ipx_interface	*ipx_primary_net = NULL;
 static ipx_interface	*ipx_internal_net = NULL;
 
-static int
-ipxcfg_set_auto_create(char val)
+static int ipxcfg_set_auto_create(char val)
 {
-	if (ipxcfg_auto_create_interfaces != val){
-		if (val){
+	if (ipxcfg_auto_create_interfaces != val)
+	{
+		if (val)
+		{
 			MOD_INC_USE_COUNT;
-		}else{
+		}
+		else
+		{
 			MOD_DEC_USE_COUNT;
 		}
 		ipxcfg_auto_create_interfaces = val;
@@ -129,8 +133,7 @@
 	return 0;
 }
 
-static int
-ipxcfg_set_auto_select(char val)
+static int ipxcfg_set_auto_select(char val)
 {
 	ipxcfg_auto_select_primary = val;
 	if (val && (ipx_primary_net == NULL))
@@ -138,8 +141,7 @@
 	return 0;
 }
 
-static int
-ipxcfg_get_config_data(ipx_config_data *arg)
+static int ipxcfg_get_config_data(ipx_config_data *arg)
 {
 	ipx_config_data	vals;
 
@@ -161,8 +163,7 @@
  *	use this facility.
  */
 
-static void
-ipx_remove_socket(struct sock *sk)
+static void ipx_remove_socket(struct sock *sk)
 {
 	struct sock	*s;
 	ipx_interface	*intrfc;
@@ -203,8 +204,7 @@
  *	touch it and we are (fairly 8-) ) safe.
  */
 
-static void
-ipx_destroy_socket(struct sock *sk)
+static void ipx_destroy_socket(struct sock *sk)
 {
 	struct sk_buff	*skb;
 
@@ -223,8 +223,7 @@
 
 static ipx_route * ipxrtr_lookup(unsigned long);
 
-static void
-ipxitf_clear_primary_net(void)
+static void ipxitf_clear_primary_net(void)
 {
 	if (ipxcfg_auto_select_primary && (ipx_interfaces != NULL))
 		ipx_primary_net = ipx_interfaces;
@@ -232,8 +231,7 @@
 		ipx_primary_net = NULL;
 }
 
-static ipx_interface *
-ipxitf_find_using_phys(struct device *dev, unsigned short datalink)
+static ipx_interface *ipxitf_find_using_phys(struct device *dev, unsigned short datalink)
 {
 	ipx_interface	*i;
 
@@ -244,8 +242,7 @@
 	return i;
 }
 
-static ipx_interface *
-ipxitf_find_using_net(unsigned long net)
+static ipx_interface *ipxitf_find_using_net(unsigned long net)
 {
 	ipx_interface	*i;
 
@@ -259,8 +256,7 @@
 }
 
 /* Sockets are bound to a particular IPX interface. */
-static void
-ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk)
+static void ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk)
 {
 	struct sock	*s;
 
@@ -275,8 +271,7 @@
 	}
 }
 
-static struct sock *
-ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
+static struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
 {
 	struct sock	*s;
 
@@ -290,8 +285,7 @@
 
 #ifdef CONFIG_IPX_INTERN
 
-static struct sock *
-ipxitf_find_internal_socket(ipx_interface *intrfc,
+static struct sock *ipxitf_find_internal_socket(ipx_interface *intrfc,
 			    unsigned char *node, unsigned short port)
 {
 	struct sock *s = intrfc->if_sklist;
@@ -311,8 +305,7 @@
 
 static void ipxrtr_del_routes(ipx_interface *);
 
-static void
-ipxitf_down(ipx_interface *intrfc)
+static void ipxitf_down(ipx_interface *intrfc)
 {
 	ipx_interface	*i;
 	struct sock	*s, *t;
@@ -359,8 +352,7 @@
 	return;
 }
 
-static int
-ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
+static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
 {
 	struct device *dev = ptr;
 	ipx_interface *i, *tmp;
@@ -400,8 +392,7 @@
  */
 
 #ifdef CONFIG_IPX_INTERN
-static int
-ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
+static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
 {
 	struct ipxhdr	*ipx = skb->nh.ipxh;
 	struct sock	*s;
@@ -464,8 +455,7 @@
 
 #else
 
-static int
-ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
+static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
 {
 	struct ipxhdr	*ipx = skb->nh.ipxh;
 	struct sock	*sock1 = NULL, *sock2 = NULL;
@@ -562,8 +552,7 @@
 }
 #endif
 
-static struct sk_buff *
-ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
+static struct sk_buff *ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
 {
 	struct sk_buff	*skb2;
 	int	in_offset = skb->h.raw - skb->head;
@@ -765,69 +754,77 @@
 	}
 
 #ifdef CONFIG_IPX_PPROP_ROUTING
-	if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 ) {
-	  int i;
-          ipx_interface *ifcs;
-	  struct sk_buff *skb2;
-  	  long *l;
-	  char *c;
+	if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 ) 
+	{
+		int i;
+        	ipx_interface *ifcs;
+		struct sk_buff *skb2;
+  		long *l;
+		char *c;
 
 #ifdef DEBUG_IPX_PPROP_ROUTING
-	  printk(KERN_INFO "IPX: PPROP packet received\n"
-		 " Src: %8x:%02x:%02x:%02x:%02x:%02x:%02x:%d/%d\n",
-		 htonl(ipx->ipx_source.net),
-		 ipx->ipx_source.node[0], ipx->ipx_source.node[1],
-		 ipx->ipx_source.node[2], ipx->ipx_source.node[3],
-		 ipx->ipx_source.node[4], ipx->ipx_source.node[5],
-		 htons(ipx->ipx_source.sock),
-		 htons(ipx->ipx_dest.sock)
-		 );
+		printk(KERN_INFO "IPX: PPROP packet received\n"
+			" Src: %8x:%02x:%02x:%02x:%02x:%02x:%02x:%d/%d\n",
+			htonl(ipx->ipx_source.net),
+			ipx->ipx_source.node[0], ipx->ipx_source.node[1],
+			ipx->ipx_source.node[2], ipx->ipx_source.node[3],
+			ipx->ipx_source.node[4], ipx->ipx_source.node[5],
+			htons(ipx->ipx_source.sock),
+			htons(ipx->ipx_dest.sock)
+			);
 #endif
 
-	  c = (char *) skb->data;
-	  c += sizeof( struct ipxhdr );
+		c = (char *) skb->data;
+		c += sizeof( struct ipxhdr );
 
-	  l = (long *) c;
+		l = (long *) c;
 
 #ifdef DEBUG_IPX_PPROP_ROUTING
-	  printk( "IPX: Routing PPROP from net num %08x\n", (unsigned int) htonl(intrfc->if_netnum) );
-	  for( i = 0 ; i < ipx->ipx_tctrl ; i++ )
-	    printk( "IPX: Routing PPROP seen net num %08x\n", (unsigned int) htonl(*l++) );
-	  l = (long *) c;
+		printk( "IPX: Routing PPROP from net num %08x\n", (unsigned int) htonl(intrfc->if_netnum) );
+		for( i = 0 ; i < ipx->ipx_tctrl ; i++ )
+			printk( "IPX: Routing PPROP seen net num %08x\n", (unsigned int) htonl(*l++) );
+		l = (long *) c;
 #endif
-	  i = 0;
-	  /* dump packet if too many hops or already seen this net */
-	  if( ipx->ipx_tctrl < 8 )
-	    for( ; i < ipx->ipx_tctrl ; i++ )
-	      if( *l++ == intrfc->if_netnum )
-		break;
-
-	  if( i == ipx->ipx_tctrl ) { /* < 8 hops && input itfc not in list */
-	    *l = intrfc->if_netnum; /* insert recvd netnum into list */
-
-	    /* xmit on all other interfaces... */
-	    for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) {
-	      /* that aren't in the list */
-	      l = (long *) c;
-	      for( i = 0 ; i <= ipx->ipx_tctrl ; i++ )
-		if( ifcs->if_netnum == *l++ )
-		  break;
-	      if( i - 1 == ipx->ipx_tctrl ) {
-		ipx->ipx_dest.net = ifcs->if_netnum;
+		i = 0;
+		/* 
+		 *	Dump packet if too many hops or already seen this net 
+		 */
+		if( ipx->ipx_tctrl < 8 )
+			for( ; i < ipx->ipx_tctrl ; i++ )
+				if( *l++ == intrfc->if_netnum )
+			break;
+
+		if( i == ipx->ipx_tctrl ) 
+		{ 
+			/* < 8 hops && input itfc not in list */
+			*l = intrfc->if_netnum; /* insert recvd netnum into list */
+			/* xmit on all other interfaces... */
+			for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) 
+			{
+				/* That aren't in the list */
+				l = (long *) c;
+				for( i = 0 ; i <= ipx->ipx_tctrl ; i++ )
+					if( ifcs->if_netnum == *l++ )
+						break;
+				if( i - 1 == ipx->ipx_tctrl ) 
+				{
+					ipx->ipx_dest.net = ifcs->if_netnum;
 #ifdef DEBUG_IPX_PPROP_ROUTING
-		printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
+					printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
 #endif
-		skb2 = skb_clone(skb, GFP_ATOMIC);
-		ipxrtr_route_skb(skb2);
-	      }
+					skb2 = skb_clone(skb, GFP_ATOMIC);
+					ipxrtr_route_skb(skb2);
+				}
 #ifdef DEBUG_IPX_PPROP_ROUTING
-	      else
-		printk( "IPX: Ignoring PPROP for net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
+				else
+					printk( "IPX: Ignoring PPROP for net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
 #endif
-	    }
-	    /* reset netnum in packet */
-	    ipx->ipx_dest.net = intrfc->if_netnum;
-	  }
+			}
+			/*
+			 *	Reset network number in packet 
+			 */
+			ipx->ipx_dest.net = intrfc->if_netnum;
+		}
 
 	}
 #endif
@@ -875,8 +872,7 @@
 	return 0;
 }
 
-static void
-ipxitf_insert(ipx_interface *intrfc)
+static void ipxitf_insert(ipx_interface *intrfc)
 {
 	ipx_interface	*i;
 
@@ -895,16 +891,17 @@
 	return;
 }
 
-static int
-ipxitf_create_internal(ipx_interface_definition *idef)
+static int ipxitf_create_internal(ipx_interface_definition *idef)
 {
 	ipx_interface	*intrfc;
 
 	/* Only one primary network allowed */
-	if (ipx_primary_net != NULL) return -EEXIST;
+	if (ipx_primary_net != NULL)
+		return -EEXIST;
 
 	/* Must have a valid network number */
-	if (idef->ipx_network == 0L) return -EADDRNOTAVAIL;
+	if (idef->ipx_network == 0L)
+		return -EADDRNOTAVAIL;
 	if (ipxitf_find_using_net(idef->ipx_network) != NULL)
 		return -EADDRINUSE;
 
@@ -926,21 +923,25 @@
 	return ipxitf_add_local_route(intrfc);
 }
 
-static int
-ipx_map_frame_type(unsigned char type)
+static int ipx_map_frame_type(unsigned char type)
 {
-	switch (type) {
-	case IPX_FRAME_ETHERII: return htons(ETH_P_IPX);
-	case IPX_FRAME_8022: return htons(ETH_P_802_2);
-	case IPX_FRAME_TR_8022: return htons(ETH_P_TR_802_2);
-	case IPX_FRAME_SNAP: return htons(ETH_P_SNAP);
-	case IPX_FRAME_8023: return htons(ETH_P_802_3);
+	switch (type) 
+	{
+		case IPX_FRAME_ETHERII:
+			return htons(ETH_P_IPX);
+		case IPX_FRAME_8022:
+			return htons(ETH_P_802_2);
+		case IPX_FRAME_TR_8022:
+			return htons(ETH_P_TR_802_2);
+		case IPX_FRAME_SNAP:
+			return htons(ETH_P_SNAP);
+		case IPX_FRAME_8023:
+			return htons(ETH_P_802_3);
 	}
 	return 0;
 }
 
-static int
-ipxitf_create(ipx_interface_definition *idef)
+static int ipxitf_create(ipx_interface_definition *idef)
 {
 	struct device	*dev;
 	unsigned short	dlink_type = 0;
@@ -957,30 +958,31 @@
 		(ipxitf_find_using_net(idef->ipx_network) != NULL))
 		return -EADDRINUSE;
 
-	switch (idef->ipx_dlink_type) {
-	case IPX_FRAME_ETHERII:
-		dlink_type = htons(ETH_P_IPX);
-		datalink = pEII_datalink;
-		break;
-	case IPX_FRAME_TR_8022:
-		dlink_type = htons(ETH_P_TR_802_2);
-		datalink = p8022tr_datalink;
-		break;
-	case IPX_FRAME_8022:
-		dlink_type = htons(ETH_P_802_2);
-		datalink = p8022_datalink;
-		break;
-	case IPX_FRAME_SNAP:
-		dlink_type = htons(ETH_P_SNAP);
-		datalink = pSNAP_datalink;
-		break;
-	case IPX_FRAME_8023:
-		dlink_type = htons(ETH_P_802_3);
-		datalink = p8023_datalink;
-		break;
-	case IPX_FRAME_NONE:
-	default:
-		break;
+	switch (idef->ipx_dlink_type) 
+	{
+		case IPX_FRAME_ETHERII:
+			dlink_type = htons(ETH_P_IPX);
+			datalink = pEII_datalink;
+			break;
+		case IPX_FRAME_TR_8022:
+			dlink_type = htons(ETH_P_TR_802_2);
+			datalink = p8022tr_datalink;
+			break;
+		case IPX_FRAME_8022:
+			dlink_type = htons(ETH_P_802_2);
+			datalink = p8022_datalink;
+			break;
+		case IPX_FRAME_SNAP:
+			dlink_type = htons(ETH_P_SNAP);
+			datalink = pSNAP_datalink;
+			break;
+		case IPX_FRAME_8023:
+			dlink_type = htons(ETH_P_802_3);
+			datalink = p8023_datalink;
+			break;
+		case IPX_FRAME_NONE:
+		default:
+			break;
 	}
 
 	if (datalink == NULL)
@@ -1032,15 +1034,16 @@
 	return ipxitf_add_local_route(intrfc);
 }
 
-static int
-ipxitf_delete(ipx_interface_definition *idef)
+static int ipxitf_delete(ipx_interface_definition *idef)
 {
 	struct device	*dev = NULL;
 	unsigned short	dlink_type = 0;
 	ipx_interface	*intrfc;
 
-	if (idef->ipx_special == IPX_INTERNAL) {
-		if (ipx_internal_net != NULL) {
+	if (idef->ipx_special == IPX_INTERNAL) 
+	{
+		if (ipx_internal_net != NULL) 
+		{
 			ipxitf_down(ipx_internal_net);
 			return 0;
 		}
@@ -1062,19 +1065,31 @@
 	return -EINVAL;
 }
 
-static ipx_interface *
-ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
+static ipx_interface *ipxitf_auto_create(struct device *dev, 
+	unsigned short dlink_type)
 {
 	struct datalink_proto *datalink = NULL;
 	ipx_interface	*intrfc;
 
-	switch (htons(dlink_type)) {
-	case ETH_P_IPX: datalink = pEII_datalink; break;
-	case ETH_P_802_2: datalink = p8022_datalink; break;
-	case ETH_P_TR_802_2: datalink = p8022tr_datalink; break;
-	case ETH_P_SNAP: datalink = pSNAP_datalink; break;
-	case ETH_P_802_3: datalink = p8023_datalink; break;
-	default: return NULL;
+	switch (htons(dlink_type)) 
+	{
+		case ETH_P_IPX:
+			datalink = pEII_datalink;
+			break;
+		case ETH_P_802_2:
+			datalink = p8022_datalink;
+			break;
+		case ETH_P_TR_802_2:
+			datalink = p8022tr_datalink;
+			break;
+		case ETH_P_SNAP:
+			datalink = pSNAP_datalink;
+			break;
+		case ETH_P_802_3:
+			datalink = p8023_datalink;
+			break;
+		default:
+			return NULL;
 	}
 
 	if (dev == NULL)
@@ -1084,7 +1099,8 @@
 	if(dev->addr_len>IPX_NODE_LEN) return NULL;
 
 	intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
-	if (intrfc!=NULL) {
+	if (intrfc!=NULL) 
+	{
 		intrfc->if_dev=dev;
 		intrfc->if_netnum=0L;
 		intrfc->if_dlink_type = dlink_type;
@@ -1103,12 +1119,12 @@
 	return intrfc;
 }
 
-static int
-ipxitf_ioctl_real(unsigned int cmd, void *arg)
+static int ipxitf_ioctl_real(unsigned int cmd, void *arg)
 {
 	switch(cmd)
 	{
-		case SIOCSIFADDR: {
+		case SIOCSIFADDR: 
+		{
 			struct ifreq ifr;
 			struct sockaddr_ipx *sipx;
 			ipx_interface_definition f;
@@ -1128,7 +1144,8 @@
 			else
 				return ipxitf_create(&f);
 		}
-		case SIOCGIFADDR: {
+		case SIOCGIFADDR: 
+		{
 			struct ifreq ifr;
 			struct sockaddr_ipx *sipx;
 			ipx_interface *ipxif;
@@ -1152,14 +1169,16 @@
 				return -EFAULT;
 			return err;
 		}
-		case SIOCAIPXITFCRT: {
+		case SIOCAIPXITFCRT: 
+		{
 			int err, val;
 			err = get_user(val, (unsigned char *) arg);
 			if (err)
 				return err;
 			return ipxcfg_set_auto_create(val);
 		}
-		case SIOCAIPXPRISLT: {
+		case SIOCAIPXPRISLT: 
+		{
 			int err, val;
 			err = get_user(val, (unsigned char *) arg);
 			if (err)
@@ -1171,8 +1190,7 @@
 	}
 }
 
-static int
-ipxitf_ioctl(unsigned int cmd, void *arg)
+static int ipxitf_ioctl(unsigned int cmd, void *arg)
 {
 	int ret;
 	MOD_INC_USE_COUNT;
@@ -1180,14 +1198,14 @@
 	MOD_DEC_USE_COUNT;
 	return ret;
 }
+
 /*******************************************************************************************************************\
 *													            *
 *	            			Routing tables for the IPX socket layer				            *
 *														    *
 \*******************************************************************************************************************/
 
-static ipx_route *
-ipxrtr_lookup(unsigned long net)
+static ipx_route *ipxrtr_lookup(unsigned long net)
 {
 	ipx_route *r;
 
@@ -1197,14 +1215,14 @@
 	return r;
 }
 
-static int
-ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
+static int ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
 {
 	ipx_route	*rt;
 
 	/* Get a route structure; either existing or create */
 	rt = ipxrtr_lookup(network);
-	if (rt==NULL) {
+	if (rt==NULL) 
+	{
 		rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);
 		if(rt==NULL)
 			return -EAGAIN;
@@ -1226,8 +1244,7 @@
 	return 0;
 }
 
-static void
-ipxrtr_del_routes(ipx_interface *intrfc)
+static void ipxrtr_del_routes(ipx_interface *intrfc)
 {
 	ipx_route	**r, *tmp;
 
@@ -1241,8 +1258,7 @@
 	}
 }
 
-static int
-ipxrtr_create(ipx_route_definition *rd)
+static int ipxrtr_create(ipx_route_definition *rd)
 {
 	ipx_interface *intrfc;
 
@@ -1255,18 +1271,18 @@
 }
 
 
-static int
-ipxrtr_delete(long net)
+static int ipxrtr_delete(long net)
 {
 	ipx_route	**r;
 	ipx_route	*tmp;
 
-	for (r = &ipx_routes; (tmp = *r) != NULL; ) {
-		if (tmp->ir_net == net) {
-			if (!(tmp->ir_routed)) {
+	for (r = &ipx_routes; (tmp = *r) != NULL; ) 
+	{
+		if (tmp->ir_net == net) 
+		{
+			if (!(tmp->ir_routed))
 				/* Directly connected; can't lose route */
 				return -EPERM;
-			}
 			*r = tmp->ir_next;
 			kfree_s(tmp, sizeof(ipx_route));
 			return 0;
@@ -1359,9 +1375,9 @@
 	else
 	{
 		rt = ipxrtr_lookup(usipx->sipx_network);
-		if (rt==NULL) {
+		if (rt==NULL)
 			return -ENETUNREACH;
-		}
+
 		intrfc = rt->ir_intrfc;
 	}
 
@@ -1433,15 +1449,15 @@
 				rt->ir_router_node : ipx->ipx_dest.node);
 }
 	
-static int
-ipxrtr_route_skb(struct sk_buff *skb)
+static int ipxrtr_route_skb(struct sk_buff *skb)
 {
 	struct ipxhdr	*ipx = skb->nh.ipxh;
 	ipx_route	*r;
 	ipx_interface	*i;
 
 	r = ipxrtr_lookup(ipx->ipx_dest.net);
-	if (r == NULL) {
+	if (r == NULL)
+	{
 		/* no known route */
 		kfree_skb(skb,FREE_READ);
 		return 0;
@@ -1493,21 +1509,26 @@
 	}
 }
 
-static const char *
-ipx_frame_name(unsigned short frame)
+static const char *ipx_frame_name(unsigned short frame)
 {
-	switch (ntohs(frame)) {
-	case ETH_P_IPX: return "EtherII";
-	case ETH_P_802_2: return "802.2";
-	case ETH_P_SNAP: return "SNAP";
-	case ETH_P_802_3: return "802.3";
-	case ETH_P_TR_802_2: return "802.2TR";
-	default: return "None";
+	switch (ntohs(frame)) 
+	{
+		case ETH_P_IPX:
+			return "EtherII";
+		case ETH_P_802_2:
+			return "802.2";
+		case ETH_P_SNAP:
+			return "SNAP";
+		case ETH_P_802_3:
+			return "802.3";
+		case ETH_P_TR_802_2:
+			return "802.2TR";
+		default:
+			return "None";
 	}
 }
 
-static const char *
-ipx_device_name(ipx_interface *intrfc)
+static const char *ipx_device_name(ipx_interface *intrfc)
 {
 	return (intrfc->if_internal ? "Internal" :
 		(intrfc->if_dev ? intrfc->if_dev->name : "Unknown"));
@@ -1772,6 +1793,15 @@
 	}
 }
 
+static void def_callback3(struct sock *sk, int len)
+{
+	if(!sk->dead)
+	{
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket, 2);
+	}
+}
+
 static int ipx_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
@@ -1807,7 +1837,7 @@
 
 	sk->state_change=def_callback1;
 	sk->data_ready=def_callback2;
-	sk->write_space=def_callback1;
+	sk->write_space=def_callback3;
 	sk->error_report=def_callback1;
 
 	sk->zapped=1;
@@ -1833,8 +1863,7 @@
 	return(ipx_create(newsock,SOCK_DGRAM));
 }
 
-static unsigned short
-ipx_first_free_socketnum(ipx_interface *intrfc)
+static unsigned short ipx_first_free_socketnum(ipx_interface *intrfc)
 {
 	unsigned short	socketNum = intrfc->if_sknum;
 
@@ -2052,56 +2081,66 @@
 /*
  * User to dump IPX packets (debugging)
  */
-void dump_data(char *str,unsigned char *d, int len) {
-  static char h2c[] = "0123456789ABCDEF";
-  int l,i;
-  char *p, b[64];
-  for (l=0;len > 0 && l<16;l++) {
-    p = b;
-    for (i=0; i < 8 ; i++, --len) {
-	  if (len > 0) {
-	      *(p++) = h2c[(d[i] >> 4) & 0x0f];
-	      *(p++) = h2c[d[i] & 0x0f];
-	  }
-	  else {
-	      *(p++) = ' ';
-	      *(p++) = ' ';
-	  }
-      *(p++) = ' ';
-    }
-    *(p++) = '-';
-    *(p++) = ' ';
-	len += 8;
-    for (i=0; i < 8 ; i++, --len)
-		if (len > 0)
-			*(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.';
-		else
+void dump_data(char *str,unsigned char *d, int len) 
+{
+	static char h2c[] = "0123456789ABCDEF";
+	int l,i;
+	char *p, b[64];
+	for (l=0;len > 0 && l<16;l++) 
+	{
+		p = b;
+		for (i=0; i < 8 ; i++, --len) 
+		{
+			if (len > 0) 
+			{
+				*(p++) = h2c[(d[i] >> 4) & 0x0f];
+				*(p++) = h2c[d[i] & 0x0f];
+			}
+			else
+			{
+				*(p++) = ' ';
+				*(p++) = ' ';
+			}
 			*(p++) = ' ';
-    *p = '\000';
-    d += i;
-    printk(KERN_DEBUG"%s-%04X: %s\n",str,l*8,b);
-  }
-}
-
-void dump_addr(char *str,ipx_address *p) {
-  printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n",
-   str,ntohl(p->net),p->node[0],p->node[1],p->node[2],
-   p->node[3],p->node[4],p->node[5],ntohs(p->sock));
-}
-
-void dump_hdr(char *str,struct ipxhdr *p) {
-  printk(KERN_DEBUG"%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n",
-   str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize),
-   p->ipx_tctrl,p->ipx_tctrl,p->ipx_type);
-  dump_addr("  IPX-DST",&p->ipx_dest);
-  dump_addr("  IPX-SRC",&p->ipx_source);
-}
-
-void dump_pkt(char *str,struct ipxhdr *p) {
-  int len = ntohs(p->ipx_pktsize);
-  dump_hdr(str,p);
-  if (len > 30)
-	  dump_data(str,(unsigned char *)p + 30, len - 30);
+		}
+		*(p++) = '-';
+		*(p++) = ' ';
+		len += 8;
+		for (i=0; i < 8 ; i++, --len)
+		{
+			if (len > 0)
+				*(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.';
+			else
+				*(p++) = ' ';
+		}
+		*p = '\000';
+		d += i;
+		printk(KERN_DEBUG"%s-%04X: %s\n",str,l*8,b);
+	}
+}
+
+void dump_addr(char *str,ipx_address *p)
+{
+	printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n",
+		str,ntohl(p->net),p->node[0],p->node[1],p->node[2],
+		p->node[3],p->node[4],p->node[5],ntohs(p->sock));
+}
+
+void dump_hdr(char *str,struct ipxhdr *p) 
+{
+	printk(KERN_DEBUG"%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n",
+		str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize),
+		p->ipx_tctrl,p->ipx_tctrl,p->ipx_type);
+	dump_addr("  IPX-DST",&p->ipx_dest);
+	dump_addr("  IPX-SRC",&p->ipx_source);
+}
+
+void dump_pkt(char *str,struct ipxhdr *p) 
+{
+	int len = ntohs(p->ipx_pktsize);
+	dump_hdr(str,p);
+	if (len > 30)
+		dump_data(str,(unsigned char *)p + 30, len - 30);
 }
 #endif
 
@@ -2253,6 +2292,10 @@
 	return(copied);
 }
 
+/*
+ *	FIXME: We have to support shutdown really.
+ */
+ 
 static int ipx_shutdown(struct socket *sk,int how)
 {
 	return -EOPNOTSUPP;
@@ -2427,7 +2470,7 @@
 	proc_net_register(&ipx_rt_procinfo);
 #endif
 
-	printk(KERN_INFO "Swansea University Computer Society IPX 0.35 for NET3.037\n");
+	printk(KERN_INFO "Swansea University Computer Society IPX 0.38 for NET3.037\n");
 	printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
 }
 
@@ -2445,8 +2488,7 @@
  * sockets be closed from user space.
  */
 
-static void
-ipx_proto_finito(void)
+static void ipx_proto_finito(void)
 {	ipx_interface	*ifc;
 
 	while (ipx_interfaces) {
diff -u --recursive --new-file v2.1.19/linux/net/lapb/Makefile linux/net/lapb/Makefile
--- v2.1.19/linux/net/lapb/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/net/lapb/Makefile	Thu Jan  2 15:13:29 1997
@@ -0,0 +1,20 @@
+#
+# Makefile for the Linux LAPB layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+
+O_TARGET := lapb.o
+O_OBJS	 := lapb_in.o lapb_out.o lapb_subr.o lapb_timer.o
+M_OBJS   := $(O_TARGET)
+
+OX_OBJS  += lapb_iface.o
+
+include $(TOPDIR)/Rules.make
+
+tar:
+		tar -cvf /dev/f1 .
diff -u --recursive --new-file v2.1.19/linux/net/lapb/lapb_iface.c linux/net/lapb/lapb_iface.c
--- v2.1.19/linux/net/lapb/lapb_iface.c	Thu Jan  1 02:00:00 1970
+++ linux/net/lapb/lapb_iface.c	Thu Jan  2 15:13:29 1997
@@ -0,0 +1,409 @@
+/*
+ *	LAPB release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	LAPB 001	Jonathan Naylor	Started Coding
+ */
+ 
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <net/lapb.h>
+
+static lapb_cb *volatile lapb_list = NULL;
+
+/*
+ *	Free an allocated lapb control block. This is done to centralise
+ *	the MOD count code.
+ */
+static void lapb_free_cb(lapb_cb *lapb)
+{
+	del_timer(&lapb->timer);
+
+	kfree_s(lapb, sizeof(lapb_cb));
+
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ *	Socket removal during an interrupt is now safe.
+ */
+static void lapb_remove_cb(lapb_cb *lapb)
+{
+	lapb_cb *s;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	if ((s = lapb_list) == lapb) {
+		lapb_list = s->next;
+		restore_flags(flags);
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == lapb) {
+			s->next = lapb->next;
+			restore_flags(flags);
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Add a socket to the bound sockets list.
+ */
+static void lapb_insert_cb(lapb_cb *lapb)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	lapb->next = lapb_list;
+	lapb_list  = lapb;
+
+	restore_flags(flags);
+}
+
+/*
+ *	Convert the integer token used by the device driver into a pointer
+ *	to a LAPB control structure.
+ */
+lapb_cb *lapb_tokentostruct(void *token)
+{
+	lapb_cb *lapb;
+
+	for (lapb = lapb_list; lapb != NULL; lapb = lapb->next)
+		if (lapb->token == token)
+			return lapb;
+
+	return NULL;
+}
+
+/*
+ *	Create an empty LAPB control block.
+ */
+static lapb_cb *lapb_create_cb(void)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = (lapb_cb *)kmalloc(sizeof(*lapb), GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	MOD_INC_USE_COUNT;
+
+	memset(lapb, 0x00, sizeof(*lapb));
+
+	skb_queue_head_init(&lapb->write_queue);
+	skb_queue_head_init(&lapb->ack_queue);
+
+	init_timer(&lapb->timer);
+
+	lapb->t1      = LAPB_DEFAULT_T1;
+	lapb->t2      = LAPB_DEFAULT_T2;
+	lapb->n2      = LAPB_DEFAULT_N2;
+	lapb->mode    = LAPB_DEFAULT_MODE;
+	lapb->window  = LAPB_DEFAULT_WINDOW;
+	lapb->state   = LAPB_STATE_0;
+
+	return lapb;
+}
+
+int lapb_register(void *token, struct lapb_register_struct *callbacks)
+{
+	lapb_cb *lapb;
+
+	if (lapb_tokentostruct(token) != NULL)
+		return LAPB_BADTOKEN;
+	
+	if ((lapb = lapb_create_cb()) == NULL)
+		return LAPB_NOMEM;
+
+	lapb->token     = token;
+	lapb->callbacks = *callbacks;
+
+	lapb_insert_cb(lapb);
+		
+	return LAPB_OK;
+}
+
+int lapb_unregister(void *token)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	lapb_clear_queues(lapb);
+
+	lapb_remove_cb(lapb);
+
+	lapb_free_cb(lapb);
+		
+	return LAPB_OK;
+}
+
+int lapb_getparms(void *token, struct lapb_parms_struct *parms)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	parms->t1      = lapb->t1;
+	parms->t1timer = lapb->t1timer;
+	parms->t2      = lapb->t2;
+	parms->t2timer = lapb->t2timer;
+	parms->n2      = lapb->n2;
+	parms->n2count = lapb->n2count;
+	parms->state   = lapb->state;
+	parms->window  = lapb->window;
+	parms->mode    = lapb->mode;
+
+	return LAPB_OK;
+}
+
+int lapb_setparms(void *token, struct lapb_parms_struct *parms)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	if (parms->t1 < 1)
+		return LAPB_INVALUE;
+
+	if (parms->t2 < 1)
+		return LAPB_INVALUE;
+
+	if (parms->n2 < 1)
+		return LAPB_INVALUE;
+
+	if (lapb->state == LAPB_STATE_0) {
+		if (parms->mode & LAPB_EXTENDED) {
+			if (parms->window < 1 || parms->window > 127)
+				return LAPB_INVALUE;
+		} else {
+			if (parms->window < 1 || parms->window > 7)
+				return LAPB_INVALUE;
+		}
+	
+		lapb->mode   = parms->mode;
+		lapb->window = parms->window;
+
+		if (lapb->mode & LAPB_DCE) {
+			lapb_set_timer(lapb);
+		} else {
+			lapb->t1timer = 0;
+		}
+	}
+
+	lapb->t1    = parms->t1;
+	lapb->t2    = parms->t2;
+	lapb->n2    = parms->n2;
+
+	return LAPB_OK;
+}
+
+int lapb_connect_request(void *token)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	switch (lapb->state) {
+		case LAPB_STATE_1:
+			return LAPB_OK;
+		case LAPB_STATE_3:
+		case LAPB_STATE_4:
+			return LAPB_CONNECTED;
+	}
+
+	lapb_establish_data_link(lapb);
+
+#if LAPB_DEBUG > 0
+	printk(KERN_DEBUG "lapb: (%p) S0 -> S1\n", lapb->token);
+#endif
+
+	lapb->state = LAPB_STATE_1;
+
+	lapb_set_timer(lapb);
+
+	return LAPB_OK;
+}
+	
+int lapb_disconnect_request(void *token)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	switch (lapb->state) {
+		case LAPB_STATE_0:
+			return LAPB_NOTCONNECTED;
+
+		case LAPB_STATE_1:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->token);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
+#endif
+			lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
+			lapb->state   = LAPB_STATE_0;
+			lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+			return LAPB_NOTCONNECTED;
+
+		case LAPB_STATE_2:
+			return LAPB_OK;
+	}
+
+	lapb_clear_queues(lapb);
+	lapb->n2count = 0;
+	lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
+	lapb->t1timer = lapb->t1;
+	lapb->t2timer = 0;
+	lapb->state   = LAPB_STATE_2;
+
+#if LAPB_DEBUG > 1
+	printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->token);
+#endif
+#if LAPB_DEBUG > 0
+	printk(KERN_DEBUG "lapb: (%p) S3 -> S2\n", lapb->token);
+#endif
+
+	return LAPB_OK;
+}
+
+int lapb_data_request(void *token, struct sk_buff *skb)
+{
+	lapb_cb *lapb;
+
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4)
+		return LAPB_NOTCONNECTED;
+
+	skb_queue_tail(&lapb->write_queue, skb);
+
+	lapb_kick(lapb);
+
+	return LAPB_OK;
+}
+
+void lapb_connect_confirmation(lapb_cb *lapb, int reason)
+{
+	if (lapb->callbacks.connect_confirmation != NULL)
+		(lapb->callbacks.connect_confirmation)(lapb->token, reason);
+}
+
+void lapb_connect_indication(lapb_cb *lapb, int reason)
+{
+	if (lapb->callbacks.connect_indication != NULL)
+		(lapb->callbacks.connect_indication)(lapb->token, reason);
+}
+
+void lapb_disconnect_confirmation(lapb_cb *lapb, int reason)
+{
+	if (lapb->callbacks.disconnect_confirmation != NULL)
+		(lapb->callbacks.disconnect_confirmation)(lapb->token, reason);
+}
+
+void lapb_disconnect_indication(lapb_cb *lapb, int reason)
+{
+	if (lapb->callbacks.disconnect_indication != NULL)
+		(lapb->callbacks.disconnect_indication)(lapb->token, reason);
+}
+
+int lapb_data_indication(lapb_cb *lapb, struct sk_buff *skb)
+{
+	int used = 0;
+
+	if (lapb->callbacks.data_indication != NULL) {
+		(lapb->callbacks.data_indication)(lapb->token, skb);
+		used = 1;
+	}
+
+	return used;
+}
+
+int lapb_data_transmit(lapb_cb *lapb, struct sk_buff *skb)
+{
+	int used = 0;
+
+	if (lapb->callbacks.data_transmit != NULL) {
+		(lapb->callbacks.data_transmit)(lapb->token, skb);
+		used = 1;
+	}
+
+	return used;
+}
+
+EXPORT_SYMBOL(lapb_register);
+EXPORT_SYMBOL(lapb_unregister);
+EXPORT_SYMBOL(lapb_getparms);
+EXPORT_SYMBOL(lapb_setparms);
+EXPORT_SYMBOL(lapb_connect_request);
+EXPORT_SYMBOL(lapb_disconnect_request);
+EXPORT_SYMBOL(lapb_data_request);
+EXPORT_SYMBOL(lapb_data_received);
+
+void lapb_proto_init(struct net_proto *pro)
+{
+	printk(KERN_INFO "LAPB for Linux. Version 0.01 for Linux NET3.038 (Linux 2.1)\n");
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	lapb_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/net/lapb/lapb_in.c linux/net/lapb/lapb_in.c
--- v2.1.19/linux/net/lapb/lapb_in.c	Thu Jan  1 02:00:00 1970
+++ linux/net/lapb/lapb_in.c	Thu Jan  2 15:13:29 1997
@@ -0,0 +1,799 @@
+/*
+ *	LAPB release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	LAPB 001	Jonathan Naulor	Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/*
+ *	State machine for state 0, Disconnected State.
+ *	The handling of the timer(s) is in file lapb_timer.c.
+ */
+static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf)
+{
+	switch (frametype) {
+		case LAPB_SABM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb->state     = LAPB_STATE_3;
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+				lapb_connect_indication(lapb, LAPB_OK);
+			}
+			break;
+
+		case LAPB_SABME:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb->state     = LAPB_STATE_3;
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+				lapb_connect_indication(lapb, LAPB_OK);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			}
+			break;
+
+		case LAPB_DISC:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf);
+#endif
+			lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			break;
+
+		default:
+			break;
+	}
+
+	kfree_skb(skb, FREE_READ);
+}
+
+/*
+ *	State machine for state 1, Awaiting Connection State.
+ *	The handling of the timer(s) is in file lapb_timer.c.
+ */
+static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf)
+{
+	switch (frametype) {
+		case LAPB_SABM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			}
+			break;
+
+		case LAPB_SABME:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			}
+			break;
+
+		case LAPB_DISC:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf);
+#endif
+			lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			break;
+
+		case LAPB_UA:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", lapb->token, pf);
+#endif
+			if (pf) {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token);
+#endif
+				lapb->state     = LAPB_STATE_3;
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+				lapb_connect_confirmation(lapb, LAPB_OK);
+			}
+			break;
+
+		case LAPB_DM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", lapb->token, pf);
+#endif
+			if (pf) {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
+#endif
+				lapb_clear_queues(lapb);
+				lapb->state   = LAPB_STATE_0;
+				lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+				lapb->t2timer = 0;
+				lapb_disconnect_indication(lapb, LAPB_REFUSED);
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	kfree_skb(skb, FREE_READ);
+}
+
+/*
+ *	State machine for state 2, Awaiting Release State.
+ *	The handling of the timer(s) is in file lapb_timer.c
+ */
+static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf)
+{
+	switch (frametype) {
+		case LAPB_SABM:
+		case LAPB_SABME:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", lapb->token, pf);
+#endif
+			lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			break;
+
+		case LAPB_DISC:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", lapb->token, pf);
+#endif
+			lapb_send_control(lapb, LAPB_UA, pf, C_RESPONSE);
+			break;
+
+		case LAPB_UA:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", lapb->token, pf);
+#endif
+			if (pf) {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
+#endif
+				lapb->state   = LAPB_STATE_0;
+				lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+				lapb->t2timer = 0;
+				lapb_disconnect_confirmation(lapb, LAPB_OK);
+			}
+			break;
+
+		case LAPB_DM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, pf);
+#endif
+			if (pf) {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
+#endif
+				lapb->state   = LAPB_STATE_0;
+				lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+				lapb->t2timer = 0;
+				lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED);
+			}
+			break;
+
+		case LAPB_I:
+		case LAPB_REJ:
+		case LAPB_RNR:
+		case LAPB_RR:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, pf);
+#endif
+			if (pf) lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			break;
+				
+		default:
+			break;
+	}
+
+	kfree_skb(skb, FREE_READ);
+}
+
+/*
+ *	State machine for state 3, Connected State.
+ *	The handling of the timer(s) is in file lapb_timer.c
+ */
+static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+	int queued = 0;
+	int modulus;
+	
+	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+	switch (frametype) {
+		case LAPB_SABM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+			}
+			break;
+
+		case LAPB_SABME:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			}
+			break;
+
+		case LAPB_DISC:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
+#endif
+			lapb_clear_queues(lapb);
+			lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			lapb->state   = LAPB_STATE_0;
+			lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+			lapb->t2timer = 0;
+			lapb_disconnect_indication(lapb, LAPB_OK);
+			break;
+
+		case LAPB_DM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
+#endif
+			lapb_clear_queues(lapb);
+			lapb->state   = LAPB_STATE_0;
+			lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+			lapb->t2timer = 0;
+			lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED);
+			break;
+
+		case LAPB_RNR:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", lapb->token, pf, nr);
+#endif
+			lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION;
+			lapb_check_need_response(lapb, type, pf);
+			if (lapb_validate_nr(lapb, nr)) {
+				lapb_check_iframes_acked(lapb, nr);
+			} else {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+			}
+			break;
+			
+		case LAPB_RR:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", lapb->token, pf, nr);
+#endif
+			lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
+			lapb_check_need_response(lapb, type, pf);
+			if (lapb_validate_nr(lapb, nr)) {
+				lapb_check_iframes_acked(lapb, nr);
+			} else {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+			}
+			break;
+				
+		case LAPB_REJ:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", lapb->token, pf, nr);
+#endif
+			lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
+			lapb_check_need_response(lapb, type, pf);
+			if (lapb_validate_nr(lapb, nr)) {
+				lapb_frames_acked(lapb, nr);
+				lapb->t1timer = 0;
+				lapb_requeue_frames(lapb);
+			} else {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+			}
+			break;
+			
+		case LAPB_I:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", lapb->token, pf, ns, nr);
+#endif
+			if (type != LAPB_COMMAND)
+				break;
+			if (!lapb_validate_nr(lapb, nr)) {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+				break;
+			}
+			if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) {
+				lapb_frames_acked(lapb, nr);
+			} else {
+				lapb_check_iframes_acked(lapb, nr);
+			}
+			if (ns == lapb->vr) {
+				lapb->vr = (lapb->vr + 1) % modulus;
+				queued = lapb_data_indication(lapb, skb);
+				lapb->condition &= ~LAPB_REJECT_CONDITION;
+				if (pf) {
+					lapb_enquiry_response(lapb);
+				} else {
+					if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) {
+						lapb->t2timer = lapb->t2;
+						lapb->condition |= LAPB_ACK_PENDING_CONDITION;
+					}
+				}
+			} else {
+				if (lapb->condition & LAPB_REJECT_CONDITION) {
+					if (pf)
+						lapb_enquiry_response(lapb);
+				} else {
+#if LAPB_DEBUG > 1
+					printk(KERN_DEBUG "lapb: (%p) S3 TX REJ(%d) R%d\n", lapb->token, pf, lapb->vr);
+#endif
+					lapb->condition |= LAPB_REJECT_CONDITION;
+					lapb_send_control(lapb, LAPB_REJ, pf, LAPB_RESPONSE);
+					lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+				}
+			}
+			break;
+
+		case LAPB_FRMR:
+		case LAPB_ILLEGAL:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX {FRMR,ILLEGAL}(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+#endif
+			lapb_establish_data_link(lapb);
+			lapb->state = LAPB_STATE_1;
+			break;
+
+		default:
+			break;
+	}
+
+	if (!queued)
+		kfree_skb(skb, FREE_READ);
+}
+
+/*
+ *	State machine for state 4, Timer Recovery State.
+ *	The handling of the timer(s) is in file lapb_timer.c
+ */
+static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+	int queued = 0;
+	int modulus;
+
+	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+	switch (frametype) {
+		case LAPB_SABM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX SABM(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+			}
+			break;
+
+		case LAPB_SABME:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX SABME(%d)\n", lapb->token, pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			}
+			break;
+
+		case LAPB_DISC:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX DISC(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token);
+#endif
+			lapb_clear_queues(lapb);
+			lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			lapb->state   = LAPB_STATE_0;
+			lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+			lapb->t2timer = 0;
+			lapb_disconnect_indication(lapb, LAPB_OK);
+			break;
+
+		case LAPB_DM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX DM(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token);
+#endif
+			lapb_clear_queues(lapb);
+			lapb->state   = LAPB_STATE_0;
+			lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+			lapb->t2timer = 0;
+			lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED);
+			break;
+
+		case LAPB_RNR:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX RNR(%d) R%d\n", lapb->token, pf, nr);
+#endif
+			lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION;
+			if (type == LAPB_RESPONSE && pf) {
+				lapb->t1timer = 0;
+				if (lapb_validate_nr(lapb, nr)) {
+					lapb_frames_acked(lapb, nr);
+					if (lapb->vs == lapb->va) {
+#if LAPB_DEBUG > 0
+						printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+						lapb->n2count = 0;
+						lapb->state   = LAPB_STATE_3;
+					}
+				} else {
+#if LAPB_DEBUG > 0
+					printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+					lapb_nr_error_recovery(lapb);
+					lapb->state = LAPB_STATE_1;
+				}
+				break;
+			}
+			 
+			lapb_check_need_response(lapb, type, pf);
+			if (lapb_validate_nr(lapb, nr)) {
+				lapb_frames_acked(lapb, nr);
+			} else {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+			}
+			break;
+			
+		case LAPB_RR:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX RR(%d) R%d\n", lapb->token, pf, nr);
+#endif
+			lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
+			if (pf && type == LAPB_RESPONSE) {
+				lapb->t1timer = 0;
+				if (lapb_validate_nr(lapb, nr)) {
+					lapb_frames_acked(lapb, nr);
+					if (lapb->vs == lapb->va) {
+#if LAPB_DEBUG > 0
+						printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+						lapb->n2count = 0;
+						lapb->state   = LAPB_STATE_3;
+					} else {
+						lapb_requeue_frames(lapb);
+					}
+				} else {
+#if LAPB_DEBUG > 0
+					printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+					lapb_nr_error_recovery(lapb);
+					lapb->state = LAPB_STATE_1;
+				}
+				break;
+			}
+
+			lapb_check_need_response(lapb, type, pf);
+			if (lapb_validate_nr(lapb, nr)) {
+				lapb_frames_acked(lapb, nr);
+			} else {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+			}
+			break;
+
+		case LAPB_REJ:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX REJ(%d) R%d\n", lapb->token, pf, nr);
+#endif
+			lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
+			if (pf && type == LAPB_RESPONSE) {
+				lapb->t1timer = 0;
+				if (lapb_validate_nr(lapb, nr)) {
+					lapb_frames_acked(lapb, nr);
+					if (lapb->vs == lapb->va) {
+#if LAPB_DEBUG > 0
+						printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+						lapb->n2count = 0;
+						lapb->state   = LAPB_STATE_3;
+					} else {
+						lapb_requeue_frames(lapb);
+					}
+				} else {
+#if LAPB_DEBUG > 0
+					printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+					lapb_nr_error_recovery(lapb);
+					lapb->state = LAPB_STATE_1;
+				}
+				break;
+			}
+			
+			lapb_check_need_response(lapb, type, pf);	
+			if (lapb_validate_nr(lapb, nr)) {
+				lapb_frames_acked(lapb, nr);
+				if (lapb->vs != lapb->va)
+					lapb_requeue_frames(lapb);
+			} else {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+			}
+			break;
+
+		case LAPB_I:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX I(%d) S%d R%d\n", lapb->token, pf, ns, nr);
+#endif
+			if (type != LAPB_COMMAND)
+				break;
+			if (!lapb_validate_nr(lapb, nr)) {
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+				lapb_nr_error_recovery(lapb);
+				lapb->state = LAPB_STATE_1;
+				break;
+			}
+			lapb_frames_acked(lapb, nr);
+			if (ns == lapb->vr) {
+				lapb->vr = (lapb->vr + 1) % modulus;
+				queued = lapb_data_indication(lapb, skb);
+				lapb->condition &= ~LAPB_REJECT_CONDITION;
+				if (pf) {
+					lapb_enquiry_response(lapb);
+				} else {
+					if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) {
+						lapb->t2timer = lapb->t2;
+						lapb->condition |= LAPB_ACK_PENDING_CONDITION;
+					}
+				}
+			} else {
+				if (lapb->condition & LAPB_REJECT_CONDITION) {
+					if (pf)
+						lapb_enquiry_response(lapb);
+				} else {
+#if LAPB_DEBUG > 1
+					printk(KERN_DEBUG "lapb: (%p) S4 TX REJ(%d) R%d\n", lapb->token, pf, lapb->vr);
+#endif
+					lapb->condition |= LAPB_REJECT_CONDITION;
+					lapb_send_control(lapb, LAPB_REJ, pf, LAPB_RESPONSE);
+					lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+				}
+			}
+			break;
+		
+		case LAPB_FRMR:
+		case LAPB_ILLEGAL:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 TX {FRMR,ILLEGAL}(%d)\n", lapb->token, pf);
+#endif
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token);
+#endif
+			lapb_establish_data_link(lapb);
+			lapb->state = LAPB_STATE_1;
+			break;
+
+		default:
+			break;
+	}
+
+	if (!queued)
+		kfree_skb(skb, FREE_READ);
+}
+
+/*
+ *	Process an incoming LAPB frame
+ */
+int lapb_data_received(void *token, struct sk_buff *skb)
+{
+	int frametype, ns, nr, pf, type;
+	lapb_cb *lapb;
+	
+	if ((lapb = lapb_tokentostruct(token)) == NULL)
+		return LAPB_BADTOKEN;
+
+	del_timer(&lapb->timer);
+
+	frametype = lapb_decode(lapb, skb, &ns, &nr, &pf, &type);
+
+	switch (lapb->state) {
+		case LAPB_STATE_0:
+			lapb_state0_machine(lapb, skb, frametype, pf);
+			break;
+		case LAPB_STATE_1:
+			lapb_state1_machine(lapb, skb, frametype, pf);
+			break;
+		case LAPB_STATE_2:
+			lapb_state2_machine(lapb, skb, frametype, pf);
+			break;
+		case LAPB_STATE_3:
+			lapb_state3_machine(lapb, skb, frametype, ns, nr, pf, type);
+			break;
+		case LAPB_STATE_4:
+			lapb_state4_machine(lapb, skb, frametype, ns, nr, pf, type);
+			break;
+	}
+
+	lapb_set_timer(lapb);
+
+	return LAPB_OK;
+}
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/net/lapb/lapb_out.c linux/net/lapb/lapb_out.c
--- v2.1.19/linux/net/lapb/lapb_out.c	Thu Jan  1 02:00:00 1970
+++ linux/net/lapb/lapb_out.c	Thu Jan  2 15:13:29 1997
@@ -0,0 +1,252 @@
+/*
+ *	LAPB release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	LAPB 001	Jonathan Naylor	Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/* 
+ *  This procedure is passed a buffer descriptor for an iframe. It builds
+ *  the rest of the control part of the frame and then writes it out.
+ */
+static void lapb_send_iframe(lapb_cb *lapb, struct sk_buff *skb, int poll_bit)
+{
+	unsigned char *frame;
+
+	if (skb == NULL)
+		return;
+
+	if (lapb->mode & LAPB_EXTENDED) {
+		frame = skb_push(skb, 2);
+
+		frame[0] = I;
+		frame[0] |= (lapb->vs << 1);
+		frame[1] = (poll_bit) ? LAPB_EPF : 0;
+		frame[1] |= (lapb->vr << 1);
+	} else {
+		frame = skb_push(skb, 1);
+
+		*frame = I;
+		*frame |= (poll_bit) ? LAPB_SPF : 0;
+		*frame |= (lapb->vr << 5);
+		*frame |= (lapb->vs << 1);
+	}
+
+	lapb_transmit_buffer(lapb, skb, LAPB_COMMAND);	
+}
+
+void lapb_kick(lapb_cb *lapb)
+{
+	struct sk_buff *skb, *skbn;
+	int modulus, last = 1;
+	unsigned short start, end, next;
+
+	del_timer(&lapb->timer);
+
+	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+	start = (skb_peek(&lapb->ack_queue) == NULL) ? lapb->va : lapb->vs;
+	end   = (lapb->va + lapb->window) % modulus;
+
+	if (!(lapb->condition & PEER_RX_BUSY_CONDITION) &&
+	    start != end                                &&
+	    skb_peek(&lapb->write_queue) != NULL) {
+
+		lapb->vs = start;
+
+		/*
+		 * Dequeue the frame and copy it.
+		 */
+		skb  = skb_dequeue(&lapb->write_queue);
+
+		do {
+			if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+				skb_queue_head(&lapb->write_queue, skb);
+				break;
+			}
+
+			next = (lapb->vs + 1) % modulus;
+#ifdef notdef
+			last = (next == end) || skb_peek(&lapb->write_queue) == NULL;
+#else
+			last = (next == end);
+#endif
+			/*
+			 * Transmit the frame copy.
+			 */
+			lapb_send_iframe(lapb, skbn, POLLOFF);
+
+			lapb->vs = next;
+
+			/*
+			 * Requeue the original data frame.
+			 */
+			skb_queue_tail(&lapb->ack_queue, skb);
+#ifdef notdef
+		} while (!last);
+#else
+		} while (!last && (skb = skb_dequeue(&lapb->write_queue)) != NULL);
+#endif
+		lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+
+		if (lapb->t1timer == 0)
+			lapb->t1timer = lapb->t1;
+	}
+
+	lapb_set_timer(lapb);
+}
+
+void lapb_transmit_buffer(lapb_cb *lapb, struct sk_buff *skb, int type)
+{
+	unsigned char *ptr;
+
+	ptr = skb_push(skb, 1);
+
+	if (lapb->mode & LAPB_MLP) {
+		if (lapb->mode & LAPB_DCE) {
+			if (type == LAPB_COMMAND)
+				*ptr = LAPB_ADDR_C;
+			if (type == LAPB_RESPONSE)
+				*ptr = LAPB_ADDR_D;
+		} else {
+			if (type == LAPB_COMMAND)
+				*ptr = LAPB_ADDR_D;
+			if (type == LAPB_RESPONSE)
+				*ptr = LAPB_ADDR_C;
+		}
+	} else {
+		if (lapb->mode & LAPB_DCE) {
+			if (type == LAPB_COMMAND)
+				*ptr = LAPB_ADDR_A;
+			if (type == LAPB_RESPONSE)
+				*ptr = LAPB_ADDR_B;
+		} else {
+			if (type == LAPB_COMMAND)
+				*ptr = LAPB_ADDR_B;
+			if (type == LAPB_RESPONSE)
+				*ptr = LAPB_ADDR_A;
+		}
+	}
+
+#if LAPB_DEBUG > 2
+	printk(KERN_DEBUG "lapb: (%p) S%d TX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]);
+#endif
+
+	if (!lapb_data_transmit(lapb, skb))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+void lapb_nr_error_recovery(lapb_cb *lapb)
+{
+	lapb_establish_data_link(lapb);
+}
+
+void lapb_establish_data_link(lapb_cb *lapb)
+{
+	lapb->condition = 0x00;
+	lapb->n2count   = 0;
+
+	if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+		printk(KERN_DEBUG "lapb: (%p) S%d TX SABME(1)\n", lapb->token, lapb->state);
+#endif
+		lapb_send_control(lapb, SABME, POLLON, LAPB_COMMAND);
+	} else {
+#if LAPB_DEBUG > 1
+		printk(KERN_DEBUG "lapb: (%p) S%d TX SABM(1)\n", lapb->token, lapb->state);
+#endif
+		lapb_send_control(lapb, SABM, POLLON, LAPB_COMMAND);
+	}
+	
+	lapb->t2timer = 0;
+	lapb->t1timer = lapb->t1;
+}
+
+void lapb_transmit_enquiry(lapb_cb *lapb)
+{
+#if LAPB_DEBUG > 1
+	printk(KERN_DEBUG "lapb: (%p) S%d TX RR(1) R%d\n", lapb->token, lapb->state, lapb->vr);
+#endif
+
+	lapb_send_control(lapb, RR, POLLON, C_COMMAND);
+
+	lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+
+	lapb->t1timer = lapb->t1;
+}
+ 	
+void lapb_enquiry_response(lapb_cb *lapb)
+{
+#if LAPB_DEBUG > 1
+	printk(KERN_DEBUG "lapb: (%p) S%d TX RR(1) R%d\n", lapb->token, lapb->state, lapb->vr);
+#endif
+
+	lapb_send_control(lapb, RR, POLLON, LAPB_RESPONSE);
+
+	lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+}
+
+void lapb_timeout_response(lapb_cb *lapb)
+{
+#if LAPB_DEBUG > 1
+	printk(KERN_DEBUG "lapb: (%p) S%d TX RR(0) R%d\n", lapb->token, lapb->state, lapb->vr);
+#endif
+
+	lapb_send_control(lapb, RR, POLLOFF, LAPB_RESPONSE);
+
+	lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+}
+
+void lapb_check_iframes_acked(lapb_cb *lapb, unsigned short nr)
+{
+	if (lapb->vs == nr) {
+		lapb_frames_acked(lapb, nr);
+		lapb->t1timer = 0;
+	} else {
+		if (lapb->va != nr) {
+			lapb_frames_acked(lapb, nr);
+			lapb->t1timer = lapb->t1;
+		}
+	}
+}
+
+void lapb_check_need_response(lapb_cb *lapb, int type, int pf)
+{
+	if (type == LAPB_COMMAND && pf)
+		lapb_enquiry_response(lapb);
+}
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/net/lapb/lapb_subr.c linux/net/lapb/lapb_subr.c
--- v2.1.19/linux/net/lapb/lapb_subr.c	Thu Jan  1 02:00:00 1970
+++ linux/net/lapb/lapb_subr.c	Thu Jan  2 15:13:29 1997
@@ -0,0 +1,236 @@
+/*
+ *	LAPB release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	LAPB 001	Jonathan Naylor	Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/*
+ *	This routine purges all the queues of frames.
+ */
+void lapb_clear_queues(lapb_cb *lapb)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&lapb->write_queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+
+	while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+void lapb_frames_acked(lapb_cb *lapb, unsigned short nr)
+{
+	struct sk_buff *skb;
+	int modulus;
+
+	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+	/*
+	 * Remove all the ack-ed frames from the ack queue.
+	 */
+	if (lapb->va != nr) {
+		while (skb_peek(&lapb->ack_queue) != NULL && lapb->va != nr) {
+		        skb = skb_dequeue(&lapb->ack_queue);
+			kfree_skb(skb, FREE_WRITE);
+			lapb->va = (lapb->va + 1) % modulus;
+		}
+	}
+}
+
+void lapb_requeue_frames(lapb_cb *lapb)
+{
+        struct sk_buff *skb, *skb_prev = NULL;
+
+	/*
+	 * Requeue all the un-ack-ed frames on the output queue to be picked
+	 * up by lapb_kick called from the timer. This arrangement handles the
+	 * possibility of an empty output queue.
+	 */
+	while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL) {
+		if (skb_prev == NULL)
+			skb_queue_head(&lapb->write_queue, skb);
+		else
+			skb_append(skb_prev, skb);
+		skb_prev = skb;
+	}
+}
+
+/*
+ *	Validate that the value of nr is between va and vs. Return true or
+ *	false for testing.
+ */
+int lapb_validate_nr(lapb_cb *lapb, unsigned short nr)
+{
+	unsigned short vc = lapb->va;
+	int modulus;
+	
+	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+	while (vc != lapb->vs) {
+		if (nr == vc) return 1;
+		vc = (vc + 1) % modulus;
+	}
+	
+	if (nr == lapb->vs) return 1;
+
+	return 0;
+}
+
+/*
+ *	This routine is the centralised routine for parsing the control
+ *	information for the different frame formats.
+ */
+int lapb_decode(lapb_cb *lapb, struct sk_buff *skb, int *ns, int *nr, int *pf, int *type)
+{
+	int frametype = LAPB_ILLEGAL;
+
+	*ns = *nr = *pf = *type = 0;
+
+#if LAPB_DEBUG > 2
+	printk(KERN_DEBUG "lapb: (%p) S%d RX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]);
+#endif
+
+	if (lapb->mode & LAPB_MLP) {
+		if (lapb->mode & LAPB_DCE) {
+			if (skb->data[0] == LAPB_ADDR_C)
+				*type = LAPB_COMMAND;
+			if (skb->data[0] == LAPB_ADDR_D)
+				*type = LAPB_RESPONSE;
+		} else {
+			if (skb->data[0] == LAPB_ADDR_D)
+				*type = LAPB_COMMAND;
+			if (skb->data[0] == LAPB_ADDR_C)
+				*type = LAPB_RESPONSE;
+		}
+	} else {
+		if (lapb->mode & LAPB_DCE) {
+			if (skb->data[0] == LAPB_ADDR_A)
+				*type = LAPB_COMMAND;
+			if (skb->data[0] == LAPB_ADDR_B)
+				*type = LAPB_RESPONSE;
+		} else {
+			if (skb->data[0] == LAPB_ADDR_B)
+				*type = LAPB_COMMAND;
+			if (skb->data[0] == LAPB_ADDR_A)
+				*type = LAPB_RESPONSE;
+		}
+	}
+		
+	skb_pull(skb, 1);
+
+	if (lapb->mode & LAPB_EXTENDED) {
+		if ((skb->data[0] & LAPB_S) == 0) {
+			frametype = LAPB_I;			/* I frame - carries NR/NS/PF */
+			*ns = (skb->data[0] >> 1) & 0x7F;
+			*nr = (skb->data[1] >> 1) & 0x7F;
+			*pf = skb->data[1] & LAPB_EPF;
+			skb_pull(skb, 2);
+		} else if ((skb->data[0] & LAPB_U) == 1) { 	/* S frame - take out PF/NR */
+			frametype = skb->data[0] & 0x0F;
+			*nr = (skb->data[1] >> 1) & 0x7F;
+			*pf = skb->data[1] & LAPB_EPF;
+			skb_pull(skb, 2);
+		} else if ((skb->data[0] & LAPB_U) == 3) { 	/* U frame - take out PF */
+			frametype = skb->data[0] & ~LAPB_SPF;
+			*pf = skb->data[0] & LAPB_SPF;
+			skb_pull(skb, 1);
+		}
+	} else {
+		if ((skb->data[0] & LAPB_S) == 0) {
+			frametype = LAPB_I;			/* I frame - carries NR/NS/PF */
+			*ns = (skb->data[0] >> 1) & 0x07;
+			*nr = (skb->data[0] >> 5) & 0x07;
+			*pf = skb->data[0] & LAPB_SPF;
+		} else if ((skb->data[0] & LAPB_U) == 1) { 	/* S frame - take out PF/NR */
+			frametype = skb->data[0] & 0x0F;
+			*nr = (skb->data[0] >> 5) & 0x07;
+			*pf = skb->data[0] & LAPB_SPF;
+		} else if ((skb->data[0] & LAPB_U) == 3) { 	/* U frame - take out PF */
+			frametype = skb->data[0] & ~PF;
+			*pf = skb->data[0] & LAPB_SPF;
+		}
+
+		skb_pull(skb, 1);
+	}
+
+	return frametype;
+}
+
+/* 
+ *	This routine is called when the HDLC layer internally  generates a
+ *	command or  response  for  the remote machine ( eg. RR, UA etc. ). 
+ *	Only supervisory or unnumbered frames are processed.
+ */
+void lapb_send_control(lapb_cb *lapb, int frametype, int poll_bit, int type)
+{
+	struct sk_buff *skb;
+	unsigned char  *dptr;
+	
+	if ((skb = alloc_skb(LAPB_HEADER_LEN + 3, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb_reserve(skb, LAPB_HEADER_LEN + 1);
+
+	/* Assume a response - address structure for DTE */
+	if (lapb->mode & LAPB_EXTENDED) {
+		if ((frametype & LAPB_U) == LAPB_U) {
+			dptr = skb_put(skb, 1);
+			*dptr = frametype;
+			*dptr |= (poll_bit) ? LAPB_SPF : 0;
+		} else {
+			dptr = skb_put(skb, 2);
+			dptr[0] = frametype;
+			dptr[1] = (lapb->vr << 1);
+			dptr[1] |= (poll_bit) ? LAPB_EPF : 0;
+		}
+	} else {
+		dptr = skb_put(skb, 1);
+		*dptr = frametype;
+		*dptr |= (poll_bit) ? LAPB_SPF : 0;
+		if ((frametype & LAPB_U) == LAPB_S)		/* S frames carry NR */
+			*dptr |= (lapb->vr << 5);
+	}
+
+	lapb_transmit_buffer(lapb, skb, type);
+}
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/net/lapb/lapb_timer.c linux/net/lapb/lapb_timer.c
--- v2.1.19/linux/net/lapb/lapb_timer.c	Thu Jan  1 02:00:00 1970
+++ linux/net/lapb/lapb_timer.c	Thu Jan  2 15:13:29 1997
@@ -0,0 +1,183 @@
+/*
+ *	LAPB release 001
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	LAPB 001	Jonathan Naylor	Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+static void lapb_timer(unsigned long);
+
+/*
+ *	Linux set/reset timer routines
+ */
+void lapb_set_timer(lapb_cb *lapb)
+{
+	unsigned long flags;	
+
+	save_flags(flags);
+	cli();
+	del_timer(&lapb->timer);
+	restore_flags(flags);
+
+	lapb->timer.next     = lapb->timer.prev = NULL;	
+	lapb->timer.data     = (unsigned long)lapb;
+	lapb->timer.function = &lapb_timer;
+
+	lapb->timer.expires = jiffies + 10;
+	add_timer(&lapb->timer);
+}
+
+static void lapb_reset_timer(lapb_cb *lapb)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	del_timer(&lapb->timer);
+	restore_flags(flags);
+
+	lapb->timer.data     = (unsigned long)lapb;
+	lapb->timer.function = &lapb_timer;
+	lapb->timer.expires  = jiffies + 10;
+	add_timer(&lapb->timer);
+}
+
+/*
+ *	LAPB TIMER 
+ *
+ *	This routine is called every 100ms. Decrement timer by this
+ *	amount - if expired then process the event.
+ */
+static void lapb_timer(unsigned long param)
+{
+	lapb_cb *lapb = (lapb_cb *)param;
+
+	if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4)
+		lapb_kick(lapb);
+
+	if (lapb->t2timer > 0 && --lapb->t2timer == 0) {
+		if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4) {
+			if (lapb->condition & LAPB_ACK_PENDING_CONDITION) {
+				lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+				lapb_timeout_response(lapb);
+			}
+		}
+	}
+
+	if (lapb->t1timer == 0 || --lapb->t1timer > 0) {
+		lapb_reset_timer(lapb);
+		return;
+	}
+
+	switch (lapb->state) {
+		case LAPB_STATE_0:
+			if (lapb->mode & LAPB_DCE)
+				lapb_send_control(lapb, LAPB_DM, LAPB_POLLOFF, LAPB_RESPONSE);
+			break;
+		case LAPB_STATE_1: 
+			if (lapb->n2count == lapb->n2) {
+				lapb_clear_queues(lapb);
+				lapb->state   = LAPB_STATE_0;
+				lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+				lapb->t2timer = 0;
+				lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
+#endif
+			} else {
+				lapb->n2count++;
+				if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+					printk(KERN_DEBUG "lapb: (%p) S1 TX SABME(1)\n", lapb->token);
+#endif
+					lapb_send_control(lapb, LAPB_SABME, LAPB_POLLON, LAPB_COMMAND);
+				} else {
+#if LAPB_DEBUG > 1
+					printk(KERN_DEBUG "lapb: (%p) S1 TX SABM(1)\n", lapb->token);
+#endif
+					lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND);
+				}
+			}
+			break;
+
+		case LAPB_STATE_2:
+			if (lapb->n2count == lapb->n2) {
+				lapb_clear_queues(lapb);
+				lapb->state   = LAPB_STATE_0;
+				lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+				lapb->t2timer = 0;
+				lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
+#endif
+			} else {
+				lapb->n2count++;
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S2 TX DISC(1)\n", lapb->token);
+#endif
+				lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
+			}
+			break;
+
+		case LAPB_STATE_3: 
+			lapb->n2count = 1;
+			lapb_transmit_enquiry(lapb);
+			lapb->state   = LAPB_STATE_4;
+			break;
+
+		case LAPB_STATE_4:
+			if (lapb->n2count == lapb->n2) {
+				lapb_clear_queues(lapb);
+				lapb->state   = LAPB_STATE_0;
+				lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0;
+				lapb->t2timer = 0;
+				lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token);
+#endif
+			} else {
+				lapb->n2count++;
+				lapb_transmit_enquiry(lapb);
+			}
+			break;
+	}
+
+	lapb->t1timer = lapb->t1;
+
+	lapb_set_timer(lapb);
+}
+
+#endif
diff -u --recursive --new-file v2.1.19/linux/net/netbeui/netbeui.c linux/net/netbeui/netbeui.c
--- v2.1.19/linux/net/netbeui/netbeui.c	Mon Dec 30 15:39:17 1996
+++ linux/net/netbeui/netbeui.c	Thu Jan  2 15:13:29 1997
@@ -49,7 +49,7 @@
 *															*
 \***********************************************************************************************************************/
 
-static netbeui_socket *volatile netbeui_socket_list=NULL;
+static netbeui_socket *netbeui_socket_list=NULL;
 
 /*
  *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
@@ -57,91 +57,42 @@
  *	use this facility.
  */
 
-static void netbeui_remove_socket(netbeui_socket *sk)
+extern inline void netbeui_remove_socket(netbeui_socket *sk)
 {
-	unsigned long flags;
-	netbeui_socket *s;
-
-	save_flags(flags);
-	cli();
-
-	s=netbeui_socket_list;
-	if(s==sk)
-	{
-		netbeui_socket_list=s->next;
-		restore_flags(flags);
-		return;
-	}
-	while(s && s->next)
-	{
-		if(s->next==sk)
-		{
-			s->next=sk->next;
-			restore_flags(flags);
-			return;
-		}
-		s=s->next;
-	}
-	restore_flags(flags);
+	sklist_remove_socket(&netbeui_socket_list,sk);
 }
 
-static void netbeui_insert_socket(netbeui_socket *sk)
+extenr inline void netbeui_insert_socket(netbeui_socket *sk)
 {
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	sk->next=netbeui_socket_list;
+	sklist_insert_socket(&netbeui_socket_list,sk);
 	netbeui_socket_list=sk;
 	restore_flags(flags);
 }
 
-/*
- *	This is only called from user mode. Thus it protects itself against
- *	interrupt users but doesn't worry about being called during work.
- *	Once it is removed from the queue no interrupt or bottom half will
- *	touch it and we are (fairly 8-) ) safe.
- */
-
-static void netbeui_destroy_socket(netbeui_socket *sk);
-
-/*
- *	Handler for deferred kills.
- */
-
-static void netbeui_destroy_timer(unsigned long data)
-{
-	netbeui_destroy_socket((netbeui_socket *)data);
-}
-
 static void netbeui_destroy_socket(netbeui_socket *sk)
 {
-	struct sk_buff *skb;
-	netbeui_remove_socket(sk);
-
-	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+	/*
+	 *	Release netbios logical channels first
+	 */
+	if(sk->af_nb.nb_link)
 	{
-		kfree_skb(skb,FREE_READ);
+		netbeui_delete_channel(sk->af_nb.nb_link);
+		sk->af_nb.nb_link=NULL;
 	}
-
-	if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead)
+	if(sk->af_nb.src_name)
 	{
-		sk_free(sk);
-		MOD_DEC_USE_COUNT;
+		netbeui_release_name(sk->af_nb.src_name);
+		sk->af_nb.src_name=NULL;
 	}
-	else
+	if(sk->af_nb.dst_name)
 	{
-		/*
-		 *	Someone is using our buffers still.. defer
-		 */
-		init_timer(&sk->timer);
-		sk->timer.expires=jiffies+10*HZ;
-		sk->timer.function=netbeui_destroy_timer;
-		sk->timer.data = (unsigned long)sk;
-		add_timer(&sk->timer);
+		netbeui_release_name(sk->af_nb.dst_name);
+		sk->af_nb.dst_name=NULL;
 	}
+	netbeui_remove_listener(sk);
+	sklist_destroy_socket(&netbeui_socket,sk);
 }
 
-
 /*
  *	Called from proc fs
  */
@@ -161,14 +112,10 @@
 	for (s = netbeui_socket_list; s != NULL; s = s->next)
 	{
 		len += sprintf (buffer+len,"%02X   ", s->type);
-		len += sprintf (buffer+len,"%04X:%02X:%02X  ",
-			ntohs(s->protinfo.af_at.src_net),
-			s->protinfo.af_at.src_node,
-			s->protinfo.af_at.src_port);
-		len += sprintf (buffer+len,"%04X:%02X:%02X  ",
-			ntohs(s->protinfo.af_at.dest_net),
-			s->protinfo.af_at.dest_node,
-			s->protinfo.af_at.dest_port);
+		len += sprintf (buffer+len,"%s  ",
+			s->af_nb.src_name->text);
+		len += sprintf (buffer+len,"%s  ",
+			s->af_nb.dst_name->text);
 		len += sprintf (buffer+len,"%08X:%08X ", s->wmem_alloc, s->rmem_alloc);
 		len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
 
@@ -258,9 +205,6 @@
 			}
 			break;
 
-		case SOL_SOCKET:
-			return sock_setsockopt(sk,level,optname,optval,optlen);
-
 		default:
 			return -EOPNOTSUPP;
 	}
@@ -291,9 +235,6 @@
 			}
 			break;
 
-		case SOL_SOCKET:
-			return sock_getsockopt(sk,level,optname,optval,optlen);
-
 		default:
 			return -EOPNOTSUPP;
 	}
@@ -320,7 +261,7 @@
 		sk->backlog=128;
 	sk->state=TCP_LISTEN;
 	sk->state_change(sk);
-	netbeui_llc_listen(sk);
+	netbeui_add_listener(sk);
 	return 0;
 }
 
@@ -339,7 +280,16 @@
 	if(!sk->dead)
 	{
 		wake_up_interruptible(sk->sleep);
-		sock_wake_async(sk->socket,0);
+		sock_wake_async(sk->socket,1);
+	}
+}
+
+static void def_callback3(struct sock *sk, int len)
+{
+	if(!sk->dead)
+	{
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket,2);
 	}
 }
 
@@ -365,13 +315,6 @@
 			return(-ESOCKTNOSUPPORT);
 	}
 
-	sk->llc802=llc_alloc(GFP_KERNEL);
-	if(sk->llc802==NULL)
-	{
-		sk_free((void *)sk);
-		return -ENOBUFS:
-	}
-
 	MOD_INC_USE_COUNT;
 
 	sk->allocation=GFP_KERNEL;
@@ -395,7 +338,7 @@
 
 	sk->state_change=def_callback1;
 	sk->data_ready=def_callback2;
-	sk->write_space=def_callback1;
+	sk->write_space=def_callback3;
 	sk->error_report=def_callback1;
 	sk->zapped=1;
 	return(0);
@@ -435,21 +378,40 @@
 {
 	netbeui_socket *sk;
 	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+	int err;
 
 	sk=(netbeui_socket *)sock->data;
 
 	if(sk->zapped==0)
 		return(-EINVAL);
 
-	if(addr_len!=sizeof(struct sockaddr_at))
+	if(addr_len!=sizeof(struct sockaddr_netbeui))
 		return -EINVAL;
 
-	if(addr->sat_family!=AF_NETBEUI)
+	if(addr->snb_family!=AF_NETBEUI)
 		return -EAFNOSUPPORT;
 
-	if(netbeui_find_socket(addr)!=NULL)
-		return -EADDRINUSE;
-
+	/*
+	 *	This will sleep. To meet POSIX it is non interruptible.
+	 *	Someone should give the 1003.1g authors an injection of
+	 *	imagination...
+	 */
+	 
+	if(sk->af_nb.src_name!=NULL)
+		return -EINVAL;
+	
+	/*
+	 *	Try and get the name. It may return various 'invalid' name
+	 *	problem reports or EADDRINUSE if we or another node holds
+	 *	the desired name.
+	 */
+	 	
+	sk->af_nb.src_name=netbeui_alloc_name(addr, &err);
+	if(sk->af_nb.src_name==NULL)
+		return err;
+	/*
+	 *	Add us to the active socket list 
+	 */
 	netbeui_insert_socket(sk);
 	sk->zapped=0;
 	return(0);
@@ -463,28 +425,44 @@
 	size_t addr_len, int flags)
 {
 	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_netbeui *addr;
-
-	sk->state = TCP_CLOSE;
-	sock->state = SS_UNCONNECTED;
-
-	if(addr_len!=sizeof(*addr))
-		return(-EINVAL);
-	addr=(struct sockaddr_netbeui *)uaddr;
-
-	if(addr->sat_family!=AF_NETBEUI)
-		return -EAFNOSUPPORT;
-
-/* FIXME - Netbios broadcast semantics ?? */
-	if(addr->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
-		return -EACCES;
-
-	if(sk->zapped)
-		return -EINVAL;
-
-	if(atrtr_get_dev(&addr->sat_addr)==NULL)
-		return -ENETUNREACH;
+	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
 
+	/*
+	 *	Check pending operations
+	 */	
+	
+	if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING) 
+	{
+		sock->state==SS_CONNECTED;
+		return 0;
+	}
+	
+	if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING)
+	{
+		sock->state==SS_UNCONNECTED;
+		return -ECONNREFUSED;
+	}
+	
+	if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK))
+		return -EINPROGRESS;
+	
+	if(sk->state==TCP_ESTABLISHED)
+		return -EISCONN;	 
+	
+	/*
+	 *	If this is new it must really be new...
+	 */
+	 
+	if(sk->af_nb.dst_name==NULL)
+	{
+		if(addr_len != sizeof(struct sockaddr_nb))
+			return -EINVAL;
+		if(addr->snb_family!=AF_NETBEUI)
+			return -EAFNOSUPPORT;
+		/*
+		 *	Try and find the name
+		 */
+	}
 }
 
 /*
diff -u --recursive --new-file v2.1.19/linux/net/netbeui/netbeui_llc.c linux/net/netbeui/netbeui_llc.c
--- v2.1.19/linux/net/netbeui/netbeui_llc.c	Thu Dec 12 19:37:31 1996
+++ linux/net/netbeui/netbeui_llc.c	Thu Jan  2 15:13:29 1997
@@ -1,124 +1,169 @@
 /*
- *	Maintain 802.2 LLC logical channels being used by NetBEUI
+ *	NET3:	802.2 LLC supervisor for the netbeui protocols. 
+ *
+ *	The basic aim is to provide a self managing link layer supervisor
+ *	for netbeui. It creates and destroys the 802.2 virtual connections
+ *	as needed, and copes with the various races when a link goes down
+ *	just as its requested etc.
+ *
+ *	The upper layers are presented with the notion of an nb_link which
+ *	is a potentially shared object that represents a logical path 
+ *	between two hosts. Each nb_link has usage counts and users can
+ *	treat it as if its their own.
  */
+ 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/datalink.h>
+#include <net/p8022.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <net/llc.h>
+#include <net/netbeui.h>
 
 
-
-void netbeui_disc_indication(llcptr llc)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	if(nb->users>0)
-		llc_connect_request(&nb->llc);
-}
-
-void netbeui_disc_confirm(llcptr llc)
+/*
+ *	When this routine is called the netbeui layer has decided to
+ *	drop the link. There is a tiny risk that we might reuse the
+ *	link after we decide. Thus before we blast the link into little
+ *	tiny pieces we must check....
+ */
+ 
+static void netbeui_do_destroy(struct nb_link *nb)
 {
-	struct nb_link *nb=LLC_TO_NB(llc);
+	/*
+	 *	Are we wanted again. Bring it back. Sigh, wish people
+	 *	would make up their minds 8)
+	 */
 	if(nb->users>0)
-		llc_connect_request(&nb->llc);
-	else
 	{
-		netbeui_destroy_channel(nb);
+		nb->state=NETBEUI_CONNWAIT;
+		llc_connect_request(&nb->llc);
+		return;
 	}
+	/*
+	 *	Blam.... into oblivion it goes
+	 */
+	
+	llc_unregister(&nb->llc);
+	netbeui_free_link(nb);
 }
 
-void netbeui_connect_confirm(llcptr llc)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	nb->status=SS_CONNECTED;
-	wakeup(&nb->wait_queue);
-}
-
-void netbeui_connect_indication(llcptr llc)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	nb->status=SS_CONNECTED;
-	wakeup(&nb->wait_queue);
-}
-
-void netbeui_reset_confirm(llcptr llc)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	/* Good question .. */
-}
+/*
+ *	Handle netbeui events. Basically that means keep it up when it
+ *	should be up, down when it should be down and handle all the data.
+ */
 
-void netbeui_reset_indication(llcptr llc, char lr)
+static void netbeui_event(llcptr llc)
 {
-	struct nb_link *nb=LLC_TO_NB(llc);
-	printk("netbeui: 802.2LLC reset.\n");
-	/* Good question too */
-}
+	struct nb_link *nb=(struct nb_link *)llc;
+	
+	/*
+	 *	See what has occured
+	 */
 	 
-void netbeui_data_indication(llcptr llc, struct sk_buff *skb)
-{
-	netbeui_rcv_seq(LLC_TO_NB(llc),skb);
-}
-
-int netbeui_unit_data_indication(llcptr llc, struct sk_buff *skb)
-{
-	return netbeui_rcv_dgram(LLC_TO_NB(llc),skb);
-}
-
-int netbeui_xid_indication(llcptr llc, int ll, char *data)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	/* No action needed */
-}
+	if(llc->llc_callbacks&LLC_CONN_CONFIRM)
+	{
+		/*
+		 *	Link up if desired. Otherwise try frantically
+		 *	to close it.
+		 */
+		if(nb->state!=NETBEUI_DEADWAIT)
+		{
+			/*
+			 *	Wake pending writers
+			 */
+			nb->state=NETBEUI_OPEN;
+			netbeui_wakeup(nb);
+		}
+		else
+			llc_disconnect_request(llc);
+	}
+	
+	/*
+	 *	Data is passed to the upper netbeui layer
+	 */
 
-int netbeui_test_indication(llcptr llc, int ll, char *data)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	/* No action needed */
-}
+	if(llc->llc_callbacks&LLC_DATA_INDIC)
+		netbeu_rcv_stream(llc,llc->inc_skb);
 
-void netbeui_report_status(llcptr llc, char status)
-{
-	struct nb_link *nb=LLC_TO_NB(llc);
-	switch(status)
+	/*
+	 *	We got disconnected
+	 */
+	 
+	if(llc->llc_callbacks&LLC_DISC_INDICATION)
 	{
-		case FRMR_RECEIVED:
-		case FRMR_SENT:
-			printk("netbeui: FRMR event %d\n",status);
-			break;	/* FRMR's - shouldnt occur - debug log */
-		case REMOTE_BUSY:
-			nb->busy=1;
-			break;
-		case REMOTE_NOT_BUSY:
-			nb->busy=0;
-			wakeup(&nb->wakeup);
-			break;
-		default:
-			printk("LLC passed netbeui bogus state %d\n",status);
-			break;
+		if(nb->state==NETBEUI_DEADWAIT)
+		{
+			netbeui_do_destroy(nb);
+			return;
+		}
+		if(nb->state==NETBEUI_DISCWAIT)
+		{
+			llc_connect_request(llc);
+			nb->state=NETBEUI_CONNWAIT;
+		}
+	}
+	
+	if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM|
+					LLC_RST_CONFIRM))
+	{
+		/*
+		 *	Reset. 
+		 *	Q: Is tearing the link down the right answer ?
+		 *
+		 *	For now we just carry on
+		 */
 	}
-}
 
-struct llc_ops netbeui_ops=
-{
-	netbeui_data_indication,		/* Sequenced frame */
-	netbeui_unit_data_indication,		/* Datagrams */
-	netbeui_connect_indication,		/* They called us */
-	netbeui_connect_confirm,		/* We called them, they OK'd */
-	netbeui_data_connect_indication,	/* Erm ?????? */
-	netbeui_data_connect_confirm,		/* Erm ?????? */
-	netbeui_disc_indication,		/* They closed */
-	netbeui_disc_confirm,			/* We closed they OK'd */
-	netbeui_reset_confirm,			/* Our reset worked */
-	netbeui_reset_indication,		/* They reset on us */
-	netbeui_xid_indication,			/* An XID frame */
-	netbeui_test_indication,		/* A TEST frame */
-	netbeui_report_status			/* Link state change */
-};
+	if(llc->llc_callbacks&LLC_REMOTE_BUSY)
+		nb->busy=1;	/* Send no more for a bit */
+	if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY)
+	{
+		/* Coming unbusy may wake sending threads */
+		nb->busy=0;
+		netbeui_wakeup(nb);
+	}		
+	/*
+	 *	UI frames are passed to the upper netbeui layer.
+	 */
+	if(llc->llc_callbacks&LLC_UI_DATA)
+		netbeui_rcv_dgram(llc,llc->inc_skb);
+
+	/* We ignore TST, XID, FRMR stuff */
+	/* FIXME: We need to free frames here once I fix the callback! */
+}
 
 /*
- *	Create a new outgoing session
+ *	Netbeui has created a new logical link. As a result we will
+ *	need to find or create a suitable 802.2 LLC session and join
+ *	it.
  */
- 
+
 struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri)
 {
 	struct nb_link *nb=netbeui_find_channel(dev,remote_mac);
 	if(nb)
 	{
+		if(nb->state==NETBEUI_DEADWAIT)
+		{
+			/*
+			 *	We had commenced a final shutdown. We
+			 *	cannot abort that (we sent the packet) but
+			 *	we can shift the mode to DISCWAIT. That will
+			 *	cause the disconnect event to bounce us
+			 *	back into connected state.
+			 */
+			nb->state==NETBEUI_DISCWAIT;
+		}
 		nb->users++;
 		return nb;
 	}
@@ -131,19 +176,17 @@
 	 */
 	 
 	nb->dev=dev;
-	init_timer(&nb->timer);
-	nb->timer.function=netbeui_link_timer;
 	nb->users=1;
 	nb->busy=0;
 	nb->wakeup=NULL;
-	nb->status=SS_CONNECTING;
+	nb->state=NETBEUI_CONNWAIT;
 	memcpy(nb->remote_mac, remote_mac, ETH_ALEN);
 	
 	/*
 	 *	Now try and attach an LLC.
 	 */
 	
-	if(register_cl2llc_client(&nb->llc,dev->name,&nebeui_llcops,
+	if(register_cl2llc_client(&nb->llc,dev->name,netbeui_event,
 		remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0)
 	{
 		netbeui_free_link(nb);
@@ -165,21 +208,33 @@
 	 
 	return nb;
 }
+
+/*
+ *	A logical netbeui channel has died. If the channel has no
+ *	further users we commence shutdown.
+ */
 	
 int netbeui_delete_channel(struct nb_link *nb)
 {
 	nb->users--;
+	
+	/*
+	 *	FIXME: Must remove ourselves from the nb_link chain when
+	 *	we add that bit
+	 */
+	 
 	if(nb->users)
 		return 0;
 		
-	llc_disconnect_request(lp);
 	/*
 	 *	Ensure we drop soon. The disconnect confirm will let
-	 *	us fix the deletion
+	 *	us fix the deletion. If someone wants the link at
+	 *	the wrong moment nothing bad will occur. The create
+	 *	or the do_destroy will sort it.
 	 */
-	nb->state = SS_DISCONNECTING;
-	nb->timer.expires=jiffies+NB_DROP_TIMEOUT;
-	add_timer(&nb->timer);
+
+	nb->state = NETBEUI_DEADWAIT;
+	llc_disconnect_request(lp);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.19/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.19/linux/net/netrom/af_netrom.c	Wed Dec 18 15:59:15 1996
+++ linux/net/netrom/af_netrom.c	Thu Jan  2 15:13:29 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.15 or higher/ NET3.039
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -34,6 +34,7 @@
   
 #include <linux/config.h>
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -82,6 +83,37 @@
 
 static struct proto_ops nr_proto_ops;
 
+static void nr_free_sock(struct sock *sk)
+{
+	kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
+
+	sk_free(sk);
+	
+	MOD_DEC_USE_COUNT;
+}
+
+static struct sock *nr_alloc_sock(void)
+{
+	struct sock *sk;
+	nr_cb *nr;
+
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	memset(nr, 0x00, sizeof(*nr));
+
+	sk->protinfo.nr = nr;
+	nr->sk = sk;
+
+	return sk;
+}
 
 /*
  *	Socket removal during an interrupt is now safe.
@@ -283,8 +315,7 @@
 		sk->timer.data     = (unsigned long)sk;
 		add_timer(&sk->timer);
 	} else {
-		kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
-		sk_free(sk);
+		nr_free_sock(sk);
 	}
 
 	restore_flags(flags);
@@ -543,7 +574,19 @@
 static void def_callback2(struct sock *sk, int len)
 {
 	if (!sk->dead)
+	{
+		sock_wake_async(sk->socket,1);
 		wake_up_interruptible(sk->sleep);
+	}
+}
+
+static void def_callback3(struct sock *sk, int len)
+{
+	if (!sk->dead)
+	{
+		sock_wake_async(sk->socket,2);
+		wake_up_interruptible(sk->sleep);
+	}
 }
 
 static int nr_create(struct socket *sock, int protocol)
@@ -554,13 +597,10 @@
 	if (sock->type != SOCK_SEQPACKET || protocol != 0)
 		return -ESOCKTNOSUPPORT;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = nr_alloc_sock()) == NULL)
 		return -ENOMEM;
 
-	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
-		sk_free(sk);
-		return -ENOMEM;
-	}
+	nr = sk->protinfo.nr;
 
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -583,7 +623,7 @@
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
-	sk->write_space  = def_callback1;
+	sk->write_space  = def_callback3;
 	sk->error_report = def_callback1;
 
 	if (sock != NULL) {
@@ -595,8 +635,6 @@
 	skb_queue_head_init(&nr->reseq_queue);
 	skb_queue_head_init(&nr->frag_queue);
 
-	nr->my_index = 0;
-	nr->my_id    = 0;
 	nr->rtt      = sysctl_netrom_transport_timeout / 2;
 	nr->t1       = sysctl_netrom_transport_timeout;
 	nr->t2       = sysctl_netrom_transport_acknowledge_delay;
@@ -606,35 +644,8 @@
 	nr->paclen   = sysctl_netrom_transport_packet_length;
 	nr->window   = sysctl_netrom_transport_requested_window_size;
 
-	nr->t1timer   = 0;
-	nr->t2timer   = 0;
-	nr->t4timer   = 0;
-	nr->idletimer = 0;
-	nr->n2count   = 0;
-
-	nr->va       = 0;
-	nr->vr       = 0;
-	nr->vs       = 0;
-	nr->vl       = 0;
-
-	nr->your_index = 0;
-	nr->your_id    = 0;
-
-	nr->my_index   = 0;
-	nr->my_id      = 0;
-
 	nr->bpqext     = 1;
-	nr->fraglen    = 0;
-	nr->hdrincl    = 0;
 	nr->state      = NR_STATE_0;
-	nr->device     = NULL;
-
-	memset(&nr->source_addr, '\0', AX25_ADDR_LEN);
-	memset(&nr->user_addr,   '\0', AX25_ADDR_LEN);
-	memset(&nr->dest_addr,   '\0', AX25_ADDR_LEN);
-
-	nr->sk          = sk;
-	sk->protinfo.nr = nr;
 
 	return 0;
 }
@@ -647,13 +658,10 @@
 	if (osk->type != SOCK_SEQPACKET)
 		return NULL;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = nr_alloc_sock()) == NULL)
 		return NULL;
 
-	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
-		sk_free(sk);
-		return NULL;
-	}
+	nr = sk->protinfo.nr;
 
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -675,7 +683,7 @@
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
-	sk->write_space  = def_callback1;
+	sk->write_space  = def_callback3;
 	sk->error_report = def_callback1;
 
 	skb_queue_head_init(&nr->ack_queue);
@@ -694,21 +702,6 @@
 	nr->device   = osk->protinfo.nr->device;
 	nr->bpqext   = osk->protinfo.nr->bpqext;
 	nr->hdrincl  = osk->protinfo.nr->hdrincl;
-	nr->fraglen  = 0;
-
-	nr->t1timer   = 0;
-	nr->t2timer   = 0;
-	nr->t4timer   = 0;
-	nr->idletimer = 0;
-	nr->n2count   = 0;
-
-	nr->va       = 0;
-	nr->vr       = 0;
-	nr->vs       = 0;
-	nr->vl       = 0;
-	
-	sk->protinfo.nr = nr;
-	nr->sk          = sk;
 
 	return sk;
 }
@@ -1248,7 +1241,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	if (!sk->protinfo.nr->hdrincl) {
@@ -1413,7 +1406,7 @@
 	return(len);
 } 
 
-static struct net_proto_family netrom_family_ops = 
+static struct net_proto_family nr_family_ops = 
 {
 	AF_NETROM,
 	nr_create
@@ -1440,7 +1433,7 @@
 	nr_recvmsg
 };
 
-struct notifier_block nr_dev_notifier = {
+static struct notifier_block nr_dev_notifier = {
 	nr_device_event,
 	0
 };
@@ -1466,9 +1459,18 @@
 };
 #endif	
 
+static struct device dev_nr[] = {
+	{"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
+};
+
 void nr_proto_init(struct net_proto *pro)
 {
-	sock_register(&netrom_family_ops);
+	int i;
+
+	sock_register(&nr_family_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
 	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.034 Linux 2.1\n");
 
@@ -1477,6 +1479,9 @@
 	if (!ax25_linkfail_register(nr_link_failed))
 		printk(KERN_ERR "NET/ROM unable to register linkfail handler with AX.25\n");
 
+	for (i = 0; i < 4; i++)
+		register_netdev(&dev_nr[i]);
+
 	nr_register_sysctl();
 
 #ifdef CONFIG_PROC_FS
@@ -1485,5 +1490,46 @@
 	proc_net_register(&proc_net_nr_nodes);
 #endif	
 }
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	nr_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_NR);
+	proc_net_unregister(PROC_NET_NR_NEIGH);
+	proc_net_unregister(PROC_NET_NR_NODES);
+#endif
+	nr_rt_free();
+
+	ax25_protocol_release(AX25_P_NETROM);
+	ax25_linkfail_release(nr_link_failed);
+
+	unregister_netdevice_notifier(&nr_dev_notifier);
+
+	nr_unregister_sysctl();
+
+	sock_unregister(AF_NETROM);
+
+	for (i = 0; i < 4; i++) {
+		if (dev_nr[i].priv != NULL) {
+			kfree(dev_nr[i].priv);
+			dev_nr[i].priv = NULL;
+			unregister_netdev(&dev_nr[i]);
+		}
+	}
+}
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.1.19/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.1.19/linux/net/netrom/nr_dev.c	Mon Dec 30 15:39:17 1996
+++ linux/net/netrom/nr_dev.c	Thu Jan  2 15:13:29 1997
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 004
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
  *	releases, misbehave and/or generally screw up. It might even work.
  *
- *	This code REQUIRES 1.3.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -18,11 +18,11 @@
  *	NET/ROM 003	Jonathan(G4KLX)	Put nr_rebuild_header into line with
  *					ax25_rebuild_header
  *	NET/ROM 004	Jonathan(G4KLX)	Callsign registration with AX.25.
+ *	NET/ROM 006	Hans(PE1AYX)	Fixed interface to IP layer.
  */
 
 #include <linux/config.h>
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
-#include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -70,9 +70,11 @@
 	skb->protocol = htons(ETH_P_IP);
 
 	/* Spoof incoming device */
-	skb->dev = dev;
+	skb->dev      = dev;
+	skb->h.raw    = skb->data;
+	skb->nh.raw   = skb->data;
+	skb->pkt_type = PACKET_HOST;
 
-	skb->h.raw = skb->data;
 	ip_rcv(skb, skb->dev, NULL);
 
 	return 1;
@@ -169,8 +171,6 @@
 	dev->tbusy = 0;
 	dev->start = 1;
 
-	MOD_INC_USE_COUNT;
-
 	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
 	return 0;
@@ -183,8 +183,6 @@
 
 	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
 
-	MOD_DEC_USE_COUNT;
-
 	return 0;
 }
 
@@ -267,61 +265,5 @@
 
 	return 0;
 };
-
-#ifdef MODULE
-extern struct proto_ops nr_proto_ops;
-extern struct notifier_block nr_dev_notifier;
-
-static struct device dev_nr[] = {
-	{"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
-	{"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
-	{"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
-	{"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
-};
-
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	int i;
-
-	for (i = 0; i < 4; i++)
-		register_netdev(&dev_nr[i]);
-
-	nr_proto_init(NULL);
-
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	int i;
-
-#ifdef CONFIG_PROC_FS
-	proc_net_unregister(PROC_NET_NR);
-	proc_net_unregister(PROC_NET_NR_NEIGH);
-	proc_net_unregister(PROC_NET_NR_NODES);
-#endif
-	nr_rt_free();
-
-	ax25_protocol_release(AX25_P_NETROM);
-	ax25_linkfail_release(nr_link_failed);
-
-	unregister_netdevice_notifier(&nr_dev_notifier);
-
-	nr_unregister_sysctl();
-
-	sock_unregister(nr_proto_ops.family);
-
-	for (i = 0; i < 4; i++) {
-		if (dev_nr[i].priv != NULL) {
-			kfree(dev_nr[i].priv);
-			dev_nr[i].priv = NULL;
-			unregister_netdev(&dev_nr[i]);
-		}
-	}
-}
-
-#endif
 
 #endif
diff -u --recursive --new-file v2.1.19/linux/net/netrom/nr_in.c linux/net/netrom/nr_in.c
--- v2.1.19/linux/net/netrom/nr_in.c	Wed Dec 18 15:59:15 1996
+++ linux/net/netrom/nr_in.c	Thu Jan  2 15:13:29 1997
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 004
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- v2.1.19/linux/net/netrom/nr_out.c	Wed Dec 18 15:59:15 1996
+++ linux/net/netrom/nr_out.c	Thu Jan  2 15:13:29 1997
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 004
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.1.19/linux/net/netrom/nr_route.c	Tue Nov 12 15:56:16 1996
+++ linux/net/netrom/nr_route.c	Thu Jan  2 15:13:29 1997
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 004
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -20,6 +20,7 @@
  *					Change default quality for new neighbour when same
  *					as node callsign.
  *			Alan Cox(GW4PTS) Added the firewall hooks.
+ *	NET/ROM 006	Jonathan(G4KLX)	Added the setting of digipeated neighbours.
  */
  
 #include <linux/config.h>
@@ -100,14 +101,14 @@
 		nr_neigh->count    = 0;
 		nr_neigh->number   = nr_neigh_no++;
 
-		if (ax25_digi != NULL) {
+		if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
 			if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
 				kfree_s(nr_neigh, sizeof(*nr_neigh));
 				return -ENOMEM;
 			}
 			*nr_neigh->digipeat = *ax25_digi;
 		}
-			
+
 		save_flags(flags);
 		cli();
 
@@ -351,7 +352,7 @@
 /*
  *	Lock a neighbour with a quality.
  */
-static int nr_add_neigh(ax25_address *callsign, struct device *dev, unsigned int quality)
+static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct device *dev, unsigned int quality)
 {
 	struct nr_neigh *nr_neigh;
 	unsigned long flags;
@@ -375,6 +376,14 @@
 	nr_neigh->count    = 0;
 	nr_neigh->number   = nr_neigh_no++;
 
+	if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
+		if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
+			kfree_s(nr_neigh, sizeof(*nr_neigh));
+			return -ENOMEM;
+		}
+		*nr_neigh->digipeat = *ax25_digi;
+	}
+
 	save_flags(flags);
 	cli();
 			
@@ -556,6 +565,25 @@
 	return NULL;
 }
 
+static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
+{
+	static ax25_digi ax25_digi;
+	int i;
+
+	if (ndigis == 0)
+		return NULL;
+
+	for (i = 0; i < ndigis; i++) {
+		ax25_digi.calls[i]    = digipeaters[i];
+		ax25_digi.repeated[i] = 0;
+	}
+
+	ax25_digi.ndigi      = ndigis;
+	ax25_digi.lastrepeat = 0;
+
+	return &ax25_digi;
+}
+
 /*
  *	Handle the ioctls that control the routing functions.
  */
@@ -573,15 +601,19 @@
 			copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct));
 			if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
 				return -EINVAL;
+			if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS)
+				return -EINVAL;
 			switch (nr_route.type) {
 				case NETROM_NODE:
 					return nr_add_node(&nr_route.callsign,
 						nr_route.mnemonic,
 						&nr_route.neighbour,
-						NULL, dev, nr_route.quality,
+						nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+						dev, nr_route.quality,
 						nr_route.obs_count);
 				case NETROM_NEIGH:
 					return nr_add_neigh(&nr_route.callsign,
+						nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
 						dev, nr_route.quality);
 				default:
 					return -EINVAL;
@@ -750,19 +782,27 @@
 	int len     = 0;
 	off_t pos   = 0;
 	off_t begin = 0;
+	int i;
 
 	cli();
 
-	len += sprintf(buffer, "addr  callsign  dev  qual lock count\n");
+	len += sprintf(buffer, "addr  callsign  dev  qual lock count digipeaters\n");
 
 	for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
-		len += sprintf(buffer + len, "%05d %-9s %-4s  %3d    %d   %3d\n",
+		len += sprintf(buffer + len, "%05d %-9s %-4s  %3d    %d   %3d",
 			nr_neigh->number,
 			ax2asc(&nr_neigh->callsign),
 			nr_neigh->dev ? nr_neigh->dev->name : "???",
 			nr_neigh->quality,
 			nr_neigh->locked,
 			nr_neigh->count);
+
+		if (nr_neigh->digipeat != NULL) {
+			for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
+				len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i]));
+		}
+
+		len += sprintf(buffer + len, "\n");
 
 		pos = begin + len;
 
diff -u --recursive --new-file v2.1.19/linux/net/netrom/nr_subr.c linux/net/netrom/nr_subr.c
--- v2.1.19/linux/net/netrom/nr_subr.c	Wed Dec 18 15:59:15 1996
+++ linux/net/netrom/nr_subr.c	Thu Jan  2 15:13:29 1997
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 004
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/netrom/nr_timer.c linux/net/netrom/nr_timer.c
--- v2.1.19/linux/net/netrom/nr_timer.c	Thu Dec 12 17:02:48 1996
+++ linux/net/netrom/nr_timer.c	Thu Jan  2 15:13:29 1997
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 004
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/protocols.c linux/net/protocols.c
--- v2.1.19/linux/net/protocols.c	Thu Dec 12 19:37:31 1996
+++ linux/net/protocols.c	Thu Jan  2 15:13:29 1997
@@ -33,6 +33,10 @@
 #include <net/x25call.h>
 #endif
 
+#ifdef CONFIG_LAPB
+#include <net/lapbcall.h>
+#endif
+
 #ifdef CONFIG_AX25
 #include <net/ax25call.h>
 #ifdef CONFIG_NETROM
@@ -117,6 +121,10 @@
 
 #ifdef CONFIG_ATALK
   { "DDP",	atalk_proto_init },			/* Netatalk Appletalk driver	*/
+#endif
+
+#ifdef CONFIG_LAPB
+  { "LAPB",     lapb_proto_init },			/* LAPB protocols */
 #endif
 
 #ifdef CONFIG_X25
diff -u --recursive --new-file v2.1.19/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.19/linux/net/rose/af_rose.c	Sun Dec 22 16:37:44 1996
+++ linux/net/rose/af_rose.c	Thu Jan  2 15:13:29 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -21,6 +21,7 @@
   
 #include <linux/config.h>
 #if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -62,10 +63,10 @@
 
 static unsigned int lci = 1;
 
-struct proto_ops rose_proto_ops;
-
 static struct sock *volatile rose_list = NULL;
 
+static struct proto_ops rose_proto_ops;
+
 /*
  *	Convert a Rose address into text.
  */
@@ -127,6 +128,38 @@
 	return 0;
 }
 
+static void rose_free_sock(struct sock *sk)
+{
+	kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
+
+	sk_free(sk);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static struct sock *rose_alloc_sock(void)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+	
+	memset(rose, 0x00, sizeof(*rose));
+
+	sk->protinfo.rose = rose;
+	rose->sk = sk;
+
+	return sk;
+}
+
 /*
  *	Socket removal during an interrupt is now safe.
  */
@@ -328,8 +361,7 @@
 		sk->timer.data     = (unsigned long)sk;
 		add_timer(&sk->timer);
 	} else {
-		kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
-		sk_free(sk);
+		rose_free_sock(sk);
 	}
 
 	restore_flags(flags);
@@ -593,7 +625,18 @@
 static void def_callback2(struct sock *sk, int len)
 {
 	if (!sk->dead)
+	{
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket,1);
+	}
+}
+static void def_callback3(struct sock *sk, int len)
+{
+	if (!sk->dead)
+	{
 		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket,2); 
+	}		
 }
 
 static int rose_create(struct socket *sock, int protocol)
@@ -604,13 +647,10 @@
 	if (sock->type != SOCK_SEQPACKET || protocol != 0)
 		return -ESOCKTNOSUPPORT;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = rose_alloc_sock()) == NULL)
 		return -ENOMEM;
 
-	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
-		sk_free(sk);
-		return -ENOMEM;
-	}
+	rose = sk->protinfo.rose;
 
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -643,39 +683,13 @@
 	skb_queue_head_init(&rose->ack_queue);
 	skb_queue_head_init(&rose->frag_queue);
 
-	rose->lci      = 0;
+	rose->t1    = sysctl_rose_call_request_timeout;
+	rose->t2    = sysctl_rose_reset_request_timeout;
+	rose->t3    = sysctl_rose_clear_request_timeout;
+	rose->hb    = sysctl_rose_ack_hold_back_timeout;
+	rose->idle  = sysctl_rose_no_activity_timeout;
 
-	rose->t1       = sysctl_rose_call_request_timeout;
-	rose->t2       = sysctl_rose_reset_request_timeout;
-	rose->t3       = sysctl_rose_clear_request_timeout;
-	rose->hb       = sysctl_rose_ack_hold_back_timeout;
-	rose->idle     = sysctl_rose_no_activity_timeout;
-
-	rose->timer    = 0;
-
-	rose->va       = 0;
-	rose->vr       = 0;
-	rose->vs       = 0;
-	rose->vl       = 0;
-
-	rose->fraglen    = 0;
-	rose->hdrincl    = 0;
-	rose->state      = ROSE_STATE_0;
-	rose->neighbour  = NULL;
-	rose->device     = NULL;
-
-	rose->source_ndigis = 0;
-	rose->dest_ndigis   = 0;
-
-	memset(&rose->source_addr, '\0', ROSE_ADDR_LEN);
-	memset(&rose->dest_addr,   '\0', ROSE_ADDR_LEN);
-	memset(&rose->source_call, '\0', AX25_ADDR_LEN);
-	memset(&rose->dest_call,   '\0', AX25_ADDR_LEN);
-	memset(&rose->source_digi, '\0', AX25_ADDR_LEN);
-	memset(&rose->dest_digi,   '\0', AX25_ADDR_LEN);
-
-	rose->sk          = sk;
-	sk->protinfo.rose = rose;
+	rose->state = ROSE_STATE_0;
 
 	return 0;
 }
@@ -688,13 +702,10 @@
 	if (osk->type != SOCK_SEQPACKET)
 		return NULL;
 
-	if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = rose_alloc_sock()) == NULL)
 		return NULL;
 
-	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
-		sk_free(sk);
-		return NULL;
-	}
+	rose = sk->protinfo.rose;
 
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -722,25 +733,14 @@
 	skb_queue_head_init(&rose->ack_queue);
 	skb_queue_head_init(&rose->frag_queue);
 
-	rose->t1       = osk->protinfo.rose->t1;
-	rose->t2       = osk->protinfo.rose->t2;
-	rose->t3       = osk->protinfo.rose->t3;
-	rose->hb       = osk->protinfo.rose->hb;
-	rose->idle     = osk->protinfo.rose->idle;
-
-	rose->device   = osk->protinfo.rose->device;
-	rose->hdrincl  = osk->protinfo.rose->hdrincl;
-	rose->fraglen  = 0;
-
-	rose->timer    = 0;
-
-	rose->va       = 0;
-	rose->vr       = 0;
-	rose->vs       = 0;
-	rose->vl       = 0;
-	
-	sk->protinfo.rose = rose;
-	rose->sk          = sk;
+	rose->t1      = osk->protinfo.rose->t1;
+	rose->t2      = osk->protinfo.rose->t2;
+	rose->t3      = osk->protinfo.rose->t3;
+	rose->hb      = osk->protinfo.rose->hb;
+	rose->idle    = osk->protinfo.rose->idle;
+
+	rose->device  = osk->protinfo.rose->device;
+	rose->hdrincl = osk->protinfo.rose->hdrincl;
 
 	return sk;
 }
@@ -1241,7 +1241,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	if (!sk->protinfo.rose->hdrincl) {
@@ -1419,12 +1419,12 @@
 	return(len);
 } 
 
-struct net_proto_family rose_family_ops = {
+static struct net_proto_family rose_family_ops = {
 	AF_ROSE,
 	rose_create
 };
 
-struct proto_ops rose_proto_ops = {
+static struct proto_ops rose_proto_ops = {
 	AF_ROSE,
 	
 	rose_dup,
@@ -1445,51 +1445,113 @@
 	rose_recvmsg
 };
 
-struct notifier_block rose_dev_notifier = {
+static struct notifier_block rose_dev_notifier = {
 	rose_device_event,
 	0
 };
 
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_rose = {
+	PROC_NET_RS, 4, "rose",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_get_info
+};
+static struct proc_dir_entry proc_net_rose_neigh = {
+	PROC_NET_RS_NEIGH, 10, "rose_neigh",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_neigh_get_info
+};
+static struct proc_dir_entry proc_net_rose_nodes = {
+	PROC_NET_RS_NODES, 10, "rose_nodes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_nodes_get_info
+};
+static struct proc_dir_entry proc_net_rose_routes = {
+	PROC_NET_RS_ROUTES, 11, "rose_routes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_routes_get_info
+};
+#endif	
+
+static struct device dev_rose[] = {
+	{"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
+};
+
 void rose_proto_init(struct net_proto *pro)
 {
+	int i;
+
 	sock_register(&rose_family_ops);
 	register_netdevice_notifier(&rose_dev_notifier);
-	printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.034 Linux 2.1\n");
+	printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.035 Linux 2.1\n");
 
 	if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame))
 		printk(KERN_ERR "Rose unable to register protocol with AX.25\n");
 	if (!ax25_linkfail_register(rose_link_failed))
 		printk(KERN_ERR "Rose unable to register linkfail handler with AX.25\n");
 
+	for (i = 0; i < 6; i++)
+		register_netdev(&dev_rose[i]);
+
 	rose_register_sysctl();
 
 #ifdef CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RS, 4, "rose",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		rose_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RS_NEIGH, 10, "rose_neigh",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		rose_neigh_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RS_NODES, 10, "rose_nodes",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		rose_nodes_get_info
-	});
-
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RS_ROUTES, 11, "rose_routes",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		rose_routes_get_info
-	});
+	proc_net_register(&proc_net_rose);
+	proc_net_register(&proc_net_rose_neigh);
+	proc_net_register(&proc_net_rose_nodes);
+	proc_net_register(&proc_net_rose_routes);
 #endif	
 }
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	rose_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_RS);
+	proc_net_unregister(PROC_NET_RS_NEIGH);
+	proc_net_unregister(PROC_NET_RS_NODES);
+	proc_net_unregister(PROC_NET_RS_ROUTES);
+#endif
+	rose_rt_free();
+
+	ax25_protocol_release(AX25_P_ROSE);
+	ax25_linkfail_release(rose_link_failed);
+
+	rose_unregister_sysctl();
+
+	unregister_netdevice_notifier(&rose_dev_notifier);
+
+	sock_unregister(AF_ROSE);
+
+	for (i = 0; i < 6; i++) {
+		if (dev_rose[i].priv != NULL) {
+			kfree(dev_rose[i].priv);
+			dev_rose[i].priv = NULL;
+			unregister_netdev(&dev_rose[i]);
+		}
+	}
+}
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.1.19/linux/net/rose/rose_dev.c	Mon Dec 30 15:39:18 1996
+++ linux/net/rose/rose_dev.c	Thu Jan  2 15:13:29 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
  *	releases, misbehave and/or generally screw up. It might even work.
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -14,11 +14,11 @@
  *
  *	History
  *	Rose 001	Jonathan(G4KLX)	Cloned from nr_dev.c.
+ *			Hans(PE1AYX)	Fixed interface to IP layer.
  */
 
 #include <linux/config.h>
 #if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
-#include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -66,9 +66,11 @@
 	skb->protocol = htons(ETH_P_IP);
 
 	/* Spoof incoming device */
-	skb->dev = dev;
+	skb->dev      = dev;
+	skb->h.raw    = skb->data;
+	skb->nh.raw   = skb->data;
+	skb->pkt_type = PACKET_HOST;
 
-	skb->h.raw = skb->data;
 	ip_rcv(skb, skb->dev, NULL);
 
 	return 1;
@@ -141,8 +143,6 @@
 	dev->tbusy = 0;
 	dev->start = 1;
 
-	MOD_INC_USE_COUNT;
-
 	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
 	return 0;
@@ -155,8 +155,6 @@
 
 	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
 
-	MOD_DEC_USE_COUNT;
-
 	return 0;
 }
 
@@ -239,58 +237,5 @@
 
 	return 0;
 };
-
-#ifdef MODULE
-extern struct proto_ops rose_proto_ops;
-extern struct notifier_block rose_dev_notifier;
-
-static struct device dev_rose[] = {
-	{"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
-};
-
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	register_netdev(&dev_rose[0]);
-	register_netdev(&dev_rose[1]);
-
-	rose_proto_init(NULL);
-
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	int i;
-
-#ifdef CONFIG_PROC_FS
-	proc_net_unregister(PROC_NET_RS);
-	proc_net_unregister(PROC_NET_RS_NEIGH);
-	proc_net_unregister(PROC_NET_RS_NODES);
-	proc_net_unregister(PROC_NET_RS_ROUTES);
-#endif
-	rose_rt_free();
-
-	ax25_protocol_release(AX25_P_ROSE);
-	ax25_linkfail_release(rose_link_failed);
-
-	rose_unregister_sysctl();
-
-	unregister_netdevice_notifier(&rose_dev_notifier);
-
-	sock_unregister(rose_proto_ops.family);
-
-	for (i = 0; i < 2; i++) {
-		if (dev_rose[i].priv != NULL) {
-			kfree(dev_rose[i].priv);
-			dev_rose[i].priv = NULL;
-			unregister_netdev(&dev_rose[i]);
-		}
-	}
-}
-
-#endif
 
 #endif
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_in.c linux/net/rose/rose_in.c
--- v2.1.19/linux/net/rose/rose_in.c	Wed Dec 18 15:59:15 1996
+++ linux/net/rose/rose_in.c	Thu Jan  2 15:13:29 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.1.19/linux/net/rose/rose_link.c	Wed Dec 18 15:59:15 1996
+++ linux/net/rose/rose_link.c	Thu Jan  2 15:13:29 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_out.c linux/net/rose/rose_out.c
--- v2.1.19/linux/net/rose/rose_out.c	Sun Dec 22 16:37:44 1996
+++ linux/net/rose/rose_out.c	Thu Jan  2 15:13:35 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.1.19/linux/net/rose/rose_route.c	Sun Dec 22 16:38:15 1996
+++ linux/net/rose/rose_route.c	Thu Jan  2 15:13:35 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- v2.1.19/linux/net/rose/rose_subr.c	Thu Dec 12 19:37:32 1996
+++ linux/net/rose/rose_subr.c	Thu Jan  2 15:13:35 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/rose/rose_timer.c linux/net/rose/rose_timer.c
--- v2.1.19/linux/net/rose/rose_timer.c	Wed Dec 18 15:59:15 1996
+++ linux/net/rose/rose_timer.c	Thu Jan  2 15:13:35 1997
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
diff -u --recursive --new-file v2.1.19/linux/net/socket.c linux/net/socket.c
--- v2.1.19/linux/net/socket.c	Mon Dec 30 15:39:18 1996
+++ linux/net/socket.c	Thu Jan  2 15:13:35 1997
@@ -44,16 +44,15 @@
  *
  *
  *	This module is effectively the top level interface to the BSD socket
- *	paradigm. Because it is very simple it works well for Unix domain sockets,
- *	but requires a whole layer of substructure for the other protocols.
- *
- *	In addition it lacks an effective kernel -> kernel interface to go with
- *	the user one.
+ *	paradigm. 
  *
  * PROBLEMS:
  *	- CLONE_FILES. Big problem, cloned thread can close file,
  *	  while other thread sleeps in kernel. It can be solved
  *	  by increasing f_count and releasing it on exit from syscall.
+ *	  _HAS_ to be fixed before 2.2 is released. I assume whoever is
+ *	  working on the CLONE stuff will fix that pile of accidents. If
+ *	  you find this comment in a 2.2-preXXX kernel scream loudly.
  *
  */
 
@@ -130,11 +129,13 @@
 /*
  *	The protocol list. Each protocol is registered in here.
  */
+
 static struct net_proto_family *net_families[NPROTO];
 
 /*
  *	Statistics counters of the socket lists
  */
+
 static int sockets_in_use  = 0;
 
 /*
@@ -215,7 +216,7 @@
 	return fd;
 }
 
-static __inline__ struct socket *socki_lookup(struct inode *inode)
+extern __inline__ struct socket *socki_lookup(struct inode *inode)
 {
 	return &inode->u.socket_i;
 }
@@ -462,12 +463,13 @@
 void sock_close(struct inode *inode, struct file *filp)
 {
 	/*
-	 * It's possible the inode is NULL if we're closing an unfinished socket. 
+	 *	It was possible the inode is NULL we were 
+	 *	closing an unfinished socket. 
 	 */
 
 	if (!inode)
 	{
-		printk(KERN_DEBUG "Nope. It is impossible!\n");
+		printk(KERN_DEBUG "sock_close: NULL inode\n");
 		return;
 	}
 	sock_fasync(inode, filp, 0);
@@ -617,8 +619,8 @@
 	if (!(sock = sock_alloc())) 
 	{
 		printk(KERN_WARNING "socket: no more sockets\n");
-		return(-ENOSR);	/* Was: EAGAIN, but we are out of
-				   system resources! */
+		return(-ENFILE);	/* Not exactly a match, but its the
+					   closest posix thing */
 	}
 
 	sock->type = type;
@@ -752,7 +754,7 @@
  *	space and move it to user at the very end. This is unclean because
  *	we open the socket then return an error.
  *
- *	1003.1g addcs the ability to recvmsg() to query connection pending
+ *	1003.1g adds the ability to recvmsg() to query connection pending
  *	status to recvmsg. We need to add that support in a way thats
  *	clean when we restucture accept also.
  */
@@ -1212,9 +1214,10 @@
 		return -EINVAL;
 
 	/*
-	 * save the user-mode address (verify_iovec will change the
-	 * kernel msghdr to use the kernel address space)
+	 *	Save the user-mode address (verify_iovec will change the
+	 *	kernel msghdr to use the kernel address space)
 	 */
+	 
 	uaddr = msg_sys.msg_name;
 	uaddr_len = &msg->msg_namelen;
 	err=verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
diff -u --recursive --new-file v2.1.19/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.19/linux/net/unix/af_unix.c	Sun Dec 22 16:38:16 1996
+++ linux/net/unix/af_unix.c	Thu Jan  2 15:40:57 1997
@@ -1,7 +1,7 @@
 /*
  * NET3:	Implementation of BSD Unix domain sockets.
  *
- * Authors:	Alan Cox, <alan@cymru.net>
+ * Authors:	Alan Cox, <alan.cox@linux.org>
  *
  *		Currently this contains all but the file descriptor passing code.
  *		Before that goes in the odd bugs in the iovec handlers need 
@@ -50,8 +50,6 @@
  *	Bug fixes and improvements.
  *		- client shutdown killed server socket.
  *		- removed all useless cli/sti pairs.
- *		- (suspicious!) not allow connect/send to connected not to us
- *		  socket, return EPERM.
  *
  *	Semantic changes/extensions.
  *		- generic control message passing.
@@ -100,7 +98,7 @@
 
 #define UNIX_ABSTRACT(sk)	((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE)
 
-static __inline__ unsigned unix_hash_fold(unsigned hash)
+extern __inline__ unsigned unix_hash_fold(unsigned hash)
 {
 	hash ^= hash>>16;
 	hash ^= hash>>8;
@@ -110,32 +108,32 @@
 
 #define unix_peer(sk) ((sk)->pair)
 
-static __inline__ int unix_our_peer(unix_socket *sk, unix_socket *osk)
+extern __inline__ int unix_our_peer(unix_socket *sk, unix_socket *osk)
 {
 	return unix_peer(osk) == sk;
 }
 
-static __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk)
+extern __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk)
 {
-	return !unix_peer(osk) || unix_peer(osk) == sk;
+	return (sk->type==osk->type);
 }
 
-static __inline__ void unix_lock(unix_socket *sk)
+extern __inline__ void unix_lock(unix_socket *sk)
 {
 	sk->users++;
 }
 
-static __inline__ int unix_unlock(unix_socket *sk)
+extern __inline__ int unix_unlock(unix_socket *sk)
 {
 	return sk->users--;
 }
 
-static __inline__ int unix_locked(unix_socket *sk)
+extern __inline__ int unix_locked(unix_socket *sk)
 {
 	return sk->users;
 }
 
-static __inline__ void unix_release_addr(struct unix_address *addr)
+extern __inline__ void unix_release_addr(struct unix_address *addr)
 {
 	if (addr)
 	{
@@ -300,10 +298,6 @@
 	}
 }
 
-/*
- *	Fixme: We need async I/O on AF_UNIX doing next.
- */
- 
 static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	return -EINVAL;
@@ -657,7 +651,7 @@
 	if (!unix_may_send(sk, other))
 	{
 		unix_unlock(other);
-		return -EPERM;
+		return -EINVAL;
 	}
 
 	/*
@@ -1001,18 +995,9 @@
 	other = unix_peer(sk);
 	if (other && other->dead)
 	{
-		/* Alan said:
+		/*
 		 *	Check with 1003.1g - what should
 		 *	datagram error
-		 * 
-		 * Pardon, if POSIX says, that we should return
-		 * error here, it is wrong.
-		 * It is the main idea of SOCK_DGRAM sockets,
-		 * they could die and be borned, and clients
-		 * should not care about it.
-		 * If you want SOCK_STREAM semantics,
-		 * use SOCK_STREAM.
-		 *				--ANK
 		 */
 		unix_unlock(other);
 		unix_peer(sk)=NULL;
@@ -1035,7 +1020,7 @@
 		{
 			unix_unlock(other);
 			kfree_skb(skb, FREE_WRITE);
-			return -EPERM;
+			return -EINVAL;
 		}
 	}
 
@@ -1102,8 +1087,8 @@
 		 *	much.
 		 */
 
-		if (size > 4000)
-			limit = 4000;	/* Fall back to 4K if we can't grab a big buffer this instant */
+		if (size > 3500)
+			limit = 3500;	/* Fall back to a page if we can't grab a big buffer this instant */
 		else
 			limit = 0;	/* Otherwise just grab and wait */
 
@@ -1232,6 +1217,11 @@
 		     apparently wrong)
 		   - clone fds (I choosed it for now, it is the most universal
 		     solution)
+		
+	           POSIX 1003.1g does not actually define this clearly
+	           at all. POSIX 1003.1g doesn't define a lot of things
+	           clearly however!		     
+		   
 		*/
 		if (UNIXCB(skb).fp)
 			scm->fp = scm_fp_dup(UNIXCB(skb).fp);
@@ -1278,11 +1268,10 @@
 		{
 			if (copied >= target)
 				break;
-#if 0
-			/* ANK: sk->err is never set for UNIX */
+
 			if (sk->err) 
 				return sock_error(sk);
-#endif
+
 			if (sk->shutdown & RCV_SHUTDOWN)
 				break;
 			up(&sk->protinfo.af_unix.readsem);
@@ -1348,8 +1337,8 @@
 		}
 		else
 		{
-			/* It is questionable,
-			   see note in unix_dgram_recvmsg.
+			/* It is questionable, see note in unix_dgram_recvmsg.
+			   
 			 */
 			if (UNIXCB(skb).fp)
 				scm->fp = scm_fp_dup(UNIXCB(skb).fp);
@@ -1415,7 +1404,7 @@
 			if(sk->state==TCP_LISTEN)
 				return -EINVAL;
 			/*
-			 *	These two are safe on a single CPU system as
+			 *	These two are safe on current systems as
 			 *	only user tasks fiddle here
 			 */
 			if((skb=skb_peek(&sk->receive_queue))!=NULL)
@@ -1544,7 +1533,7 @@
 void unix_proto_init(struct net_proto *pro)
 {
 	struct sk_buff *dummy_skb;
-	printk(KERN_INFO "NET3: Unix domain sockets 0.14 for Linux NET3.037.\n");
+	printk(KERN_INFO "NET3: Unix domain sockets 0.15 for Linux NET3.038.\n");
 	if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
 	{
 		printk(KERN_CRIT "unix_proto_init: panic\n");
diff -u --recursive --new-file v2.1.19/linux/net/unix/sysctl_net_unix.c linux/net/unix/sysctl_net_unix.c
--- v2.1.19/linux/net/unix/sysctl_net_unix.c	Tue Apr  2 09:03:35 1996
+++ linux/net/unix/sysctl_net_unix.c	Thu Jan  2 15:13:35 1997
@@ -1,8 +1,14 @@
-/* -*- linux-c -*-
- * sysctl_net_unix.c: sysctl interface to net af_unix subsystem.
+/*
+ * NET3:	Sysctl interface to net af_unix subsystem.
  *
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/unix directory entry (empty =) ). [MS]
+ * Authors:	Mike Shaver.
+ *
+ *		Added /proc/sys/net/unix directory entry (empty =) ).
+ *
+ *		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/mm.h>
diff -u --recursive --new-file v2.1.19/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.19/linux/net/x25/af_x25.c	Wed Dec 18 15:59:15 1996
+++ linux/net/x25/af_x25.c	Thu Jan  2 15:13:35 1997
@@ -55,10 +55,10 @@
 
 static unsigned int lci = 1;
 
-struct proto_ops x25_proto_ops;
-
 static struct sock *volatile x25_list = NULL;
 
+static struct proto_ops x25_proto_ops;
+
 int x25_addr_ntoa(unsigned char *p, x25_address *called_addr, x25_address *calling_addr)
 {
 	int called_len, calling_len;
@@ -136,31 +136,9 @@
 /*
  *	Socket removal during an interrupt is now safe.
  */
-static void x25_remove_socket(struct sock *sk)
+extern inline void x25_remove_socket(struct sock *sk)
 {
-	struct sock *s;
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-
-	if ((s = x25_list) == sk) {
-		x25_list = s->next;
-		restore_flags(flags);
-		return;
-	}
-
-	while (s != NULL && s->next != NULL) {
-		if (s->next == sk) {
-			s->next = sk->next;
-			restore_flags(flags);
-			return;
-		}
-
-		s = s->next;
-	}
-
-	restore_flags(flags);
+	sklist_remove_socket(&x25_list,sk);
 }
 
 /*
@@ -189,7 +167,11 @@
 {
 	struct device *dev = (struct device *)ptr;
 
-	if (dev->type == ARPHRD_X25 || dev->type == ARPHRD_ETHER) {
+	if (dev->type == ARPHRD_X25
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+	 || dev->type == ARPHRD_ETHER
+#endif
+	 ) {
 		switch (event) {
 			case NETDEV_UP:
 				x25_link_device_up(dev);
@@ -208,17 +190,10 @@
 /*
  *	Add a socket to the bound sockets list.
  */
-static void x25_insert_socket(struct sock *sk)
+ 
+extern inline void x25_insert_socket(struct sock *sk)
 {
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	sk->next = x25_list;
-	x25_list  = sk;
-
-	restore_flags(flags);
+	sklist_insert_socket(&x25_list,sk);
 }
 
 /*
@@ -504,6 +479,8 @@
 		return NULL;
 	}
 
+	memset(x25, 0x00, sizeof(*x25));
+
 	x25->sk          = sk;
 	sk->protinfo.x25 = x25;
 
@@ -524,14 +501,6 @@
 	skb_queue_head_init(&x25->fragment_queue);
 	skb_queue_head_init(&x25->interrupt_queue);
 
-	x25->condition = 0x00;
-	x25->timer     = 0;
-
-	x25->va    = 0;
-	x25->vr    = 0;
-	x25->vs    = 0;
-	x25->vl    = 0;
-
 	return sk;
 }
 
@@ -566,16 +535,11 @@
 		sk->sleep = &sock->wait;
 	}
 
-	x25->lci      = 0;
-
 	x25->t21      = sysctl_x25_call_request_timeout;
 	x25->t22      = sysctl_x25_reset_request_timeout;
 	x25->t23      = sysctl_x25_clear_request_timeout;
 	x25->t2       = sysctl_x25_ack_holdback_timeout;
 
-	x25->fraglen     = 0;
-	x25->qbitincl    = 0;
-	x25->intflg      = 0;
 	x25->state       = X25_STATE_0;
 
 	x25->facilities.window_size = X25_DEFAULT_WINDOW_SIZE;
@@ -583,11 +547,6 @@
 	x25->facilities.throughput  = X25_DEFAULT_THROUGHPUT;
 	x25->facilities.reverse     = X25_DEFAULT_REVERSE;
 
-	x25->neighbour   = NULL;
-
-	memset(&x25->source_addr, '\0', X25_ADDR_LEN);
-	memset(&x25->dest_addr,   '\0', X25_ADDR_LEN);
-
 	return 0;
 }
 
@@ -623,9 +582,7 @@
 
 	x25->facilities = osk->protinfo.x25->facilities;
 
-	x25->qbitincl    = osk->protinfo.x25->qbitincl;
-	x25->intflg      = 0;
-	x25->fraglen     = 0;
+	x25->qbitincl   = osk->protinfo.x25->qbitincl;
 
 	return sk;
 }
@@ -1103,7 +1060,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT;
@@ -1272,7 +1229,7 @@
 	x25_create
 };
 
-struct proto_ops x25_proto_ops = {
+static struct proto_ops x25_proto_ops = {
 	AF_X25,
 
 	x25_dup,
@@ -1345,11 +1302,10 @@
 }
 
 #ifdef MODULE
+EXPORT_NO_SYMBOLS;
 
 int init_module(void)
 {
-	register_symtab(NULL);
-
 	x25_proto_init(NULL);
 	
 	return 0;
@@ -1373,7 +1329,7 @@
 
 	dev_remove_pack(&x25_packet_type);
 
-	sock_unregister(x25_proto_ops.family);
+	sock_unregister(AF_X25);
 }
 
 #endif
diff -u --recursive --new-file v2.1.19/linux/net/x25/x25_dev.c linux/net/x25/x25_dev.c
--- v2.1.19/linux/net/x25/x25_dev.c	Wed Dec 18 15:59:15 1996
+++ linux/net/x25/x25_dev.c	Thu Jan  2 15:13:35 1997
@@ -47,9 +47,8 @@
 #include <linux/firewall.h>
 #include <net/x25.h>
 
-static int x25_receive_data(struct sk_buff *skb, struct device *dev)
+static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh)
 {
-	struct x25_neigh *neigh;
 	struct sock *sk;
 	unsigned short frametype;
 	unsigned int lci;
@@ -61,14 +60,6 @@
 	}
 #endif
 
-	/*
-	 *	Packet received from unrecognised device, throw it away.
-	 */
-	if ((neigh = x25_get_neigh(dev)) == NULL) {
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
 	frametype = skb->data[2];
         lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
 
@@ -107,12 +98,40 @@
 
 int x25_lapb_receive_frame(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
 {
+	struct x25_neigh *neigh;
+
 	skb->sk = NULL;
 	
-	switch (*skb->data) {
+	/*
+	 *	Packet received from unrecognised device, throw it away.
+	 */
+	if ((neigh = x25_get_neigh(dev)) == NULL) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	switch (skb->data[0]) {
 		case 0x00:
 			skb_pull(skb, 1);
-			return x25_receive_data(skb, dev);
+			return x25_receive_data(skb, neigh);
+
+		case 0x01:
+			x25_link_established(neigh);
+			kfree_skb(skb, FREE_READ);
+			return 0;
+
+		case 0x02:
+			x25_link_terminated(neigh);
+			kfree_skb(skb, FREE_READ);
+			return 0;
+
+		case 0x03:
+			kfree_skb(skb, FREE_READ);
+			return 0;
+
+		case 0x04:
+			kfree_skb(skb, FREE_READ);
+			return 0;
 
 		default:
 			kfree_skb(skb, FREE_READ);
@@ -122,59 +141,107 @@
 
 int x25_llc_receive_frame(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
 {
-	unsigned int len;
+	struct x25_neigh *neigh;
 
 	skb->sk = NULL;
-
-	memcpy(&len, skb->data, sizeof(int));
-
-	skb_pull(skb, sizeof(int));
-
-	skb_trim(skb, len);
 	
-	return x25_receive_data(skb, dev);
+	/*
+	 *	Packet received from unrecognised device, throw it away.
+	 */
+	if ((neigh = x25_get_neigh(dev)) == NULL) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	return x25_receive_data(skb, neigh);
 }
 
-int x25_link_up(struct device *dev)
+void x25_establish_link(struct x25_neigh *neigh)
 {
-	switch (dev->type) {
-		case ARPHRD_ETHER:
-			return 1;
+	struct sk_buff *skb;
+	unsigned char *ptr;
+
+	switch (neigh->dev->type) {
 		case ARPHRD_X25:
-			return 0;
+			if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
+				printk(KERN_ERR "x25_dev: out of memory\n");
+				return;
+			}
+			ptr  = skb_put(skb, 1);
+			*ptr = 0x01;
+			break;
+
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+		case ARPHRD_ETHER:
+			return;
+#endif
 		default:
-			return 0;
+			return;
 	}
+
+	skb->protocol = htons(ETH_P_X25);
+	skb->priority = SOPRI_NORMAL;
+	skb->dev      = neigh->dev;
+	skb->arp      = 1;
+
+	dev_queue_xmit(skb);
 }
 
-void x25_send_frame(struct sk_buff *skb, struct device *dev)
+void x25_terminate_link(struct x25_neigh *neigh)
 {
-	unsigned char *dptr;
-	unsigned int  len;
-	static char bcast_addr[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+	struct sk_buff *skb;
+	unsigned char *ptr;
+
+	switch (neigh->dev->type) {
+		case ARPHRD_X25:
+			if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
+				printk(KERN_ERR "x25_dev: out of memory\n");
+				return;
+			}
+			ptr  = skb_put(skb, 1);
+			*ptr = 0x02;
+			break;
+
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+		case ARPHRD_ETHER:
+			return;
+#endif
+		default:
+			return;
+	}
 
 	skb->protocol = htons(ETH_P_X25);
 	skb->priority = SOPRI_NORMAL;
-	skb->dev      = dev;
+	skb->dev      = neigh->dev;
 	skb->arp      = 1;
 
-	switch (dev->type) {
-		case ARPHRD_ETHER:
-			len  = skb->len;
-			dptr = skb_push(skb, sizeof(int));
-			memcpy(dptr, &len, sizeof(int));
-			dev->hard_header(skb, dev, ETH_P_X25, bcast_addr, NULL, 0);
-			break;
+	dev_queue_xmit(skb);
+}
+
+void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh)
+{
+	unsigned char *dptr;
 
+	switch (neigh->dev->type) {
 		case ARPHRD_X25:
 			dptr  = skb_push(skb, 1);
 			*dptr = 0x00;
 			break;
 
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+		case ARPHRD_ETHER:
+			kfree_skb(skb, FREE_WRITE);
+			return;
+#endif
 		default:
 			kfree_skb(skb, FREE_WRITE);
 			return;
 	}
+
+	skb->protocol = htons(ETH_P_X25);
+	skb->priority = SOPRI_NORMAL;
+	skb->dev      = neigh->dev;
+	skb->arp      = 1;
 
 	dev_queue_xmit(skb);
 }
diff -u --recursive --new-file v2.1.19/linux/net/x25/x25_link.c linux/net/x25/x25_link.c
--- v2.1.19/linux/net/x25/x25_link.c	Wed Dec 18 15:59:25 1996
+++ linux/net/x25/x25_link.c	Thu Jan  2 15:13:36 1997
@@ -115,14 +115,14 @@
 	switch (frametype) {
 		case X25_RESTART_REQUEST:
 			neigh->t20timer = 0;
-			neigh->state    = 1;
+			neigh->state    = X25_LINK_STATE_3;
 			del_timer(&neigh->timer);
 			x25_transmit_restart_confirmation(neigh);
 			break;
 
 		case X25_RESTART_CONFIRMATION:
 			neigh->t20timer = 0;
-			neigh->state    = 1;
+			neigh->state    = X25_LINK_STATE_3;
 			del_timer(&neigh->timer);
 			break;
 
@@ -135,9 +135,9 @@
 			break;
 	}
 
-	if (neigh->state == 1) {
+	if (neigh->state == X25_LINK_STATE_3) {
 		while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
-			x25_send_frame(skbn, neigh->dev);
+			x25_send_frame(skbn, neigh);
 	}
 }
 
@@ -167,7 +167,7 @@
 
 	skb->sk = NULL;
 
-	x25_send_frame(skb, neigh->dev);
+	x25_send_frame(skb, neigh);
 }
 
 /*
@@ -194,7 +194,7 @@
 
 	skb->sk = NULL;
 
-	x25_send_frame(skb, neigh->dev);
+	x25_send_frame(skb, neigh);
 }
 
 /*
@@ -222,7 +222,7 @@
 
 	skb->sk = NULL;
 
-	x25_send_frame(skb, neigh->dev);
+	x25_send_frame(skb, neigh);
 }
 
 /*
@@ -252,7 +252,7 @@
 
 	skb->sk = NULL;
 
-	x25_send_frame(skb, neigh->dev);
+	x25_send_frame(skb, neigh);
 }
 
 void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh)
@@ -262,24 +262,42 @@
 		return;
 #endif
 
-	if (!x25_link_up(neigh->dev))
-		neigh->state = 0;
-
-	skb->arp  = 1;
+	switch (neigh->state) {
+		case X25_LINK_STATE_0:
+			skb_queue_tail(&neigh->queue, skb);
+			neigh->state = X25_LINK_STATE_1;
+			x25_establish_link(neigh);
+			break;
+		case X25_LINK_STATE_1:
+		case X25_LINK_STATE_2:
+			skb_queue_tail(&neigh->queue, skb);
+			break;
+		case X25_LINK_STATE_3:
+			x25_send_frame(skb, neigh);
+			break;
+	}
+}
 
-	if (neigh->state == 1) {
-		x25_send_frame(skb, neigh->dev);
-	} else {
-		skb_queue_tail(&neigh->queue, skb);
-		
-		if (neigh->t20timer == 0) {
+void x25_link_established(struct x25_neigh *neigh)
+{
+	switch (neigh->state) {
+		case X25_LINK_STATE_0:
+			neigh->state = X25_LINK_STATE_2;
+			break;
+		case X25_LINK_STATE_1:
 			x25_transmit_restart_request(neigh);
+			neigh->state    = X25_LINK_STATE_2;
 			neigh->t20timer = neigh->t20;
 			x25_link_set_timer(neigh);
-		}
+			break;
 	}
 }
 
+void x25_link_terminated(struct x25_neigh *neigh)
+{
+	neigh->state = X25_LINK_STATE_0;
+}
+
 /*
  *	Add a new device.
  */
@@ -295,7 +313,7 @@
 	init_timer(&x25_neigh->timer);
 
 	x25_neigh->dev      = dev;
-	x25_neigh->state    = 0;
+	x25_neigh->state    = X25_LINK_STATE_0;
 	x25_neigh->extended = 0;
 	x25_neigh->t20timer = 0;
 	x25_neigh->t20      = sysctl_x25_restart_request_timeout;
@@ -412,10 +430,10 @@
 
 	cli();
 
-	len += sprintf(buffer, "device           st    t20    ext\n");
+	len += sprintf(buffer, "device  st    t20    ext\n");
 
 	for (x25_neigh = x25_neigh_list; x25_neigh != NULL; x25_neigh = x25_neigh->next) {
-		len += sprintf(buffer + len, "%-15s  %2d %3d/%03d  %d\n",
+		len += sprintf(buffer + len, "%-6s  %2d  %3d/%03d  %d\n",
 			x25_neigh->dev->name,
 			x25_neigh->state,
 			x25_neigh->t20timer / X25_SLOWHZ,
diff -u --recursive --new-file v2.1.19/linux/net/x25/x25_route.c linux/net/x25/x25_route.c
--- v2.1.19/linux/net/x25/x25_route.c	Wed Dec 18 15:59:25 1996
+++ linux/net/x25/x25_route.c	Thu Jan  2 15:13:36 1997
@@ -62,7 +62,9 @@
 	if ((x25_route = (struct x25_route *)kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
-	x25_route->address   = *address;
+	strcpy(x25_route->address.x25_addr, "000000000000000");
+	memcpy(x25_route->address.x25_addr, address->x25_addr, sigdigits);
+
 	x25_route->sigdigits = sigdigits;
 	x25_route->dev       = dev;
 
@@ -143,7 +145,11 @@
 	if ((dev = dev_get(devname)) == NULL)
 		return NULL;
 
-	if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25 || dev->type == ARPHRD_ETHER))
+	if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+	   || dev->type == ARPHRD_ETHER
+#endif
+	   ))
 		return dev;
 
 	return NULL;
@@ -217,10 +223,10 @@
 
 	cli();
 
-	len += sprintf(buffer, "address         digits  device\n");
+	len += sprintf(buffer, "address          digits  device\n");
 
 	for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) {
-		len += sprintf(buffer + len, "%-15s %2d %-4s\n",
+		len += sprintf(buffer + len, "%-15s  %-6d  %-5s\n",
 			x25_route->address.x25_addr, x25_route->sigdigits,
 			(x25_route->dev != NULL) ? x25_route->dev->name : "???");
 
