diff -u --recursive --new-file v2.2.4/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.2.4/linux/Documentation/Configure.help	Tue Mar 23 14:35:46 1999
+++ linux/Documentation/Configure.help	Fri Mar 26 13:23:20 1999
@@ -7957,6 +7957,19 @@
   read Documentation/modules.txt. The module will be called
   istallion.o.
 
+Microgate SyncLink adapter support
+CONFIG_SYNCLINK
+  Provides support for the SyncLink ISA and PCI
+  multiprotocol serial adapters. These adapters
+  support asynchronous and HDLC bit synchronous
+  communication up to 10Mbps (PCI adapter)
+
+Synchronous HDLC line discipline support
+CONFIG_N_HDLC
+  Allows synchronous HDLC communications with
+  tty device drivers that support synchronous
+  HDLC such as the Microgate SyncLink adapter.
+  
 Hayes ESP serial port support
 CONFIG_ESPSERIAL
   This is a driver which supports Hayes ESP serial ports. Both single
@@ -8029,15 +8042,14 @@
   removed from the running kernel whenever you want), say M here and
   read Documentation/modules.txt. The module will be called lp.o. 
 
-  If you have several parallel ports, you should specify the base
-  address for the port to be used by the printer with the "lp" kernel
-  command line option. (Try "man bootparam" or see the documentation
-  of your boot loader (lilo or loadlin) about how to pass options to
-  the kernel at boot time. The lilo procedure is also explained in the
-  SCSI-HOWTO, available via FTP (user: anonymous) in
-  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.) The standard base
-  addresses as well as the syntax of the "lp" command line option can
-  be found in drivers/char/lp.c.
+  If you have several parallel ports, you can specify which ports to
+  use with the "lp" kernel command line option. (Try "man bootparam"
+  or see the documentation of your boot loader (lilo or loadlin)
+  about how to pass options to the kernel at boot time. The lilo
+  procedure is also explained in the SCSI-HOWTO, available via FTP
+  (user: anonymous) in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.)
+  The syntax of the "lp" command line option can be found in
+  drivers/char/lp.c.
 
   If you have more than 3 printers, you need to increase the LP_NO
   variable in lp.c.
diff -u --recursive --new-file v2.2.4/linux/Documentation/parport.txt linux/Documentation/parport.txt
--- v2.2.4/linux/Documentation/parport.txt	Tue Jul 21 00:15:29 1998
+++ linux/Documentation/parport.txt	Fri Mar 26 13:23:24 1999
@@ -87,7 +87,7 @@
 If you have configured the /proc filesystem into your kernel, you will
 see a new directory entry: /proc/parport.  In there will be a
 directory entry for each parallel port for which parport is
-configured.  In each of those directories are three files describing
+configured.  In each of those directories are four files describing
 that parallel port.  For example:
 
 File:				Contents:
diff -u --recursive --new-file v2.2.4/linux/Documentation/stallion.txt linux/Documentation/stallion.txt
--- v2.2.4/linux/Documentation/stallion.txt	Fri Oct 23 22:01:19 1998
+++ linux/Documentation/stallion.txt	Sun Mar 28 09:02:27 1999
@@ -2,10 +2,10 @@
 Stallion Multiport Serial Driver Readme
 ---------------------------------------
 
-Copyright (C) 1994-1998,  Stallion Technologies (support@stallion.com).
+Copyright (C) 1994-1999,  Stallion Technologies (support@stallion.com).
 
-Version:   5.4.7
-Date:      23OCT98
+Version:   5.5.1
+Date:      28MAR99
 
 
 
@@ -15,28 +15,28 @@
 multiport serial boards. One is for the Stallion smart boards - that is
 EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for
 the true Stallion intelligent multiport boards - EasyConnection 8/64
-(ISA and EISA), ONboard and Brumby.
+(ISA, EISA, MCA), EasyConnection/RA-PCI, ONboard and Brumby.
 
 If you are using any of the Stallion intelligent multiport boards (Brumby,
-ONboard, EasyConnection 8/64 (ISA or EISA)) with Linux you will need to
-get the driver utility package. This package is available at most of the
-Linux archive sites (and on CD-ROMs that contain these archives). The file
-will be called stallion-X.X.X.tar.gz where X.X.X will be the version
-number. In particular this package contains the board embedded executable
-images that are required for these boards. It also contains the downloader
-program. These boards cannot be used without this.
+ONboard, EasyConnection 8/64 (ISA, EISA, MCA), EasyConnection/RA-PCI) with
+Linux you will need to get the driver utility package. This package is
+available at most of the Linux archive sites (and on CD-ROMs that contain
+these archives). The file will be called stallion-X.X.X.tar.gz where X.X.X
+will be the version number. In particular this package contains the board
+embedded executable images that are required for these boards. It also
+contains the downloader program. These boards cannot be used without this.
 
 The Stallion Technologies ftp site, ftp.stallion.com, will always have
 the latest version of the driver utility package. Other sites that usually
 have the latest version are tsx-11.mit.edu, sunsite.unc.edu and their
 mirrors.
 
-ftp.stallion.com:/drivers/ata5/Linux/v544.tar.gz
-tsx-11.mit.edu:/pub/linux/packages/stallion/stallion-5.4.4.tar.gz
-sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.4.tar.gz
+ftp.stallion.com:/drivers/ata5/Linux/v550.tar.gz
+tsx-11.mit.edu:/pub/linux/packages/stallion/stallion-5.5.0.tar.gz
+sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.5.0.tar.gz
 
 As of the printing of this document the latest version of the driver
-utility package is 5.4.4. If a later version is now available then you
+utility package is 5.5.0. If a later version is now available then you
 should use the latest version.
 
 If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI
@@ -44,7 +44,7 @@
 script to create the /dev device nodes for these boards, and a serial stats
 display program.
 
-If you require DIP switch settings, EISA/MCA configuration files, or any
+If you require DIP switch settings, EISA or MCA configuration files, or any
 other information related to Stallion boards then have a look at Stallion's
 web pages at http://www.stallion.com.
 
@@ -55,12 +55,101 @@
 The drivers can be used as loadable modules or compiled into the kernel.
 You can choose which when doing a "config" on the kernel.
 
-All ISA, EISA and MCA boards that you want to use need to be entered into
-the driver(s) configuration structures. All PCI boards will be automatically
-detected when you load the driver - so they do not need to be entered into
-the driver(s) configuration structure. (Note that kernel PCI BIOS32 support
-is required to use PCI boards.)
+All ISA, EISA and MCA boards that you want to use need to be configured into
+the driver(s). All PCI boards will be automatically detected when you load
+the driver - so they do not need to be entered into the driver(s)
+configuration structure. Note that kernel PCI support is required to use PCI
+boards.
 
+There are two methods of configuring ISA, EISA and MCA boards into the drivers.
+If using the driver as a loadable module then the simplist method is to pass
+the driver configuration as module arguments. The other method is to modify
+the driver source to add configuration lines for each board in use.
+
+If you have pre-built Stallion driver modules then the module argument
+configuration method should be used. A lot of Linux distributions come with
+pre-built driver modules in /lib/modules/X.Y.Z/misc for the kernel in use.
+That makes things pretty simple to get going.
+
+
+2.1 MODULE DRIVER CONFIGURATION:
+
+The simplest configuration for modules is to use the module load arguments
+to configure any ISA, EISA or MCA boards. PCI boards are automatically
+detected, so do not need any additional configuration at all.
+
+If using EasyIO, EasyConnection 8/32 ISA or MCA, or EasyConnection 8/63-PCI
+boards then use the "stallion" driver module, Otherwise if you are using
+an EasyConnection 8/64 ISA, EISA or MCA, EasyConnection/RA-PCI, ONboard,
+Brumby or original Stallion board then use the "istallion" driver module.
+
+Typically to load up the smart board driver use:
+
+    insmod stallion.o
+
+This will load the EasyIO and EasyConnection 8/32 driver. It will output a
+message to say that it loaded and print the driver version number. It will
+also print out whether it found the configured boards or not. These messages
+may not appear on the console, but typically are always logged to
+/var/adm/messages or /var/log/syslog files - depending on how the klogd and
+syslogd daemons are setup on your system.
+
+To load the intelligent board driver use:
+
+    insmod istallion.o
+
+It will output similar messages to the smart board driver.
+
+If not using an auto-detectable board type (that is a PCI board) then you
+will also need to supply command line arguments to the "insmod" command
+when loading the driver. The general form of the configuration argument is
+
+    board?=<name>[,<ioaddr>[,<addr>][,<irq>]]
+
+where:
+
+    board?  -- specifies the arbitary board number of this board,
+               can be in the range 0 to 3.
+
+    name    -- textual name of this board. The board name is the comman
+               board name, or any "shortened" version of that. The board
+               type number may also be used here.
+
+    ioaddr  -- specifies the I/O address of this board. This argument is
+               optional, but should generally be specified.
+
+    addr    -- optional second address argument. Some board types require
+               a second I/O address, some require a memory address. The
+               exact meaning of this argument depends on the board type.
+
+    irq     -- optional IRQ line used by this board.
+
+Up to 4 board configuration arguments can be specified on the load line.
+Here is some examples:
+
+    insmod stallion.o board0=easyio,0x2a0,5
+
+This configures an EasyIO board as board 0 at I/O address 0x2a0 and IRQ 5.
+
+    insmod istallion.o board3=ec8/64,0x2c0,0xcc000
+
+This configures an EasyConnection 8/64 ISA as board 3 at I/O address 0x2c0 at
+memory address 0xcc000.
+
+    insmod stallion.o board1=ec8/32-at,0x2a0,0x280,10
+
+This configures an EasyConnection 8/32 ISA board at primary I/O address 0x2a0,
+secondary address 0x280 and IRQ 10.
+
+You will probably want to enter this module load and configuration information
+into your system startup scripts so that the drivers are loaded and configured
+on each system boot. Typically the start up script would be something line
+/etc/rc.d/rc.modules.
+
+
+2.2 STATIC DRIVER CONFIGURATION:
+
+For static driver configuration you need to modify the driver source code.
 Entering ISA, EISA and MCA boards into the driver(s) configuration structure
 involves editing the driver(s) source file. It's pretty easy if you follow
 the instructions below. Both drivers can support up to 4 boards. The smart
@@ -96,14 +185,16 @@
 driver will emit some kernel trace messages about whether the configured
 boards were detected or not. Depending on how your system logger is set
 up these may come out on the console, or just be logged to
-/var/adm/messages. You should check the messages to confirm that all is well.
+/var/adm/messages or /var/log/syslog. You should check the messages to
+confirm that all is well.
 
 
-2.1 SHARING INTERRUPTS
+2.3 SHARING INTERRUPTS
 
 It is possible to share interrupts between multiple EasyIO and
-EasyConnection 8/32 boards in an EISA system. To do this you will need to
-do a couple of things:
+EasyConnection 8/32 boards in an EISA system. To do this you must be using
+static driver configuration, modifying the driver source code to add driver
+configuration. Then a couple of extra things are required:
 
 1. When entering the board resources into the stallion.c file you need to
    mark the boards as using level triggered interrupts. Do this by replacing
@@ -130,7 +221,7 @@
 sharing interrupts.
 
 
-2.2 USING HIGH SHARED MEMORY
+2.4 USING HIGH SHARED MEMORY
 
 The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of
 using shared memory addresses above the usual 640K - 1Mb range. The ONboard
@@ -145,16 +236,18 @@
 
 
 
-2.3 TROUBLE SHOOTING
+2.5 TROUBLE SHOOTING
 
 If a board is not found by the driver but is actually in the system then the
-most likely problem is that the I/O address is wrong. Change it in the driver
-stallion.c or istallion.c configuration structure and rebuild the kernel or
-modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards
-the IRQ is software programmable, so if there is a conflict you may need to
-change the IRQ used for a board in the stallion.c configuration structure.
-There are no interrupts to worry about for ONboard, Brumby or EasyConnection
-8/64 (ISA, EISA and MCA) boards. The memory region on EasyConnection 8/64 and
+most likely problem is that the I/O address is wrong. Change the module load
+argument for the loadable module form. Or change it in the driver stallion.c
+or istallion.c configuration structure and rebuild the kernel or modules, or
+change it on the board.
+
+On EasyIO and EasyConnection 8/32 boards the IRQ is software programmable, so
+if there is a conflict you may need to change the IRQ used for a board. There
+are no interrupts to worry about for ONboard, Brumby or EasyConnection 8/64
+(ISA, EISA and MCA) boards. The memory region on EasyConnection 8/64 and
 ONboard boards is software programmable, but not on the Brumby boards.
 
 
@@ -167,9 +260,13 @@
 to them. This is done via a user level application supplied in the driver
 utility package called "stlload". Compile this program wherever you dropped
 the package files, by typing "make". In its simplest form you can then type
+
     ./stlload -i cdk.sys
+
 in this directory and that will download board 0 (assuming board 0 is an
-EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do:
+EasyConnection 8/64 or EasyConnection/RA board). To download to an
+ONboard, Brumby or Stallion do:
+
     ./stlload -i 2681.sys
 
 Normally you would want all boards to be downloaded as part of the standard
@@ -182,6 +279,7 @@
 and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put
 them anyway). As an example your /etc/rc.d/rc.S file might have the
 following lines added to it (if you had 3 boards):
+
     /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys
     /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys
     /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys
diff -u --recursive --new-file v2.2.4/linux/Makefile linux/Makefile
--- v2.2.4/linux/Makefile	Tue Mar 23 14:35:46 1999
+++ linux/Makefile	Tue Mar 23 14:48:17 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 2
-SUBLEVEL = 4
+SUBLEVEL = 5
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
diff -u --recursive --new-file v2.2.4/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- v2.2.4/linux/arch/alpha/kernel/smp.c	Tue Jan 19 11:32:50 1999
+++ linux/arch/alpha/kernel/smp.c	Sun Mar 28 22:36:32 1999
@@ -499,6 +499,7 @@
 			return;
 		}
 		mdelay(1);
+		barrier();
 	}
 	DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid));
 }
@@ -541,6 +542,7 @@
 		if (!(hwrpb->txrdy & cpumask))
 			goto ready1;
 		udelay(100);
+		barrier();
 	}
 	goto timeout;
 
@@ -549,6 +551,7 @@
 		if (!(hwrpb->txrdy & cpumask))
 			goto ready2;
 		udelay(100);
+		barrier();
 	}
 	goto timeout;
 
diff -u --recursive --new-file v2.2.4/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.2.4/linux/arch/i386/kernel/ptrace.c	Tue Mar 23 14:35:46 1999
+++ linux/arch/i386/kernel/ptrace.c	Wed Mar 24 13:18:46 1999
@@ -386,7 +386,7 @@
 		    (current->uid != child->uid) ||
 	 	    (current->gid != child->egid) ||
 	 	    (current->gid != child->sgid) ||
-	 	    (cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+	 	    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
 	 	    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
 			goto out;
 		/* the same process cannot be attached many times */
diff -u --recursive --new-file v2.2.4/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.2.4/linux/arch/sparc/kernel/process.c	Tue Mar 23 14:35:46 1999
+++ linux/arch/sparc/kernel/process.c	Thu Mar 25 09:23:33 1999
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.132 1999/03/22 02:12:13 davem Exp $
+/*  $Id: process.c,v 1.133 1999/03/24 11:42:30 davem Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -517,9 +517,16 @@
 	p->tss.kregs = childregs;
 
 	if(regs->psr & PSR_PS) {
-		childregs->u_regs[UREG_FP] = p->tss.ksp;
+		new_stack = (struct reg_window *)
+			((((unsigned long)p) +
+			  (TASK_UNION_SIZE)) -
+			 (REGWIN_SZ));
+		childregs->u_regs[UREG_FP] = (unsigned long) new_stack;
 		p->tss.flags |= SPARC_FLAG_KTHREAD;
 		p->tss.current_ds = KERNEL_DS;
+		memcpy((void *)new_stack,
+		       (void *)regs->u_regs[UREG_FP],
+		       sizeof(struct reg_window));
 		childregs->u_regs[UREG_G6] = (unsigned long) p;
 	} else {
 		childregs->u_regs[UREG_FP] = sp;
diff -u --recursive --new-file v2.2.4/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.2.4/linux/arch/sparc/kernel/ptrace.c	Tue Mar 23 14:35:46 1999
+++ linux/arch/sparc/kernel/ptrace.c	Wed Mar 24 15:10:40 1999
@@ -542,7 +542,7 @@
 		    (current->uid != child->uid) ||
 		    (current->gid != child->egid) ||
 		    (current->gid != child->sgid) || 
-	 	    (cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+	 	    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
 		    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) {
 			pt_error_return(regs, EPERM);
 			goto out;
diff -u --recursive --new-file v2.2.4/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.2.4/linux/arch/sparc/mm/srmmu.c	Tue Mar 23 14:35:47 1999
+++ linux/arch/sparc/mm/srmmu.c	Thu Mar 25 09:23:33 1999
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.184 1999/03/20 22:02:03 davem Exp $
+/* $Id: srmmu.c,v 1.185 1999/03/24 11:42:35 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -817,19 +817,11 @@
 
 static void srmmu_switch_to_context(struct task_struct *tsk)
 {
-	int set = 0;
-
 	if(tsk->mm->context == NO_CONTEXT) {
 		alloc_context(tsk->mm);
-		flush_cache_mm(tsk->mm);
 		ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
-		flush_tlb_mm(tsk->mm);
-		set = 1;
-	} else if(tsk->mm != current->mm)
-		set = 1;
-
-	if(set != 0)
-		srmmu_set_context(tsk->mm->context);
+	}
+	srmmu_set_context(tsk->mm->context);
 }
 
 static void srmmu_init_new_context(struct mm_struct *mm)
@@ -1353,7 +1345,7 @@
 static void viking_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
 	if(pgdp != swapper_pg_dir)
-		viking_flush_page((unsigned long)pgdp);
+		flush_chunk((unsigned long)pgdp);
 	if(tsk->mm->context != NO_CONTEXT &&
 	   tsk->mm->pgd != pgdp) {
 		flush_cache_mm(tsk->mm);
@@ -1402,8 +1394,6 @@
 
 static void hypersparc_switch_to_context(struct task_struct *tsk)
 {
-	int set = 0;
-
 	if(tsk->mm->context == NO_CONTEXT) {
 		ctxd_t *ctxp;
 
@@ -1411,14 +1401,9 @@
 		ctxp = &srmmu_context_table[tsk->mm->context];
 		srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4))));
 		hypersparc_flush_page_to_ram((unsigned long)ctxp);
-		set = 1;
-	} else if(tsk->mm != current->mm)
-		set = 1;
-
-	if(set != 0) {
-		hyper_flush_whole_icache();
-		srmmu_set_context(tsk->mm->context);
 	}
+	hyper_flush_whole_icache();
+	srmmu_set_context(tsk->mm->context);
 }
 
 static void hypersparc_init_new_context(struct mm_struct *mm)
@@ -2739,11 +2724,10 @@
 		BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP);
 	}
 
-	/* flush_cache_* are nops */
-	BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NOP);
-	BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NOP);
-	BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP);
-	BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP);
+	BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NORM);
 
 #ifdef __SMP__
 	if (sparc_cpu_model == sun4d) {
diff -u --recursive --new-file v2.2.4/linux/arch/sparc/mm/viking.S linux/arch/sparc/mm/viking.S
--- v2.2.4/linux/arch/sparc/mm/viking.S	Tue Mar 23 14:35:47 1999
+++ linux/arch/sparc/mm/viking.S	Thu Mar 25 09:23:33 1999
@@ -1,4 +1,4 @@
-/* $Id: viking.S,v 1.12 1999/02/23 13:23:50 jj Exp $
+/* $Id: viking.S,v 1.13 1999/03/24 11:42:32 davem Exp $
  * viking.S: High speed Viking cache/mmu operations
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -67,7 +67,7 @@
 	clr	%o1			! set counter, 0 - 127
 	sethi	%hi(KERNBASE + PAGE_SIZE - 0x80000000), %o3
 	sethi	%hi(0x80000000), %o4
-	sethi	%hi(VIKING_PTAG_VALID | VIKING_PTAG_DIRTY), %o5
+	sethi	%hi(VIKING_PTAG_VALID), %o5
 	sethi	%hi(2*PAGE_SIZE), %o0
 	sethi	%hi(PAGE_SIZE), %g7
 	clr	%o2			! block counter, 0 - 3
@@ -80,15 +80,12 @@
 	or	%g5, %g4, %g5
 	ldda	[%g5] ASI_M_DATAC_TAG, %g2
 	cmp	%g3, %g1		! ptag == ppage?
-	bne,a	7f
+	bne	7f
 	 inc	%o2
 
-	and	%g2, %o5, %g3		! ptag VALID and DIRTY?
-	cmp	%g3, %o5
-	bne,a	7f
-	 inc	%o2
-
-	add	%g4, %o3, %g2		! (KERNBASE + PAGE_SIZE) | (set << 5)
+	andcc	%g2, %o5, %g0		! ptag VALID?
+	be	7f
+	 add	%g4, %o3, %g2		! (KERNBASE + PAGE_SIZE) | (set << 5)
 	ld	[%g2], %g3
 	ld	[%g2 + %g7], %g3
 	add	%g2, %o0, %g2
@@ -99,18 +96,15 @@
 	ld	[%g2 + %g7], %g3
 	add	%g2, %o0, %g2
 	ld	[%g2], %g3
-	ld	[%g2 + %g7], %g3
-
 	b	8f
-	 inc	%o1
+	 ld	[%g2 + %g7], %g3
 
 7:
 	cmp	%o2, 3
 	ble	6b
 	 sll	%o2, 26, %g5			! block << 26
 
-	inc	%o1
-8:
+8:	inc	%o1
 	cmp	%o1, 0x7f
 	ble	5b
 	 clr	%o2
@@ -148,10 +142,33 @@
 	retl
 	 nop
 
-viking_flush_cache_all:
+#define WINDOW_FLUSH(tmp1, tmp2)					\
+	mov	0, tmp1;						\
+98:	ld	[%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2;	\
+	orcc	%g0, tmp2, %g0;						\
+	add	tmp1, 1, tmp1;						\
+	bne	98b;							\
+	 save	%sp, -64, %sp;						\
+99:	subcc	tmp1, 1, tmp1;						\
+	bne	99b;							\
+	 restore %g0, %g0, %g0;
+
+viking_flush_cache_page:
+#ifndef __SMP__
+	ld	[%o0 + 0x0], %o0		/* XXX vma->vm_mm, GROSS XXX */
+#endif
 viking_flush_cache_mm:
 viking_flush_cache_range:
-viking_flush_cache_page:
+#ifndef __SMP__
+	ld	[%o0 + AOFF_mm_context], %g1
+	cmp	%g1, -1
+	bne	viking_flush_cache_all
+	 nop
+	b,a	viking_flush_cache_out
+#endif
+viking_flush_cache_all:
+	WINDOW_FLUSH(%g4, %g5)
+viking_flush_cache_out:
 	retl
 	 nop
 
diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.2.4/linux/arch/sparc64/kernel/ioctl32.c	Tue Mar 23 14:35:47 1999
+++ linux/arch/sparc64/kernel/ioctl32.c	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.59 1999/03/12 13:30:21 jj Exp $
+/* $Id: ioctl32.c,v 1.60 1999/03/22 10:40:54 jj Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
@@ -577,8 +577,7 @@
 			cmap.transp = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL);
 			if (!cmap.transp)
 				goto out;
-		} else
-			cmap.transp = NULL;
+		}
 			
 		if (cmd == FBIOGETCMAP)
 			break;
diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c
--- v2.2.4/linux/arch/sparc64/kernel/ptrace.c	Tue Mar 23 14:35:47 1999
+++ linux/arch/sparc64/kernel/ptrace.c	Wed Mar 24 15:10:28 1999
@@ -605,7 +605,7 @@
 		    (current->uid != child->uid) ||
 		    (current->gid != child->egid) ||
 		    (current->gid != child->sgid) ||
-		    (cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+		    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
 		    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) {
 			pt_error_return(regs, EPERM);
 			goto out;
diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.2.4/linux/arch/sparc64/kernel/smp.c	Tue Mar 23 14:35:47 1999
+++ linux/arch/sparc64/kernel/smp.c	Sun Mar 28 09:07:47 1999
@@ -347,6 +347,17 @@
 extern unsigned long xcall_tlbcachesync;
 extern unsigned long xcall_flush_cache_all;
 extern unsigned long xcall_report_regs;
+extern unsigned long xcall_receive_signal;
+
+void smp_receive_signal(int cpu)
+{
+	if(smp_processors_ready &&
+	   (cpu_present_map & (1UL<<cpu)) != 0) {
+		u64 pstate, data0 = (((u64)&xcall_receive_signal) & 0xffffffff);
+		__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+		xcall_deliver(data0, 0, 0, pstate, cpu);
+	}
+}
 
 void smp_report_regs(void)
 {
diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.2.4/linux/arch/sparc64/mm/init.c	Tue Mar 23 14:35:47 1999
+++ linux/arch/sparc64/mm/init.c	Sun Mar 28 09:07:47 1999
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.124 1999/02/08 07:01:42 ecd Exp $
+/*  $Id: init.c,v 1.125 1999/03/28 08:39:33 davem Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
@@ -142,9 +142,9 @@
 	printk("%d reserved pages\n",reserved);
 	printk("%d pages shared\n",shared);
 	printk("%d pages swap cached\n",cached);
-	printk("%ld pages in page table cache\n",pgtable_cache_size);
+	printk("%d pages in page table cache\n",pgtable_cache_size);
 #ifndef __SMP__
-	printk("%ld entries in page dir cache\n",pgd_cache_size);
+	printk("%d entries in page dir cache\n",pgd_cache_size);
 #endif	
 	show_buffers();
 #ifdef CONFIG_NET
diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/mm/ultra.S linux/arch/sparc64/mm/ultra.S
--- v2.2.4/linux/arch/sparc64/mm/ultra.S	Thu Nov 19 09:56:27 1998
+++ linux/arch/sparc64/mm/ultra.S	Sun Mar 28 09:07:47 1999
@@ -1,4 +1,4 @@
-/* $Id: ultra.S,v 1.31 1998/11/07 06:39:21 davem Exp $
+/* $Id: ultra.S,v 1.32 1999/03/28 08:39:34 davem Exp $
  * ultra.S: Don't expand these all over the place...
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -260,6 +260,21 @@
 	 nop
 	b,pt		%xcc, rtrap
 	 clr		%l6
+
+	.globl		xcall_receive_signal
+xcall_receive_signal:
+	rdpr		%pstate, %g2
+	wrpr		%g2, PSTATE_IG | PSTATE_AG, %pstate
+	rdpr		%tstate, %g1
+	andcc		%g1, TSTATE_PRIV, %g0
+	/* If we did not trap from user space, just ignore. */
+	bne,pn		%xcc, 99f
+	 sethi		%hi(109f), %g7
+	b,pt		%xcc, etrap
+109:	 or		%g7, %lo(109b), %g7
+	b,pt		%xcc, rtrap
+	 clr		%l6
+99:	retry
 
 	/* These two are not performance critical... */
 	.globl		xcall_flush_tlb_all
diff -u --recursive --new-file v2.2.4/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.2.4/linux/drivers/block/floppy.c	Wed Jan 13 15:00:41 1999
+++ linux/drivers/block/floppy.c	Fri Mar 26 13:29:14 1999
@@ -3592,7 +3592,7 @@
 	/* read drive info out of physical CMOS */
 	drive=0;
 	if (!UDP->cmos)
-		UDP->cmos= FLOPPY0_TYPE;
+		UDP->cmos = FLOPPY0_TYPE;
 	drive=1;
 	if (!UDP->cmos && FLOPPY1_TYPE)
 		UDP->cmos = FLOPPY1_TYPE;
@@ -3601,26 +3601,31 @@
 	/* additional physical CMOS drive detection should go here */
 
 	for (drive=0; drive < N_DRIVE; drive++){
-		if (UDP->cmos >= 16)
-			UDP->cmos = 0;
-		if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
-			memcpy((char *) UDP,
-			       (char *) (&default_drive_params[(int)UDP->cmos].params),
-			       sizeof(struct floppy_drive_params));
-		if (UDP->cmos){
-			if (first)
-				printk(KERN_INFO "Floppy drive(s): ");
-			else
-				printk(", ");
-			first=0;
-			if (UDP->cmos > 0){
+		unsigned int type = UDP->cmos;
+		struct floppy_drive_params *params;
+		const char *name = NULL;
+		static char temparea[32];
+
+		if (type < NUMBER(default_drive_params)) {
+			params = &default_drive_params[type].params;
+			if (type) {
+				name = default_drive_params[type].name;
 				allowed_drive_mask |= 1 << drive;
-				printk("fd%d is %s", drive,
-				       default_drive_params[(int)UDP->cmos].name);
-			} else
-				printk("fd%d is unknown type %d",drive,
-				       UDP->cmos);
+			}
+		} else {
+			params = &default_drive_params[0].params;
+			sprintf(temparea, "unknown type %d (usb?)", type);
+			name = temparea;
+		}
+		if (name) {
+			const char * prepend = ",";
+			if (first) {
+				prepend = KERN_INFO "Floppy drive(s):";
+				first = 0;
+			}
+			printk("%s fd%d is %s", prepend, drive, name);
 		}
+		*UDP = *params;
 	}
 	if (!first)
 		printk("\n");
@@ -4020,11 +4025,6 @@
 	}
 	if (current_drive >= 4 && !FDC2)
 		FDC2 = 0x370;
-	if (ints[2] <= 0 || 
-	    (ints[2] >= NUMBER(default_drive_params) && ints[2] != 16)){
-		DPRINT("bad CMOS code %d\n", ints[2]);
-		return;
-	}
 	DP->cmos = ints[2];
 	DPRINT("setting CMOS code to %d\n", ints[2]);
 }
diff -u --recursive --new-file v2.2.4/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v2.2.4/linux/drivers/block/rd.c	Wed Mar 10 15:29:45 1999
+++ linux/drivers/block/rd.c	Fri Mar 26 13:57:41 1999
@@ -158,8 +158,6 @@
 
 static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int err;
-	
 	if (!inode || !inode->i_rdev) 	
 		return -EINVAL;
 
diff -u --recursive --new-file v2.2.4/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.2.4/linux/drivers/char/Config.in	Wed Mar 10 15:29:45 1999
+++ linux/drivers/char/Config.in	Wed Mar 24 11:46:00 1999
@@ -42,6 +42,8 @@
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 	dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m
    fi
+   dep_tristate 'Microgate SyncLink card support' CONFIG_SYNCLINK m
+   dep_tristate 'HDLC line discipline support' CONFIG_N_HDLC m
 fi
 bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
 if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
diff -u --recursive --new-file v2.2.4/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.2.4/linux/drivers/char/Makefile	Tue Mar 23 14:35:47 1999
+++ linux/drivers/char/Makefile	Wed Mar 24 11:46:00 1999
@@ -139,6 +139,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SYNCLINK),m)
+  M_OBJS += synclink.o
+endif
+
+ifeq ($(CONFIG_N_HDLC),m)
+  M_OBJS += n_hdlc.o
+endif
+
 ifeq ($(CONFIG_SPECIALIX),y)
 L_OBJS += specialix.o
 else
diff -u --recursive --new-file v2.2.4/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.2.4/linux/drivers/char/bttv.c	Wed Mar 10 15:29:45 1999
+++ linux/drivers/char/bttv.c	Fri Mar 26 13:57:41 1999
@@ -3751,6 +3751,8 @@
 
 #ifdef MODULE
 
+EXPORT_NO_SYMBOLS;
+
 int init_module(void)
 {
 #else
diff -u --recursive --new-file v2.2.4/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.2.4/linux/drivers/char/bw-qcam.c	Tue Mar 23 14:35:47 1999
+++ linux/drivers/char/bw-qcam.c	Fri Mar 26 13:57:41 1999
@@ -986,11 +986,41 @@
 	kfree(qcam);
 }
 
+/* The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ *       -- March 14, 1999  Billy Donahue <billy@escape.com> */
+#ifdef MODULE
+static char *parport[MAX_CAMS] = { NULL, };
+MODULE_PARM(parport, "1-" __MODULE_STRING(MAX_CAMS) "s");
+#endif
+
 #ifdef MODULE
 int init_module(void)
 {
 	struct parport *port;
-
+	int n;
+	if(parport[0] && strncmp(parport[0], "auto", 4)){
+		/* user gave parport parameters */
+		for(n=0; parport[n] && n<MAX_CAMS; n++){
+			char *ep;
+			unsigned long r;
+			r = simple_strtoul(parport[n], &ep, 0);
+			if(ep == parport[n]){
+				printk(KERN_ERR
+					"bw-qcam: bad port specifier \"%s\"\n",
+					parport[n]);
+				continue;
+			}
+			for (port=parport_enumerate(); port; port=port->next){
+				if(r!=port->number)
+					continue;
+				init_bwqcam(port);
+				break;
+			}
+		}
+		return (num_cams)?0:-ENODEV;
+	} 
+	/* no parameter or "auto" */
 	for (port = parport_enumerate(); port; port=port->next)
 		init_bwqcam(port);
 
diff -u --recursive --new-file v2.2.4/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.2.4/linux/drivers/char/istallion.c	Thu Dec 31 10:29:00 1998
+++ linux/drivers/char/istallion.c	Sun Mar 28 09:02:27 1999
@@ -3,7 +3,7 @@
 /*
  *	istallion.c  -- stallion intelligent multiport serial driver.
  *
- *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
+ *	Copyright (C) 1996-1999  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -27,29 +27,24 @@
 /*****************************************************************************/
 
 #include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/wait.h>
+#include <linux/malloc.h>
 #include <linux/interrupt.h>
-#include <linux/termios.h>
-#include <linux/fcntl.h>
-#include <linux/tty_driver.h>
-#include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/cdk.h>
 #include <linux/comstats.h>
 #include <linux/istallion.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/init.h>
-#include <asm/system.h>
+
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
 /*****************************************************************************/
 
 /*
@@ -79,6 +74,7 @@
 #define	BRD_ECHPCI	26
 #define	BRD_ECH64PCI	27
 #define	BRD_EASYIOPCI	28
+#define	BRD_ECPPCI	29
 
 #define	BRD_BRUMBY	BRD_BRUMBY4
 
@@ -131,7 +127,7 @@
 } stlconf_t;
 
 static stlconf_t	stli_brdconf[] = {
- 	{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+	/*{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },*/
 };
 
 static int	stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t);
@@ -170,7 +166,7 @@
  */
 static char	*stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
 static char	*stli_drvname = "istallion";
-static char	*stli_drvversion = "5.4.7";
+static char	*stli_drvversion = "5.5.1";
 static char	*stli_serialname = "ttyE";
 static char	*stli_calloutname = "cue";
 
@@ -296,8 +292,110 @@
 	"EC8/32-PCI",
 	"EC8/64-PCI",
 	"EasyIO-PCI",
+	"EC/RA-PCI",
 };
 
+/*****************************************************************************/
+
+#ifdef MODULE
+/*
+ *	Define some string labels for arguments passed from the module
+ *	load line. These allow for easy board definitions, and easy
+ *	modification of the io, memory and irq resoucres.
+ */
+
+static char	*board0[8];
+static char	*board1[8];
+static char	*board2[8];
+static char	*board3[8];
+
+static char	**stli_brdsp[] = {
+	(char **) &board0,
+	(char **) &board1,
+	(char **) &board2,
+	(char **) &board3
+};
+
+/*
+ *	Define a set of common board names, and types. This is used to
+ *	parse any module arguments.
+ */
+
+typedef struct stlibrdtype {
+	char	*name;
+	int	type;
+} stlibrdtype_t;
+
+static stlibrdtype_t	stli_brdstr[] = {
+	{ "stallion", BRD_STALLION },
+	{ "1", BRD_STALLION },
+	{ "brumby", BRD_BRUMBY },
+	{ "brumby4", BRD_BRUMBY },
+	{ "brumby/4", BRD_BRUMBY },
+	{ "brumby-4", BRD_BRUMBY },
+	{ "brumby8", BRD_BRUMBY },
+	{ "brumby/8", BRD_BRUMBY },
+	{ "brumby-8", BRD_BRUMBY },
+	{ "brumby16", BRD_BRUMBY },
+	{ "brumby/16", BRD_BRUMBY },
+	{ "brumby-16", BRD_BRUMBY },
+	{ "2", BRD_BRUMBY },
+	{ "onboard2", BRD_ONBOARD2 },
+	{ "onboard-2", BRD_ONBOARD2 },
+	{ "onboard/2", BRD_ONBOARD2 },
+	{ "onboard-mc", BRD_ONBOARD2 },
+	{ "onboard/mc", BRD_ONBOARD2 },
+	{ "onboard-mca", BRD_ONBOARD2 },
+	{ "onboard/mca", BRD_ONBOARD2 },
+	{ "3", BRD_ONBOARD2 },
+	{ "onboard", BRD_ONBOARD },
+	{ "onboardat", BRD_ONBOARD },
+	{ "4", BRD_ONBOARD },
+	{ "onboarde", BRD_ONBOARDE },
+	{ "onboard-e", BRD_ONBOARDE },
+	{ "onboard/e", BRD_ONBOARDE },
+	{ "onboard-ei", BRD_ONBOARDE },
+	{ "onboard/ei", BRD_ONBOARDE },
+	{ "7", BRD_ONBOARDE },
+	{ "ecp", BRD_ECP },
+	{ "ecpat", BRD_ECP },
+	{ "ec8/64", BRD_ECP },
+	{ "ec8/64-at", BRD_ECP },
+	{ "ec8/64-isa", BRD_ECP },
+	{ "23", BRD_ECP },
+	{ "ecpe", BRD_ECPE },
+	{ "ecpei", BRD_ECPE },
+	{ "ec8/64-e", BRD_ECPE },
+	{ "ec8/64-ei", BRD_ECPE },
+	{ "24", BRD_ECPE },
+	{ "ecpmc", BRD_ECPMC },
+	{ "ec8/64-mc", BRD_ECPMC },
+	{ "ec8/64-mca", BRD_ECPMC },
+	{ "25", BRD_ECPMC },
+	{ "ecppci", BRD_ECPPCI },
+	{ "ec/ra", BRD_ECPPCI },
+	{ "ec/ra-pc", BRD_ECPPCI },
+	{ "ec/ra-pci", BRD_ECPPCI },
+	{ "29", BRD_ECPPCI },
+};
+
+/*
+ *	Define the module agruments.
+ */
+MODULE_AUTHOR("Greg Ungerer");
+MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver");
+
+MODULE_PARM(board0, "1-3s");
+MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]");
+MODULE_PARM(board1, "1-3s");
+MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]");
+MODULE_PARM(board2, "1-3s");
+MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]");
+MODULE_PARM(board3, "1-3s");
+MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]");
+
+#endif
+
 /*
  *	Set up a default memory address table for EISA board probing.
  *	The default addresses are all bellow 1Mbyte, which has to be the
@@ -317,18 +415,34 @@
 static int	stli_eisamempsize = sizeof(stli_eisamemprobeaddrs) / sizeof(unsigned long);
 int		stli_eisaprobe = STLI_EISAPROBE;
 
+/*
+ *	Define the Stallion PCI vendor and device IDs.
+ */
+#ifdef CONFIG_PCI
+#ifndef	PCI_VENDOR_ID_STALLION
+#define	PCI_VENDOR_ID_STALLION		0x124d
+#endif
+#ifndef PCI_DEVICE_ID_ECRA
+#define	PCI_DEVICE_ID_ECRA		0x0004
+#endif
+#endif
+
 /*****************************************************************************/
 
 /*
  *	Hardware configuration info for ECP boards. These defines apply
  *	to the directly accessible io ports of the ECP. There is a set of
- *	defines for each ECP board type, ISA, EISA and MCA.
+ *	defines for each ECP board type, ISA, EISA, MCA and PCI.
  */
 #define	ECP_IOSIZE	4
+
 #define	ECP_MEMSIZE	(128 * 1024)
+#define	ECP_PCIMEMSIZE	(256 * 1024)
+
 #define	ECP_ATPAGESIZE	(4 * 1024)
-#define	ECP_EIPAGESIZE	(64 * 1024)
 #define	ECP_MCPAGESIZE	(4 * 1024)
+#define	ECP_EIPAGESIZE	(64 * 1024)
+#define	ECP_PCIPAGESIZE	(64 * 1024)
 
 #define	STL_EISAID	0x8c4e
 
@@ -377,6 +491,14 @@
 #define	ECP_MCDISABLE	0x00
 
 /*
+ *	Important defines for the PCI class of ECP board.
+ *	(It has a lot in common with the other ECP boards.)
+ */
+#define	ECP_PCIIREG	0
+#define	ECP_PCICONFR	1
+#define	ECP_PCISTOP	0x01
+
+/*
  *	Hardware configuration info for ONboard and Brumby boards. These
  *	defines apply to the directly accessible io ports of these boards.
  */
@@ -516,7 +638,11 @@
 /*
  *	Define some handy local macros...
  */
-#define	MIN(a,b)		(((a) <= (b)) ? (a) : (b))
+#undef MIN
+#define	MIN(a,b)	(((a) <= (b)) ? (a) : (b))
+
+#undef	TOLOWER
+#define	TOLOWER(x)	((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
 
 /*****************************************************************************/
 
@@ -527,6 +653,10 @@
 #ifdef MODULE
 int		init_module(void);
 void		cleanup_module(void);
+static void	stli_argbrds(void);
+static int	stli_parsebrd(stlconf_t *confp, char **argp);
+
+static unsigned long	stli_atol(char *str);
 #endif
 
 int		stli_init(void);
@@ -583,6 +713,7 @@
 static int	stli_getportstruct(unsigned long arg);
 static int	stli_getbrdstruct(unsigned long arg);
 static void	*stli_memalloc(int len);
+static stlibrd_t *stli_allocbrd(void);
 
 static void	stli_ecpinit(stlibrd_t *brdp);
 static void	stli_ecpenable(stlibrd_t *brdp);
@@ -599,6 +730,9 @@
 static void	stli_ecpmcdisable(stlibrd_t *brdp);
 static char	*stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
 static void	stli_ecpmcreset(stlibrd_t *brdp);
+static void	stli_ecppciinit(stlibrd_t *brdp);
+static char	*stli_ecppcigetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void	stli_ecppcireset(stlibrd_t *brdp);
 
 static void	stli_onbinit(stlibrd_t *brdp);
 static void	stli_onbenable(stlibrd_t *brdp);
@@ -625,6 +759,12 @@
 static inline int	stli_findeisabrds(void);
 static inline int	stli_eisamemprobe(stlibrd_t *brdp);
 static inline int	stli_initports(stlibrd_t *brdp);
+static inline int	stli_getbrdnr(void);
+
+#ifdef	CONFIG_PCI
+static inline int	stli_findpcibrds(void);
+static inline int	stli_initpcibrd(int brdtype, struct pci_dev *devp);
+#endif
 
 /*****************************************************************************/
 
@@ -741,8 +881,7 @@
 		kfree_s(stli_txcookbuf, STLI_TXBUFSIZE);
 
 	for (i = 0; (i < stli_nrbrds); i++) {
-		brdp = stli_brds[i];
-		if (brdp == (stlibrd_t *) NULL)
+		if ((brdp = stli_brds[i]) == (stlibrd_t *) NULL)
 			continue;
 		for (j = 0; (j < STL_MAXPORTS); j++) {
 			portp = brdp->ports[j];
@@ -763,6 +902,114 @@
 	restore_flags(flags);
 }
 
+/*****************************************************************************/
+
+/*
+ *	Check for any arguments passed in on the module load command line.
+ */
+
+static void stli_argbrds()
+{
+	stlconf_t	conf;
+	stlibrd_t	*brdp;
+	int		nrargs, i;
+
+#if DEBUG
+	printk("stli_argbrds()\n");
+#endif
+
+	nrargs = sizeof(stli_brdsp) / sizeof(char **);
+
+	for (i = stli_nrbrds; (i < nrargs); i++) {
+		memset(&conf, 0, sizeof(conf));
+		if (stli_parsebrd(&conf, stli_brdsp[i]) == 0)
+			continue;
+		if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
+			continue;
+		stli_nrbrds = i + 1;
+		brdp->brdnr = i;
+		brdp->brdtype = conf.brdtype;
+		brdp->iobase = conf.ioaddr1;
+		brdp->memaddr = conf.memaddr;
+		stli_brdinit(brdp);
+	}
+}
+
+/*****************************************************************************/
+
+/*
+ *	Convert an ascii string number into an unsigned long.
+ */
+
+static unsigned long stli_atol(char *str)
+{
+	unsigned long	val;
+	int		base, c;
+	char		*sp;
+
+	val = 0;
+	sp = str;
+	if ((*sp == '0') && (*(sp+1) == 'x')) {
+		base = 16;
+		sp += 2;
+	} else if (*sp == '0') {
+		base = 8;
+		sp++;
+	} else {
+		base = 10;
+	}
+
+	for (; (*sp != 0); sp++) {
+		c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0');
+		if ((c < 0) || (c >= base)) {
+			printk("STALLION: invalid argument %s\n", str);
+			val = 0;
+			break;
+		}
+		val = (val * base) + c;
+	}
+	return(val);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Parse the supplied argument string, into the board conf struct.
+ */
+
+static int stli_parsebrd(stlconf_t *confp, char **argp)
+{
+	char	*sp;
+	int	nrbrdnames, i;
+
+#if DEBUG
+	printk("stli_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp);
+#endif
+
+	if ((argp[0] == (char *) NULL) || (*argp[0] == 0))
+		return(0);
+
+	for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
+		*sp = TOLOWER(*sp);
+
+	nrbrdnames = sizeof(stli_brdstr) / sizeof(stlibrdtype_t);
+	for (i = 0; (i < nrbrdnames); i++) {
+		if (strcmp(stli_brdstr[i].name, argp[0]) == 0)
+			break;
+	}
+	if (i >= nrbrdnames) {
+		printk("STALLION: unknown board name, %s?\n", argp[0]);
+		return(0);
+	}
+
+	confp->brdtype = stli_brdstr[i].type;
+	if ((argp[1] != (char *) NULL) && (*argp[1] != 0))
+		confp->ioaddr1 = stli_atol(argp[1]);
+	if ((argp[2] != (char *) NULL) && (*argp[2] != 0))
+		confp->memaddr = stli_atol(argp[2]);
+	return(1);
+}
+
 #endif
 
 /*****************************************************************************/
@@ -1433,7 +1680,6 @@
 
 		down(&stli_tmpwritesem);
 		copy_from_user(stli_tmpwritebuf, chbuf, count);
-		up(&stli_tmpwritesem);
 		chbuf = &stli_tmpwritebuf[0];
 	}
 
@@ -1485,8 +1731,10 @@
 		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
-
 	EBRDDISABLE(brdp);
+
+	if (from_user)
+		up(&stli_tmpwritesem);
 	restore_flags(flags);
 
 	return(count);
@@ -3363,6 +3611,60 @@
 /*****************************************************************************/
 
 /*
+ *	The following set of functions act on ECP PCI boards.
+ */
+
+static void stli_ecppciinit(stlibrd_t *brdp)
+{
+#if DEBUG
+	printk("stli_ecppciinit(brdp=%x)\n", (int) brdp);
+#endif
+
+	outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
+	udelay(10);
+	outb(0, (brdp->iobase + ECP_PCICONFR));
+	udelay(500);
+}
+
+/*****************************************************************************/
+
+static char *stli_ecppcigetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{	
+	void		*ptr;
+	unsigned char	val;
+
+#if DEBUG
+	printk("stli_ecppcigetmemptr(brdp=%x,offset=%x,line=%d)\n",
+		(int) brdp, (int) offset, line);
+#endif
+
+	if (offset > brdp->memsize) {
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), board=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
+		ptr = 0;
+		val = 0;
+	} else {
+		ptr = brdp->membase + (offset % ECP_PCIPAGESIZE);
+		val = (offset / ECP_PCIPAGESIZE) << 1;
+	}
+	outb(val, (brdp->iobase + ECP_PCICONFR));
+	return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecppcireset(stlibrd_t *brdp)
+{	
+	outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
+	udelay(10);
+	outb(0, (brdp->iobase + ECP_PCICONFR));
+	udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
  *	The following routines act on ONboards.
  */
 
@@ -3678,7 +3980,7 @@
 
 	brdp->iosize = ECP_IOSIZE;
 	if (check_region(brdp->iobase, brdp->iosize))
-		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+		printk("STALLION: Warning, board %d I/O address %x conflicts "
 			"with another device\n", brdp->brdnr, brdp->iobase);
 
 /*
@@ -3729,6 +4031,20 @@
 		name = "serial(EC8/64-MCA)";
 		break;
 
+	case BRD_ECPPCI:
+		brdp->membase = (void *) brdp->memaddr;
+		brdp->memsize = ECP_PCIMEMSIZE;
+		brdp->pagesize = ECP_PCIPAGESIZE;
+		brdp->init = stli_ecppciinit;
+		brdp->enable = NULL;
+		brdp->reenable = NULL;
+		brdp->disable = NULL;
+		brdp->getmemptr = stli_ecppcigetmemptr;
+		brdp->intr = stli_ecpintr;
+		brdp->reset = stli_ecppcireset;
+		name = "serial(EC/RA-PCI)";
+		break;
+
 	default:
 		return(-EINVAL);
 	}
@@ -3817,7 +4133,7 @@
 
 	brdp->iosize = ONB_IOSIZE;
 	if (check_region(brdp->iobase, brdp->iosize))
-		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+		printk("STALLION: Warning, board %d I/O address %x conflicts "
 			"with another device\n", brdp->brdnr, brdp->iobase);
 
 /*
@@ -4082,6 +4398,7 @@
 	case BRD_ECP:
 	case BRD_ECPE:
 	case BRD_ECPMC:
+	case BRD_ECPPCI:
 		stli_initecp(brdp);
 		break;
 	case BRD_ONBOARD:
@@ -4104,20 +4421,20 @@
 			stli_brdnames[brdp->brdtype]);
 		return(ENODEV);
 	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n",
+		printk("STALLION: board=%d is unknown board type=%d\n",
 			brdp->brdnr, brdp->brdtype);
 		return(ENODEV);
 	}
 
 	if ((brdp->state & BST_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n",
+		printk("STALLION: %s board not found, board=%d io=%x mem=%x\n",
 			stli_brdnames[brdp->brdtype], brdp->brdnr,
 			brdp->iobase, (int) brdp->memaddr);
 		return(ENODEV);
 	}
 
 	stli_initports(brdp);
-	printk("STALLION: %s found, unit=%d io=%x mem=%x "
+	printk("STALLION: %s found, board=%d io=%x mem=%x "
 		"nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype],
 		brdp->brdnr, brdp->iobase, (int) brdp->memaddr,
 		brdp->nrpanels, brdp->nrports);
@@ -4279,29 +4596,13 @@
 			continue;
 
 /*
- *		Check that we have room for this new board in our board
- *		info table.
- */
-		if (stli_nrbrds >= STL_MAXBRDS) {
-			printk("STALLION: no room for more probed boards, "
-				"maximum supported %d\n", STL_MAXBRDS);
-			break;
-		}
-
-/*
  *		We have found a Stallion board and it is not configured already.
  *		Allocate a board structure and initialize it.
  */
-		brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
-		if (brdp == (stlibrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory "
-				"(size=%d)\n", sizeof(stlibrd_t));
+		if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
+			return(-ENOMEM);
+		if ((brdp->brdnr = stli_getbrdnr()) < 0)
 			return(-ENOMEM);
-		}
-		memset(brdp, 0, sizeof(stlibrd_t));
-
-		brdp->magic = STLI_BOARDMAGIC;
-		brdp->brdnr = stli_nrbrds++;
 		eid = inb(iobase + 0xc82);
 		if (eid == ECP_EISAID)
 			brdp->brdtype = BRD_ECPE;
@@ -4322,6 +4623,123 @@
 /*****************************************************************************/
 
 /*
+ *	Find the next available board number that is free.
+ */
+
+static inline int stli_getbrdnr()
+{
+	int	i;
+
+	for (i = 0; (i < STL_MAXBRDS); i++) {
+		if (stli_brds[i] == (stlibrd_t *) NULL) {
+			if (i >= stli_nrbrds)
+				stli_nrbrds = i + 1;
+			return(i);
+		}
+	}
+	return(-1);
+}
+
+/*****************************************************************************/
+
+#ifdef	CONFIG_PCI
+
+/*
+ *	We have a Stallion board. Allocate a board structure and
+ *	initialize it. Read its IO and MEMORY resources from PCI
+ *	configuration space.
+ */
+
+static inline int stli_initpcibrd(int brdtype, struct pci_dev *devp)
+{
+	stlibrd_t	*brdp;
+
+#if DEBUG
+	printk("stli_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype,
+		dev->bus->number, dev->devfn);
+#endif
+
+	if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
+		return(-ENOMEM);
+	if ((brdp->brdnr = stli_getbrdnr()) < 0) {
+		printk("STALLION: too many boards found, "
+			"maximum supported %d\n", STL_MAXBRDS);
+		return(0);
+	}
+	brdp->brdtype = brdtype;
+
+#if DEBUG
+	printk("%s(%d): BAR[]=%x,%x,%x,%x\n", __FILE__, __LINE__,
+		devp->base_address[0], devp->base_address[1],
+		devp->base_address[2], devp->base_address[3]);
+#endif
+
+/*
+ *	We have all resources from the board, so lets setup the actual
+ *	board structure now.
+ */
+	brdp->iobase = (devp->base_address[3] & PCI_BASE_ADDRESS_IO_MASK);
+	brdp->memaddr = (devp->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK);
+	stli_brdinit(brdp);
+
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Find all Stallion PCI boards that might be installed. Initialize each
+ *	one as it is found.
+ */
+
+static inline int stli_findpcibrds()
+{
+	struct pci_dev	*dev = NULL;
+	int		rc;
+
+#if DEBUG
+	printk("stli_findpcibrds()\n");
+#endif
+
+	if (! pci_present())
+		return(0);
+
+	while ((dev = pci_find_device(PCI_VENDOR_ID_STALLION,
+	    PCI_DEVICE_ID_ECRA, dev))) {
+		if ((rc = stli_initpcibrd(BRD_ECPPCI, dev)))
+			return(rc);
+	}
+
+	return(0);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *	Allocate a new board structure. Fill out the basic info in it.
+ */
+
+static stlibrd_t *stli_allocbrd()
+{
+	stlibrd_t	*brdp;
+
+	brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
+	if (brdp == (stlibrd_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlibrd_t));
+		return((stlibrd_t *) NULL);
+	}
+
+	memset(brdp, 0, sizeof(stlibrd_t));
+	brdp->magic = STLI_BOARDMAGIC;
+	return(brdp);
+}
+
+/*****************************************************************************/
+
+/*
  *	Scan through all the boards in the configuration and see what we
  *	can find.
  */
@@ -4344,19 +4762,16 @@
 
 /*
  *	Firstly scan the list of static boards configured. Allocate
- *	resources and initialize the boards as found.
+ *	resources and initialize the boards as found. If this is a
+ *	module then let the module args override static configuration.
  */
 	for (i = 0; (i < stli_nrbrds); i++) {
 		confp = &stli_brdconf[i];
-		brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
-		if (brdp == (stlibrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory "
-				"(size=%d)\n", sizeof(stlibrd_t));
+#ifdef MODULE
+		stli_parsebrd(confp, stli_brdsp[i]);
+#endif
+		if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
 			return(-ENOMEM);
-		}
-		memset(brdp, 0, sizeof(stlibrd_t));
-
-		brdp->magic = STLI_BOARDMAGIC;
 		brdp->brdnr = i;
 		brdp->brdtype = confp->brdtype;
 		brdp->iobase = confp->ioaddr1;
@@ -4365,10 +4780,17 @@
 	}
 
 /*
- *	Now go probing for EISA boards if enabled.
+ *	Static configuration table done, so now use dynamic methods to
+ *	see if any more boards should be configured.
  */
+#ifdef MODULE
+	stli_argbrds();
+#endif
 	if (stli_eisaprobe)
 		stli_findeisabrds();
+#ifdef CONFIG_PCI
+	stli_findpcibrds();
+#endif
 
 /*
  *	All found boards are initialized. Now for a little optimization, if
diff -u --recursive --new-file v2.2.4/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.2.4/linux/drivers/char/misc.c	Wed Mar 10 15:29:46 1999
+++ linux/drivers/char/misc.c	Fri Mar 26 13:57:41 1999
@@ -73,6 +73,7 @@
 extern void watchdog_init(void);
 extern void wdt_init(void);
 extern void acq_init(void);
+extern void dtlk_init(void);
 extern void pcwatchdog_init(void);
 extern int rtc_init(void);
 extern int rtc_DP8570A_init(void);
diff -u --recursive --new-file v2.2.4/linux/drivers/char/n_hdlc.c linux/drivers/char/n_hdlc.c
--- v2.2.4/linux/drivers/char/n_hdlc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/n_hdlc.c	Fri Mar 12 08:20:38 1999
@@ -0,0 +1,1151 @@
+/* generic HDLC line discipline for Linux
+ *
+ * Written by Paul Fulghum paulkf@microgate.com
+ * for Microgate Corporation
+ *
+ * Microgate and SyncLink are registered trademarks of Microgate Corporation
+ *
+ * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>,
+ *	Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
+ *
+ * Original release 01/11/99
+ *
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * This module implements the tty line discipline N_HDLC for use with
+ * tty device drivers that support bit-synchronous HDLC communications.
+ *
+ * All HDLC data is frame oriented which means:
+ *
+ * 1. tty write calls represent one complete transmit frame of data
+ *    The device driver should accept the complete frame or none of 
+ *    the frame (busy) in the write method. Each write call should have
+ *    a byte count in the range of 2-4096 bytes (2 is min HDLC frame
+ *    with 1 addr byte and 1 ctrl byte).
+ *
+ * 2. receive callbacks from the device driver represents
+ *    one received frame. The device driver should bypass
+ *    the tty flip buffer and call the line discipline receive
+ *    callback directly to avoid fragmenting or concatenating
+ *    multiple frames into a single receive callback.
+ *
+ *    The HDLC line discipline queues the receive frames in seperate
+ *    buffers so complete receive frames can be returned by the
+ *    tty read calls.
+ *
+ * 3. tty read calls returns an entire frame of data or nothing.
+ *    
+ * 4. all send and receive data is considered raw. No processing
+ *    or translation is performed by the line discipline, regardless
+ *    of the tty flags
+ *
+ * 5. When line discipline is queried for the amount of receive
+ *    data available (FIOC), 0 is returned if no data available,
+ *    otherwise the count of the next available frame is returned.
+ *    (instead of the sum of all received frame counts).
+ *
+ * These conventions allow the standard tty programming interface
+ * to be used for synchronous HDLC applications when used with
+ * this line discipline (or another line discipline that is frame
+ * oriented such as N_PPP).
+ *
+ * The SyncLink driver (synclink.c) implements both asynchronous
+ * (using standard line discipline N_TTY) and synchronous HDLC
+ * (using N_HDLC) communications, with the latter using the above
+ * conventions.
+ *
+ * This implementation is very basic and does not maintain
+ * any statistics. The main point is to enforce the raw data
+ * and frame orientation of HDLC communications.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define HDLC_MAGIC 0x239e
+#define HDLC_VERSION "1.0"
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+
+#undef VERSION
+#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
+
+#if LINUX_VERSION_CODE < VERSION(2,1,14)
+#include <linux/ioport.h>
+#endif
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,23)
+#include <linux/poll.h>
+#endif
+
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/sched.h>	/* to get the struct task_struct */
+#include <linux/string.h>	/* used in new tty drivers */
+#include <linux/signal.h>	/* used in new tty drivers */
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/termios.h>
+#include <linux/if.h>
+
+#include <linux/ioctl.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,4)
+#include <asm/segment.h>
+#define GET_USER(error,value,addr) error = get_user(value,addr)
+#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
+#define PUT_USER(error,value,addr) error = put_user(value,addr)
+#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,5)
+#include <asm/uaccess.h>
+#endif
+
+#else  /* 2.0.x and 2.1.x before 2.1.4 */
+
+#define GET_USER(error,value,addr)					  \
+do {									  \
+	error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \
+	if (error == 0)							  \
+		value = get_user(addr);					  \
+} while (0)
+
+#define COPY_FROM_USER(error,dest,src,size)				  \
+do {									  \
+	error = verify_area (VERIFY_READ, (void *) src, size);		  \
+	if (error == 0)							  \
+		memcpy_fromfs (dest, src, size);			  \
+} while (0)
+
+#define PUT_USER(error,value,addr)					   \
+do {									   \
+	error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \
+	if (error == 0)							   \
+		put_user (value, addr);					   \
+} while (0)
+
+#define COPY_TO_USER(error,dest,src,size)				  \
+do {									  \
+	error = verify_area (VERIFY_WRITE, (void *) dest, size);		  \
+	if (error == 0)							  \
+		memcpy_tofs (dest, src, size);				  \
+} while (0)
+
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,0)
+#define __init
+typedef int spinlock_t;
+#define spin_lock_irqsave(a,b) {save_flags((b));cli();}
+#define spin_unlock_irqrestore(a,b) {restore_flags((b));}
+#define spin_lock(a)
+#define spin_unlock(a)
+#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();}
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,37)
+#define test_and_set_bit(nr, addr)	set_bit(nr, addr)
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,57)
+#define signal_pending(p)	((p)->signal & ~(p)->blocked)
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,25)
+#define net_device_stats	enet_statistics
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,60)
+typedef int		rw_ret_t;
+typedef unsigned int	rw_count_t;
+#else
+typedef ssize_t		rw_ret_t;
+typedef size_t		rw_count_t;
+#endif
+
+/*
+ * Buffers for individual HDLC frames
+ */
+#define MAX_HDLC_FRAME_SIZE 4096
+#define DEFAULT_RX_BUF_COUNT 10
+#define MAX_RX_BUF_COUNT 30
+#define DEFAULT_TX_BUF_COUNT 1
+
+typedef struct _n_hdlc_buf
+{
+	struct _n_hdlc_buf *link;
+	int count;
+	char buf[MAX_HDLC_FRAME_SIZE];
+} N_HDLC_BUF;
+
+typedef struct _n_hdlc_buf_list
+{
+	N_HDLC_BUF *head;
+	N_HDLC_BUF *tail;
+	int count;
+	spinlock_t spinlock;
+	
+} N_HDLC_BUF_LIST;
+
+/*
+ * Per device instance data structure
+ */
+struct n_hdlc {
+	int		magic;		/* magic value for structure	*/
+	__u32		flags;		/* miscellaneous control flags	*/
+	
+	struct tty_struct *tty;		/* ptr to TTY structure	*/
+	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */
+	
+	/* Queues for select() functionality */
+	struct wait_queue *read_wait;
+	struct wait_queue *write_wait;
+
+	int		tbusy;		/* reentrancy flag for tx wakeup code */
+	int		woke_up;
+	N_HDLC_BUF	*tbuf;		/* currently transmitting tx buffer */
+	N_HDLC_BUF_LIST tx_buf_list;	/* list of pending transmit frame buffers */	
+	N_HDLC_BUF_LIST	rx_buf_list;	/* list of received frame buffers */
+	N_HDLC_BUF_LIST tx_free_buf_list;	/* list unused transmit frame buffers */	
+	N_HDLC_BUF_LIST	rx_free_buf_list;	/* list unused received frame buffers */
+};
+
+/*
+ * HDLC buffer list manipulation functions
+ */
+void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list);
+void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf);
+N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list);
+
+/* Local functions */
+
+static struct n_hdlc *n_hdlc_alloc (void);
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,19) 
+MODULE_PARM(debuglevel, "i");
+#endif
+
+/* debug level can be set by insmod for debugging purposes */
+#define DEBUG_LEVEL_INFO	1
+int debuglevel=0;
+
+/* TTY callbacks */
+
+static rw_ret_t n_hdlc_tty_read(struct tty_struct *,
+	struct file *, __u8 *, rw_count_t);
+static rw_ret_t n_hdlc_tty_write(struct tty_struct *,
+	struct file *, const __u8 *, rw_count_t);
+static int n_hdlc_tty_ioctl(struct tty_struct *,
+	struct file *, unsigned int, unsigned long);
+#if LINUX_VERSION_CODE < VERSION(2,1,23)
+static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode,
+		      struct file *filp, int sel_type, select_table * wait);
+#else
+static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, struct file *filp,
+				  poll_table * wait);
+#endif
+static int n_hdlc_tty_open (struct tty_struct *);
+static void n_hdlc_tty_close (struct tty_struct *);
+static int n_hdlc_tty_room (struct tty_struct *tty);
+static void n_hdlc_tty_receive (struct tty_struct *tty,
+	const __u8 * cp, char *fp, int count);
+static void n_hdlc_tty_wakeup (struct tty_struct *tty);
+
+#define bset(p,b)	((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
+
+#define tty2n_hdlc(tty)	((struct n_hdlc *) ((tty)->disc_data))
+#define n_hdlc2tty(n_hdlc)	((n_hdlc)->tty)
+
+/* Define this string only once for all macro invocations */
+static char szVersion[] = HDLC_VERSION;
+
+/* n_hdlc_release()
+ *
+ *	release an n_hdlc per device line discipline info structure
+ *
+ */
+static void n_hdlc_release (struct n_hdlc *n_hdlc)
+{
+	struct tty_struct *tty = n_hdlc2tty (n_hdlc);
+	N_HDLC_BUF *buf;
+	
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__);
+		
+	/* Ensure that the n_hdlcd process is not hanging on select()/poll() */
+	wake_up_interruptible (&n_hdlc->read_wait);
+	wake_up_interruptible (&n_hdlc->write_wait);
+
+	if (tty != NULL && tty->disc_data == n_hdlc)
+		tty->disc_data = NULL;	/* Break the tty->n_hdlc link */
+
+	/* Release transmit and receive buffers */
+	for(;;) {
+		buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);
+		if (buf) {
+			kfree(buf);
+		} else
+			break;
+	}
+	for(;;) {
+		buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);
+		if (buf) {
+			kfree(buf);
+		} else
+			break;
+	}
+	for(;;) {
+		buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
+		if (buf) {
+			kfree(buf);
+		} else
+			break;
+	}
+	for(;;) {
+		buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
+		if (buf) {
+			kfree(buf);
+		} else
+			break;
+	}
+	
+	kfree(n_hdlc);
+	
+}	/* end of n_hdlc_release() */
+
+/* n_hdlc_tty_close()
+ *
+ *	Called when the line discipline is changed to something
+ *	else, the tty is closed, or the tty detects a hangup.
+ */
+static void n_hdlc_tty_close(struct tty_struct *tty)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__);
+		
+	if (n_hdlc != NULL) {
+		if (n_hdlc->magic != HDLC_MAGIC) {
+			printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n");
+			return;
+		}
+		tty->disc_data = NULL;
+		if (tty == n_hdlc->backup_tty)
+			n_hdlc->backup_tty = 0;
+		if (tty != n_hdlc->tty)
+			return;
+		if (n_hdlc->backup_tty) {
+			n_hdlc->tty = n_hdlc->backup_tty;
+		} else {
+			n_hdlc_release (n_hdlc);
+			MOD_DEC_USE_COUNT;
+		}
+	}
+	
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__);
+		
+}	/* end of n_hdlc_tty_close() */
+
+/* n_hdlc_tty_open
+ * 
+ * 	called when line discipline changed to n_hdlc
+ * 	
+ * Arguments:	tty	pointer to tty info structure
+ * Return Value:	0 if success, otherwise error code
+ */
+static int n_hdlc_tty_open (struct tty_struct *tty)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_open() called\n",__FILE__,__LINE__);
+		
+	/* There should not be an existing table for this slot. */
+	if (n_hdlc) {
+		printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" );
+		return -EEXIST;
+	}
+	
+	n_hdlc = n_hdlc_alloc();
+	if (!n_hdlc) {
+		printk (KERN_ERR "n_hdlc_alloc failed\n");
+		return -ENFILE;
+	}
+		
+	tty->disc_data = n_hdlc;
+	n_hdlc->tty    = tty;
+		
+	MOD_INC_USE_COUNT;
+	
+	/* Flush any pending characters in the driver and discipline. */
+	
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer (tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer (tty);
+		
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__);
+		
+	return 0;
+	
+}	/* end of n_tty_hdlc_open() */
+
+/* n_hdlc_send_frames()
+ * 
+ * 	send frames on pending send buffer list until the
+ * 	driver does not accept a frame (busy)
+ * 	this function is called after adding a frame to the
+ * 	send buffer list and by the tty wakeup callback
+ * 	
+ * Arguments:		n_hdlc		pointer to ldisc instance data
+ * 			tty		pointer to tty instance data
+ * Return Value:	None
+ */
+static void n_hdlc_send_frames (struct n_hdlc *n_hdlc, struct tty_struct *tty)
+{
+	register int actual;
+	unsigned long flags;
+	N_HDLC_BUF *tbuf;
+	
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__);
+		
+	save_flags(flags);
+	cli ();
+	if (n_hdlc->tbusy) {
+		n_hdlc->woke_up = 1;
+		restore_flags(flags);
+		return;
+	}
+	n_hdlc->tbusy = 1;
+	restore_flags(flags);
+
+	/* get current transmit buffer or get new transmit */
+	/* buffer from list of pending transmit buffers */
+		
+	tbuf = n_hdlc->tbuf;
+	if (!tbuf)
+		tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
+		
+	while (tbuf) {
+		if (debuglevel >= DEBUG_LEVEL_INFO)	
+			printk("%s(%d)sending frame %p, count=%d\n",
+				__FILE__,__LINE__,tbuf,tbuf->count);
+			
+		/* Send the next block of data to device */
+		n_hdlc->woke_up = 0;
+		tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = tty->driver.write(tty, 0, tbuf->buf, tbuf->count);
+		    
+		/* if transmit error, throw frame away by */
+		/* pretending it was accepted by driver */
+		if (actual < 0)
+			actual = tbuf->count;
+		
+		if (actual == tbuf->count) {
+			if (debuglevel >= DEBUG_LEVEL_INFO)	
+				printk("%s(%d)frame %p completed\n",
+					__FILE__,__LINE__,tbuf);
+					
+			/* free current transmit buffer */
+			n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf);
+			
+			/* this tx buffer is done */
+			n_hdlc->tbuf = NULL;
+			
+			/* wait up sleeping writers */
+			wake_up_interruptible(&n_hdlc->write_wait);
+	
+			/* get next pending transmit buffer */
+			tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
+		} else {
+			if (debuglevel >= DEBUG_LEVEL_INFO)	
+				printk("%s(%d)frame %p pending\n",
+					__FILE__,__LINE__,tbuf);
+					
+			/* buffer not accepted by driver */
+			
+			/* check if wake up code called since last write call */
+			if (n_hdlc->woke_up)
+				continue;
+				
+			/* set this buffer as pending buffer */
+			n_hdlc->tbuf = tbuf;
+			break;
+		}
+	}
+	
+	if (!tbuf)
+		tty->flags  &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	
+	/* Clear the re-entry flag */
+	save_flags(flags);
+	cli ();
+	n_hdlc->tbusy = 0;
+	restore_flags(flags);
+	
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__);
+		
+}	/* end of n_hdlc_send_frames() */
+
+/* n_hdlc_tty_wakeup()
+ *
+ *	Callback for transmit wakeup. Called when low level
+ *	device driver can accept more send data.
+ *
+ * Arguments:		tty	pointer to associated tty instance data
+ * Return Value:	None
+ */
+static void n_hdlc_tty_wakeup (struct tty_struct *tty)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__);
+		
+	if (!n_hdlc)
+		return;
+
+	if (tty != n_hdlc->tty) {
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		return;
+	}
+	
+	if (!n_hdlc->tbuf)
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	else
+		n_hdlc_send_frames (n_hdlc, tty);
+		
+}	/* end of n_hdlc_tty_wakeup() */
+
+/* n_hdlc_tty_room()
+ * 
+ *	Callback function from tty driver. Return the amount of 
+ *	space left in the receiver's buffer to decide if remote
+ *	transmitter is to be throttled.
+ *
+ * Arguments:		tty	pointer to associated tty instance data
+ * Return Value:	number of bytes left in receive buffer
+ */
+static int n_hdlc_tty_room (struct tty_struct *tty)
+{
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_room() called\n",__FILE__,__LINE__);
+	/* always return a larger number to prevent */
+	/* throttling of remote transmitter. */
+	return 65536;
+}	/* end of n_hdlc_tty_root() */
+
+/* n_hdlc_tty_receive()
+ * 
+ * 	Called by tty low level driver when receive data is
+ * 	available. Data is interpreted as one HDLC frame.
+ * 	
+ * Arguments:	 	tty		pointer to tty isntance data
+ * 			data		pointer to received data
+ * 			flags		pointer to flags for data
+ * 			count		count of received data in bytes
+ * 	
+ * Return Value:	None
+ */
+static void n_hdlc_tty_receive(struct tty_struct *tty,
+	const __u8 * data, char *flags, int count)
+{
+	register struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+	register N_HDLC_BUF *buf;
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_receive() called count=%d\n",
+			__FILE__,__LINE__, count);
+		
+	/* This can happen if stuff comes in on the backup tty */
+	if (n_hdlc == 0 || tty != n_hdlc->tty)
+		return;
+		
+	/* verify line is using HDLC discipline */
+	if (n_hdlc->magic != HDLC_MAGIC) {
+		printk("%s(%d) line not using HDLC discipline\n",
+			__FILE__,__LINE__);
+		return;
+	}
+	
+	/* get a free HDLC buffer */	
+	buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);
+	if (!buf) {
+		/* no buffers in free list, attempt to allocate another rx buffer */
+		/* unless the maximum count has been reached */
+		if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT)
+			buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_ATOMIC);
+	}
+	
+	if (!buf) {
+		printk("%s(%d) no more rx buffers, data discarded\n",
+			__FILE__,__LINE__);
+		return;
+	}
+		
+	/* copy received data to HDLC buffer */
+	memcpy(buf->buf,data,count);
+	buf->count=count;
+
+	/* add HDLC buffer to list of received frames */
+	n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf);
+	
+	/* wake up any blocked reads and perform async signalling */
+	wake_up_interruptible (&n_hdlc->read_wait);
+	if (n_hdlc->tty->fasync != NULL)
+		kill_fasync (n_hdlc->tty->fasync, SIGIO);
+
+}	/* end of n_hdlc_tty_receive() */
+
+/* n_hdlc_tty_read()
+ * 
+ * 	Called to retreive one frame of data (if available)
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty instance data
+ * 	file		pointer to open file object
+ * 	buf		pointer to returned data buffer
+ * 	nr		size of returned data buffer
+ * 	
+ * Return Value:
+ * 
+ * 	Number of bytes returned or error code
+ */
+static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty,
+	struct file *file, __u8 * buf, rw_count_t nr)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
+	int error;
+	rw_ret_t ret;
+	N_HDLC_BUF *rbuf;
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__);
+		
+	/* Validate the pointers */
+	if (!n_hdlc)
+		return -EIO;
+
+	/* verify user access to buffer */
+	error = verify_area (VERIFY_WRITE, buf, nr);
+	if (error != 0) {
+		printk(KERN_WARNING"%s(%d) n_hdlc_tty_read() can't verify user "
+		"buffer\n",__FILE__,__LINE__);
+		return (error);
+	}
+
+	for (;;) {
+		n_hdlc = tty2n_hdlc (tty);
+		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
+			 tty != n_hdlc->tty)
+			return 0;
+
+		rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
+		if (rbuf)
+			break;
+			
+		/* no data */
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+			
+		/* TODO: no timeout? current->timeout = 0;*/
+		interruptible_sleep_on (&n_hdlc->read_wait);
+		if (signal_pending(current))
+			return -EINTR;
+	}
+
+	if (rbuf->count > nr) {
+		/* frame too large for caller's buffer (discard frame) */
+		ret = (rw_ret_t)-EOVERFLOW;
+	} else {
+		/* Copy the data to the caller's buffer */
+		COPY_TO_USER(error,buf,rbuf->buf,rbuf->count);
+		if (error)
+			ret = (rw_ret_t)error;
+		else
+			ret = (rw_ret_t)rbuf->count;
+	}
+	
+	/* return HDLC buffer to free list unless the free list */
+	/* count has exceeded the default value, in which case the */
+	/* buffer is freed back to the OS to conserve memory */
+	if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT)
+		kfree(rbuf);
+	else	
+		n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
+	
+	return ret;
+	
+}	/* end of n_hdlc_tty_read() */
+
+/* n_hdlc_tty_write()
+ * 
+ * 	write a single frame of data to device
+ * 	
+ * Arguments:	tty	pointer to associated tty device instance data
+ * 		file	pointer to file object data
+ * 		data	pointer to transmit data (one frame)
+ * 		count	size of transmit frame in bytes
+ * 		
+ * Return Value:	number of bytes written (or error code)
+ */
+static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file,
+	const __u8 * data, rw_count_t count)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+	int error = 0;
+	struct wait_queue wait = {current, NULL};
+	N_HDLC_BUF *tbuf;
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_write() called count=%d\n",
+			__FILE__,__LINE__,count);
+		
+	/* Verify pointers */
+	if (!n_hdlc)
+		return -EIO;
+
+	if (n_hdlc->magic != HDLC_MAGIC)
+		return -EIO;
+
+	/* verify frame size */
+	if (count > MAX_HDLC_FRAME_SIZE) {
+		if (debuglevel & DEBUG_LEVEL_INFO)
+			printk (KERN_WARNING
+				"n_hdlc_tty_write: truncating user packet "
+				"from %lu to %d\n", (unsigned long) count,
+				MAX_HDLC_FRAME_SIZE);
+		count = MAX_HDLC_FRAME_SIZE;
+	}
+	
+	/* Allocate transmit buffer */
+	tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);
+	if (!tbuf) {
+		/* sleep until transmit buffer available */		
+		add_wait_queue(&n_hdlc->write_wait, &wait);
+		while (!tbuf) {
+			/* TODO: no timeout? current->timeout = 0;*/
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+
+			n_hdlc = tty2n_hdlc (tty);
+			if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || 
+			    tty != n_hdlc->tty) {
+				printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc);
+				error = -EIO;
+				break;
+			}
+			
+			if (signal_pending(current)) {
+				error = -EINTR;
+				break;
+			}
+			
+			tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&n_hdlc->write_wait, &wait);
+	}
+
+	if (!error) {		
+		/* Retrieve the user's buffer */
+		COPY_FROM_USER (error, tbuf->buf, data, count);
+		if (error) {
+			/* return tx buffer to free list */
+			n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf);
+		} else {
+			/* Send the data */
+			tbuf->count = error = count;
+			n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
+			n_hdlc_send_frames(n_hdlc,tty);
+		}
+	}
+
+	return error;
+	
+}	/* end of n_hdlc_tty_write() */
+
+/* n_hdlc_tty_ioctl()
+ *
+ *	Process IOCTL system call for the tty device.
+ *
+ * Arguments:
+ *
+ *	tty		pointer to tty instance data
+ *	file		pointer to open file object for device
+ *	cmd		IOCTL command code
+ *	arg		argument for IOCTL call (cmd dependent)
+ *
+ * Return Value:	Command dependent
+ */
+static int n_hdlc_tty_ioctl (struct tty_struct *tty, struct file * file,
+               unsigned int cmd, unsigned long arg)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+	int error = 0;
+	
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
+			__FILE__,__LINE__,cmd);
+		
+	/* Verify the status of the device */
+	if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC)
+		return -EBADF;
+
+	switch (cmd) {
+	case FIONREAD:
+		{
+			/* report count of read data available */
+			/* in next available frame (if any) */
+			int count;
+			unsigned long flags;
+			spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
+			if (n_hdlc->rx_buf_list.head)
+				count = n_hdlc->rx_buf_list.head->count;
+			else
+				count = 0;
+			spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
+			PUT_USER (error, count, (int *) arg);
+		}
+		break;
+		
+	default:
+		error = n_tty_ioctl (tty, file, cmd, arg);
+		break;
+	}
+	return error;
+	
+}	/* end of n_hdlc_tty_ioctl() */
+
+#if LINUX_VERSION_CODE < VERSION(2,1,23)
+/* n_hdlc_tty_select()
+ * 
+ * 	Device select method. Determine if operation requires
+ * 	blocking and if so put appropriate wait queue in select
+ * 	table and return 0, otherwise return 1.
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty device instance data
+ * 	inode		pointer to inode for device
+ * 	filp		pointer to file object
+ * 	sel_type	identified the select type (read/write/exception)
+ * 	wait		select table for adding wait queue if appropriate
+ * 	
+ * Return Value:
+ * 
+ * 	1 if no need to block on operation
+ * 	0 if must block and wait queue added to select table
+ */
+static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode,
+		struct file *filp, int sel_type, select_table * wait)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
+	int result = 1;
+	
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_select() called\n",__FILE__,__LINE__);
+		
+	/* Verify the status of the device */
+	if (!n_hdlc)
+		return -EBADF;
+
+	if (n_hdlc->magic != HDLC_MAGIC || tty != n_hdlc->tty)
+		return -EBADF;
+
+	switch (sel_type) {
+	case SEL_IN:
+		if (n_hdlc->rx_buf_list.head)
+			break;
+
+	case SEL_EX:	/* Exceptions or read errors */
+		/* Is this a pty link and the remote disconnected? */
+		if (tty->flags & (1 << TTY_OTHER_CLOSED))
+			break;
+
+		/* Is this a local link and the modem disconnected? */
+		if (tty_hung_up_p (filp))
+			break;
+
+		select_wait (&n_hdlc->read_wait, wait);
+		result = 0;
+		break;
+		
+	/* Write mode. A write is allowed if there is no current transmission */
+	case SEL_OUT:
+		if (!n_hdlc->tx_free_buf_list.head) {
+			select_wait (&n_hdlc->write_wait, wait);
+			result = 0;
+		}
+		break;
+	}
+	return result;
+}	/* end of n_hdlc_tty_select() */
+
+#else	/* 2.1.23 or later */
+
+/* n_hdlc_tty_poll()
+ * 
+ * 	TTY callback for poll system call. Determine which 
+ * 	operations (read/write) will not block and return
+ * 	info to caller.
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty instance data
+ * 	filp		pointer to open file object for device
+ * 	poll_table	wait queue for operations
+ * 
+ * Return Value:
+ * 
+ * 	bit mask containing info on which ops will not block
+ */
+static unsigned int n_hdlc_tty_poll (struct tty_struct *tty,
+	 struct file *filp, poll_table * wait)
+{
+	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);
+	unsigned int mask = 0;
+
+	if (debuglevel >= DEBUG_LEVEL_INFO)	
+		printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__);
+		
+	if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {
+		/* queue current process into any wait queue that */
+		/* may awaken in the future (read and write) */
+#if LINUX_VERSION_CODE < VERSION(2,1,89)
+		poll_wait(&n_hdlc->read_wait, wait);
+		poll_wait(&n_hdlc->write_wait, wait);
+#else
+		poll_wait(filp, &n_hdlc->read_wait, wait);
+		poll_wait(filp, &n_hdlc->write_wait, wait);
+#endif
+		/* set bits for operations that wont block */
+		if(n_hdlc->rx_buf_list.head)
+			mask |= POLLIN | POLLRDNORM;	/* readable */
+		if(tty->flags & (1 << TTY_OTHER_CLOSED))
+			mask |= POLLHUP;
+		if(tty_hung_up_p(filp))
+			mask |= POLLHUP;
+		if(n_hdlc->tx_free_buf_list.head)
+			mask |= POLLOUT | POLLWRNORM;	/* writable */
+	}
+	return mask;
+}	/* end of n_hdlc_tty_poll() */
+
+#endif
+
+/* n_hdlc_alloc()
+ * 
+ * 	Allocate an n_hdlc instance data structure
+ *
+ * Arguments:		None
+ * Return Value:	pointer to structure if success, otherwise 0	
+ */
+static struct n_hdlc *n_hdlc_alloc (void)
+{
+	struct n_hdlc	*n_hdlc;
+	N_HDLC_BUF	*buf;
+	int		i;
+	
+	n_hdlc = (struct n_hdlc *)kmalloc(sizeof(struct n_hdlc), GFP_KERNEL);
+	if (!n_hdlc)
+		return 0;
+
+	memset(n_hdlc, 0, sizeof(*n_hdlc));
+
+	n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list);
+	n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list);
+	n_hdlc_buf_list_init(&n_hdlc->rx_buf_list);
+	n_hdlc_buf_list_init(&n_hdlc->tx_buf_list);
+	
+	/* allocate free rx buffer list */
+	for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {
+		buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_KERNEL);
+		if (buf)
+			n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,buf);
+	}
+	
+	/* allocate free rx buffer list */
+	for(i=0;i<DEFAULT_TX_BUF_COUNT;i++) {
+		buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_KERNEL);
+		if (buf)
+			n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,buf);
+	}
+	
+	/* Initialize the control block */
+	n_hdlc->magic  = HDLC_MAGIC;
+
+	n_hdlc->flags  = 0;
+	n_hdlc->read_wait  = NULL;
+	n_hdlc->write_wait = NULL;
+	
+	return n_hdlc;
+	
+}	/* end of n_hdlc_alloc() */
+
+/* n_hdlc_buf_list_init()
+ * 
+ * 	initialize specified HDLC buffer list
+ * 	
+ * Arguments:	 	list	pointer to buffer list
+ * Return Value:	None	
+ */
+void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list)
+{
+	memset(list,0,sizeof(N_HDLC_BUF_LIST));
+		
+}	/* end of n_hdlc_buf_list_init() */
+
+/* n_hdlc_buf_put()
+ * 
+ * 	add specified HDLC buffer to tail of specified list
+ * 	
+ * Arguments:
+ * 
+ * 	list	pointer to buffer list
+ * 	buf	pointer to buffer
+ * 
+ * Return Value:	None	
+ */
+void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&list->spinlock,flags);
+	
+	buf->link=NULL;
+	if(list->tail)
+		list->tail->link = buf;
+	else
+		list->head = buf;
+	list->tail = buf;
+	(list->count)++;
+	
+	spin_unlock_irqrestore(&list->spinlock,flags);
+	
+}	/* end of n_hdlc_buf_put() */
+
+/* n_hdlc_buf_get()
+ * 
+ * 	remove and return an HDLC buffer from the
+ * 	head of the specified HDLC buffer list
+ * 	
+ * Arguments:
+ * 
+ * 	list	pointer to HDLC buffer list
+ * 	
+ * Return Value:
+ * 
+ * 	pointer to HDLC buffer if available, otherwise NULL
+ */
+N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list)
+{
+	unsigned long flags;
+	N_HDLC_BUF *buf;
+	spin_lock_irqsave(&list->spinlock,flags);
+	
+	buf = list->head;
+	if (buf) {
+		list->head = buf->link;
+		(list->count)--;
+	}
+	if (!list->head)
+		list->tail = NULL;
+	
+	spin_unlock_irqrestore(&list->spinlock,flags);
+	return buf;
+	
+}	/* end of n_hdlc_buf_get() */
+
+/* init_module()
+ *
+ *	called when module is loading to register line discipline
+ * 	
+ * Arguments:		None
+ * Return Value:	0 if success, otherwise error code
+ */
+int init_module(void)
+{
+	static struct tty_ldisc	n_hdlc_ldisc;
+	int    status;
+
+	printk("HDLC line discipline: version %s\n", szVersion);
+
+	/* Register the tty discipline */
+	
+	memset(&n_hdlc_ldisc, 0, sizeof (n_hdlc_ldisc));
+	n_hdlc_ldisc.magic		= TTY_LDISC_MAGIC;
+#if LINUX_VERSION_CODE >= VERSION(2,1,28)
+	n_hdlc_ldisc.name          	= "hdlc";
+#endif
+	n_hdlc_ldisc.open		= n_hdlc_tty_open;
+	n_hdlc_ldisc.close		= n_hdlc_tty_close;
+	n_hdlc_ldisc.read		= n_hdlc_tty_read;
+	n_hdlc_ldisc.write		= n_hdlc_tty_write;
+	n_hdlc_ldisc.ioctl		= n_hdlc_tty_ioctl;
+#if LINUX_VERSION_CODE < VERSION(2,1,23)
+	n_hdlc_ldisc.select		= n_hdlc_tty_select;
+#else
+	n_hdlc_ldisc.poll		= n_hdlc_tty_poll;
+#endif
+	n_hdlc_ldisc.receive_room	= n_hdlc_tty_room;
+	n_hdlc_ldisc.receive_buf	= n_hdlc_tty_receive;
+	n_hdlc_ldisc.write_wakeup	= n_hdlc_tty_wakeup;
+
+	status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc);
+	if (!status)
+		printk (KERN_INFO"N_HDLC line discipline registered.\n");
+	else
+		printk (KERN_ERR"error registering line discipline: %d\n",status);
+
+	if (status)
+		printk(KERN_INFO"N_HDLC: init failure %d\n", status);
+	return (status);
+	
+}	/* end of init_module() */
+
+/* cleanup_module()
+ *
+ *	called when module is unloading to unregister line discipline
+ * 	
+ * Arguments:		None
+ * Return Value:	None
+ */
+void cleanup_module(void)
+{
+	int status;
+	/* Release tty registration of line discipline */
+	if ((status = tty_register_ldisc(N_HDLC, NULL)))
+		printk("N_HDLC: can't unregister line discipline (err = %d)\n", status);
+	else
+		printk("N_HDLC: line discipline unregistered\n");
+}
diff -u --recursive --new-file v2.2.4/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.2.4/linux/drivers/char/stallion.c	Thu Dec 31 10:29:00 1998
+++ linux/drivers/char/stallion.c	Sun Mar 28 09:02:27 1999
@@ -3,7 +3,7 @@
 /*
  *	stallion.c  -- stallion multiport serial driver.
  *
- *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
+ *	Copyright (C) 1996-1999  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -27,28 +27,18 @@
 /*****************************************************************************/
 
 #include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
+#include <linux/malloc.h>
 #include <linux/interrupt.h>
-#include <linux/termios.h>
-#include <linux/fcntl.h>
-#include <linux/tty_driver.h>
-#include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/cd1400.h>
 #include <linux/sc26198.h>
 #include <linux/comstats.h>
 #include <linux/stallion.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
 #include <linux/ioport.h>
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 
-#include <asm/system.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
@@ -78,7 +68,7 @@
  *	stl_brdconf[] array is a board. Each line contains io/irq/memory
  *	ranges for that board (as well as what type of board it is).
  *	Some examples:
- *		{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }
+ *		{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
  *	This line would configure an EasyIO board (4 or 8, no difference),
  *	at io address 2a0 and irq 10.
  *	Another example:
@@ -105,7 +95,7 @@
 } stlconf_t;
 
 static stlconf_t	stl_brdconf[] = {
-	{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
+	/*{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },*/
 };
 
 static int	stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
@@ -144,7 +134,7 @@
  */
 static char	*stl_drvtitle = "Stallion Multiport Serial Driver";
 static char	*stl_drvname = "stallion";
-static char	*stl_drvversion = "5.4.7";
+static char	*stl_drvversion = "5.5.1";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -259,6 +249,85 @@
 
 /*****************************************************************************/
 
+#ifdef MODULE
+/*
+ *	Define some string labels for arguments passed from the module
+ *	load line. These allow for easy board definitions, and easy
+ *	modification of the io, memory and irq resoucres.
+ */
+
+static char	*board0[4];
+static char	*board1[4];
+static char	*board2[4];
+static char	*board3[4];
+
+static char	**stl_brdsp[] = {
+	(char **) &board0,
+	(char **) &board1,
+	(char **) &board2,
+	(char **) &board3
+};
+
+/*
+ *	Define a set of common board names, and types. This is used to
+ *	parse any module arguments.
+ */
+
+typedef struct stlbrdtype {
+	char	*name;
+	int	type;
+} stlbrdtype_t;
+
+static stlbrdtype_t	stl_brdstr[] = {
+	{ "easyio", BRD_EASYIO },
+	{ "eio", BRD_EASYIO },
+	{ "20", BRD_EASYIO },
+	{ "ec8/32", BRD_ECH },
+	{ "ec8/32-at", BRD_ECH },
+	{ "ec8/32-isa", BRD_ECH },
+	{ "ech", BRD_ECH },
+	{ "echat", BRD_ECH },
+	{ "21", BRD_ECH },
+	{ "ec8/32-mc", BRD_ECHMC },
+	{ "ec8/32-mca", BRD_ECHMC },
+	{ "echmc", BRD_ECHMC },
+	{ "echmca", BRD_ECHMC },
+	{ "22", BRD_ECHMC },
+	{ "ec8/32-pc", BRD_ECHPCI },
+	{ "ec8/32-pci", BRD_ECHPCI },
+	{ "26", BRD_ECHPCI },
+	{ "ec8/64-pc", BRD_ECH64PCI },
+	{ "ec8/64-pci", BRD_ECH64PCI },
+	{ "ech-pci", BRD_ECH64PCI },
+	{ "echpci", BRD_ECH64PCI },
+	{ "echpc", BRD_ECH64PCI },
+	{ "27", BRD_ECH64PCI },
+	{ "easyio-pc", BRD_EASYIOPCI },
+	{ "easyio-pci", BRD_EASYIOPCI },
+	{ "eio-pci", BRD_EASYIOPCI },
+	{ "eiopci", BRD_EASYIOPCI },
+	{ "28", BRD_EASYIOPCI },
+};
+
+/*
+ *	Define the module agruments.
+ */
+MODULE_AUTHOR("Greg Ungerer");
+MODULE_DESCRIPTION("Stallion Multiport Serial Driver");
+
+MODULE_PARM(board0, "1-4s");
+MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]");
+MODULE_PARM(board1, "1-4s");
+MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]");
+MODULE_PARM(board2, "1-4s");
+MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]");
+MODULE_PARM(board3, "1-4s");
+MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]");
+
+#endif
+
+/*****************************************************************************/
+
 /*
  *	Hardware ID bits for the EasyIO and ECH boards. These defines apply
  *	to the directly accessible io ports of these boards (not the uarts -
@@ -399,9 +468,11 @@
 /*
  *	Define some handy local macros...
  */
-#ifndef	MIN
-#define	MIN(a,b)		(((a) <= (b)) ? (a) : (b))
-#endif
+#undef	MIN
+#define	MIN(a,b)	(((a) <= (b)) ? (a) : (b))
+
+#undef	TOLOWER
+#define	TOLOWER(x)	((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
 
 /*****************************************************************************/
 
@@ -412,6 +483,10 @@
 #ifdef MODULE
 int		init_module(void);
 void		cleanup_module(void);
+static void	stl_argbrds(void);
+static int	stl_parsebrd(stlconf_t *confp, char **argp);
+
+static unsigned long stl_atol(char *str);
 #endif
 
 int		stl_init(void);
@@ -459,15 +534,17 @@
 static void	stl_echpci64intr(stlbrd_t *brdp);
 static void	stl_offintr(void *private);
 static void	*stl_memalloc(int len);
+static stlbrd_t *stl_allocbrd(void);
 static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
 
 static inline int	stl_initbrds(void);
 static inline int	stl_initeio(stlbrd_t *brdp);
 static inline int	stl_initech(stlbrd_t *brdp);
+static inline int	stl_getbrdnr(void);
 
 #ifdef	CONFIG_PCI
 static inline int	stl_findpcibrds(void);
-static inline int	stl_initpcibrd(int brdtype, struct pci_dev *dev);
+static inline int	stl_initpcibrd(int brdtype, struct pci_dev *devp);
 #endif
 
 /*
@@ -747,7 +824,8 @@
 		kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE);
 
 	for (i = 0; (i < stl_nrbrds); i++) {
-		brdp = stl_brds[i];
+		if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL)
+			continue;
 		for (j = 0; (j < STL_MAXPANELS); j++) {
 			panelp = brdp->panels[j];
 			if (panelp == (stlpanel_t *) NULL)
@@ -779,6 +857,124 @@
 	restore_flags(flags);
 }
 
+/*****************************************************************************/
+
+/*
+ *	Check for any arguments passed in on the module load command line.
+ */
+
+static void stl_argbrds()
+{
+	stlconf_t	conf;
+	stlbrd_t	*brdp;
+	int		nrargs, i;
+
+#if DEBUG
+	printk("stl_argbrds()\n");
+#endif
+
+	nrargs = sizeof(stl_brdsp) / sizeof(char **);
+
+	for (i = stl_nrbrds; (i < nrargs); i++) {
+		memset(&conf, 0, sizeof(conf));
+		if (stl_parsebrd(&conf, stl_brdsp[i]) == 0)
+			continue;
+		if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
+			continue;
+		stl_nrbrds = i + 1;
+		brdp->brdnr = i;
+		brdp->brdtype = conf.brdtype;
+		brdp->ioaddr1 = conf.ioaddr1;
+		brdp->ioaddr2 = conf.ioaddr2;
+		brdp->irq = conf.irq;
+		brdp->irqtype = conf.irqtype;
+		stl_brdinit(brdp);
+	}
+}
+
+/*****************************************************************************/
+
+/*
+ *	Convert an ascii string number into an unsigned long.
+ */
+
+static unsigned long stl_atol(char *str)
+{
+	unsigned long	val;
+	int		base, c;
+	char		*sp;
+
+	val = 0;
+	sp = str;
+	if ((*sp == '0') && (*(sp+1) == 'x')) {
+		base = 16;
+		sp += 2;
+	} else if (*sp == '0') {
+		base = 8;
+		sp++;
+	} else {
+		base = 10;
+	}
+
+	for (; (*sp != 0); sp++) {
+		c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0');
+		if ((c < 0) || (c >= base)) {
+			printk("STALLION: invalid argument %s\n", str);
+			val = 0;
+			break;
+		}
+		val = (val * base) + c;
+	}
+	return(val);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Parse the supplied argument string, into the board conf struct.
+ */
+
+static int stl_parsebrd(stlconf_t *confp, char **argp)
+{
+	char	*sp;
+	int	nrbrdnames, i;
+
+#if DEBUG
+	printk("stl_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp);
+#endif
+
+	if ((argp[0] == (char *) NULL) || (*argp[0] == 0))
+		return(0);
+
+	for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
+		*sp = TOLOWER(*sp);
+
+	nrbrdnames = sizeof(stl_brdstr) / sizeof(stlbrdtype_t);
+	for (i = 0; (i < nrbrdnames); i++) {
+		if (strcmp(stl_brdstr[i].name, argp[0]) == 0)
+			break;
+	}
+	if (i >= nrbrdnames) {
+		printk("STALLION: unknown board name, %s?\n", argp[0]);
+		return(0);
+	}
+
+	confp->brdtype = stl_brdstr[i].type;
+
+	i = 1;
+	if ((argp[i] != (char *) NULL) && (*argp[i] != 0))
+		confp->ioaddr1 = stl_atol(argp[i]);
+	i++;
+	if (confp->brdtype == BRD_ECH) {
+		if ((argp[i] != (char *) NULL) && (*argp[i] != 0))
+			confp->ioaddr2 = stl_atol(argp[i]);
+		i++;
+	}
+	if ((argp[i] != (char *) NULL) && (*argp[i] != 0))
+		confp->irq = stl_atol(argp[i]);
+	return(1);
+}
+
 #endif
 
 /*****************************************************************************/
@@ -794,6 +990,28 @@
 
 /*****************************************************************************/
 
+/*
+ *	Allocate a new board structure. Fill out the basic info in it.
+ */
+
+static stlbrd_t *stl_allocbrd()
+{
+	stlbrd_t	*brdp;
+
+	brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+	if (brdp == (stlbrd_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlbrd_t));
+		return((stlbrd_t *) NULL);
+	}
+
+	memset(brdp, 0, sizeof(stlbrd_t));
+	brdp->magic = STL_BOARDMAGIC;
+	return(brdp);
+}
+
+/*****************************************************************************/
+
 static int stl_open(struct tty_struct *tty, struct file *filp)
 {
 	stlport_t	*portp;
@@ -1121,7 +1339,6 @@
 		
 		down(&stl_tmpwritesem);
 		copy_from_user(stl_tmpwritebuf, chbuf, count);
-		up(&stl_tmpwritesem);
 		chbuf = &stl_tmpwritebuf[0];
 	}
 
@@ -1154,6 +1371,9 @@
 	clear_bit(ASYI_TXLOW, &portp->istate);
 	stl_startrxtx(portp, -1, 1);
 
+	if (from_user)
+		up(&stl_tmpwritesem);
+
 	return(count);
 }
 
@@ -2185,12 +2405,12 @@
 	}
 
 	if (check_region(brdp->ioaddr1, brdp->iosize1)) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+		printk("STALLION: Warning, board %d I/O address %x conflicts "
 			"with another device\n", brdp->brdnr, brdp->ioaddr1);
 	}
 	if (brdp->iosize2 > 0) {
 		if (check_region(brdp->ioaddr2, brdp->iosize2)) {
-			printk("STALLION: Warning, unit %d I/O address %x "
+			printk("STALLION: Warning, board %d I/O address %x "
 				"conflicts with another device\n",
 				brdp->brdnr, brdp->ioaddr2);
 		}
@@ -2376,7 +2596,7 @@
 		conflict = check_region(brdp->ioaddr2, brdp->iosize2) ?
 			brdp->ioaddr2 : 0;
 	if (conflict) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+		printk("STALLION: Warning, board %d I/O address %x conflicts "
 			"with another device\n", brdp->brdnr, conflict);
 	}
 
@@ -2499,14 +2719,14 @@
 		stl_initech(brdp);
 		break;
 	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n",
+		printk("STALLION: board=%d is unknown board type=%d\n",
 			brdp->brdnr, brdp->brdtype);
 		return(ENODEV);
 	}
 
 	stl_brds[brdp->brdnr] = brdp;
 	if ((brdp->state & BRD_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n",
+		printk("STALLION: %s board not found, board=%d io=%x irq=%d\n",
 			stl_brdnames[brdp->brdtype], brdp->brdnr,
 			brdp->ioaddr1, brdp->irq);
 		return(ENODEV);
@@ -2516,7 +2736,7 @@
 		if (brdp->panels[i] != (stlpanel_t *) NULL)
 			stl_initports(brdp, brdp->panels[i]);
 
-	printk("STALLION: %s found, unit=%d io=%x irq=%d "
+	printk("STALLION: %s found, board=%d io=%x irq=%d "
 		"nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
 		brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
 		brdp->nrports);
@@ -2525,6 +2745,26 @@
 
 /*****************************************************************************/
 
+/*
+ *	Find the next available board number that is free.
+ */
+
+static inline int stl_getbrdnr()
+{
+	int	i;
+
+	for (i = 0; (i < STL_MAXBRDS); i++) {
+		if (stl_brds[i] == (stlbrd_t *) NULL) {
+			if (i >= stl_nrbrds)
+				stl_nrbrds = i + 1;
+			return(i);
+		}
+	}
+	return(-1);
+}
+
+/*****************************************************************************/
+
 #ifdef	CONFIG_PCI
 
 /*
@@ -2533,42 +2773,32 @@
  *	configuration space.
  */
 
-static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev)
+static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp)
 {
-	unsigned int	bar[4];
 	stlbrd_t	*brdp;
-	int		i;
-	unsigned char	irq;
 
 #if DEBUG
-	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n",
-		brdtype, dev->bus->number, dev->devfn);
+	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype,
+		dev->bus->number, dev->devfn);
 #endif
 
-	brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-	if (brdp == (stlbrd_t *) NULL) {
-		printk("STALLION: failed to allocate memory (size=%d)\n",
-			sizeof(stlbrd_t));
+	if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
 		return(-ENOMEM);
+	if ((brdp->brdnr = stl_getbrdnr()) < 0) {
+		printk("STALLION: too many boards found, "
+			"maximum supported %d\n", STL_MAXBRDS);
+		return(0);
 	}
-
-	memset(brdp, 0, sizeof(stlbrd_t));
-	brdp->magic = STL_BOARDMAGIC;
-	brdp->brdnr = stl_nrbrds++;
 	brdp->brdtype = brdtype;
 
 /*
- *	Read in all the BAR registers from this board. Different Stallion
- *	boards use these in different ways, so we just read in the whole
- *	lot and then figure out what is what later.
- */
-	for (i = 0; (i < 4); i++)
-		bar[i] = dev->base_address[i];
-	irq = dev->irq;
-
+ *	Different Stallion boards use the BAR registers in different ways,
+ *	so set up io addresses based on board type.
+ */
 #if DEBUG
 	printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__,
-		bar[0], bar[1], bar[2], bar[3], irq);
+		devp->base_address[0], devp->base_address[1],
+		devp->base_address[2], devp->base_address[3], devp->irq);
 #endif
 
 /*
@@ -2577,29 +2807,34 @@
  */
 	switch (brdtype) {
 	case BRD_ECHPCI:
-		brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK);
-		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (devp->base_address[0] &
+			PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (devp->base_address[1] &
+			PCI_BASE_ADDRESS_IO_MASK);
 		break;
 	case BRD_ECH64PCI:
-		brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
-		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (devp->base_address[2] &
+			PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (devp->base_address[1] &
+			PCI_BASE_ADDRESS_IO_MASK);
 		break;
 	case BRD_EASYIOPCI:
-		brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
-		brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (devp->base_address[2] &
+			PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (devp->base_address[1] &
+			PCI_BASE_ADDRESS_IO_MASK);
 		break;
 	default:
 		printk("STALLION: unknown PCI board type=%d\n", brdtype);
 		break;
 	}
 
-	brdp->irq = irq;
+	brdp->irq = devp->irq;
 	stl_brdinit(brdp);
 
 	return(0);
 }
 
-
 /*****************************************************************************/
 
 /*
@@ -2621,17 +2856,8 @@
 		return(0);
 
 	for (i = 0; (i < stl_nrpcibrds); i++)
-		while ((dev = pci_find_device(stl_pcibrds[i].vendid, stl_pcibrds[i].devid, dev))) {
-
-/*
- *			Check that we can handle more boards...
- */
-			if (stl_nrbrds >= STL_MAXBRDS) {
-				printk("STALLION: too many boards found, "
-					"maximum supported %d\n", STL_MAXBRDS);
-				i = stl_nrpcibrds;
-				break;
-			}
+		while ((dev = pci_find_device(stl_pcibrds[i].vendid,
+		    stl_pcibrds[i].devid, dev))) {
 
 /*
  *			Found a device on the PCI bus that has our vendor and
@@ -2680,15 +2906,11 @@
  */
 	for (i = 0; (i < stl_nrbrds); i++) {
 		confp = &stl_brdconf[i];
-		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-		if (brdp == (stlbrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory "
-				"(size=%d)\n", sizeof(stlbrd_t));
+#ifdef MODULE
+		stl_parsebrd(confp, stl_brdsp[i]);
+#endif
+		if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
 			return(-ENOMEM);
-		}
-		memset(brdp, 0, sizeof(stlbrd_t));
-
-		brdp->magic = STL_BOARDMAGIC;
 		brdp->brdnr = i;
 		brdp->brdtype = confp->brdtype;
 		brdp->ioaddr1 = confp->ioaddr1;
@@ -2698,11 +2920,14 @@
 		stl_brdinit(brdp);
 	}
 
-#ifdef CONFIG_PCI
 /*
- *	If the PCI BIOS support is compiled in then let's go looking for
- *	ECH-PCI boards.
+ *	Find any dynamically supported boards. That is via module load
+ *	line options or auto-detected on the PCI bus.
  */
+#ifdef MODULE
+	stl_argbrds();
+#endif
+#ifdef CONFIG_PCI
 	stl_findpcibrds();
 #endif
 
diff -u --recursive --new-file v2.2.4/linux/drivers/char/synclink.c linux/drivers/char/synclink.c
--- v2.2.4/linux/drivers/char/synclink.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/synclink.c	Sun Mar 28 10:31:31 1999
@@ -0,0 +1,6989 @@
+/*
+ * linux/drivers/char/synclink.c
+ *
+ * Device driver for Microgate SyncLink ISA and PCI
+ * high speed multiprotocol serial adapters.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
+ *
+ * Original release 01/11/99
+ *
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * This driver is primarily intended for use in synchronous
+ * HDLC mode. Asynchronous mode is also provided.
+ *
+ * When operating in synchronous mode, each call to mgsl_write()
+ * contains exactly one complete HDLC frame. Calling mgsl_put_char
+ * will start assembling an HDLC frame that will not be sent until
+ * mgsl_flush_chars or mgsl_write is called.
+ * 
+ * Synchronous receive data is reported as complete frames. To accomplish
+ * this, the TTY flip buffer is bypassed (too small to hold largest
+ * frame and may fragment frames) and the line discipline
+ * receive entry point is called directly.
+ *
+ * This driver has been tested with a slightly modified ppp.c driver
+ * for synchronous PPP.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ 
+#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
+#define BREAKPOINT() asm("   int $3");
+
+#define MAX_ISA_DEVICES 10
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <asm/serial.h>
+#else
+#include <linux/bios32.h>
+#endif
+
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,4)
+#include <asm/segment.h>
+#define GET_USER(error,value,addr) error = get_user(value,addr)
+#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
+#define PUT_USER(error,value,addr) error = put_user(value,addr)
+#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,5)
+#include <asm/uaccess.h>
+#endif
+
+#else  /* 2.0.x and 2.1.x before 2.1.4 */
+
+#define GET_USER(error,value,addr)					  \
+do {									  \
+	error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \
+	if (error == 0)							  \
+		value = get_user(addr);					  \
+} while (0)
+
+#define COPY_FROM_USER(error,dest,src,size)				  \
+do {									  \
+	error = verify_area (VERIFY_READ, (void *) src, size);		  \
+	if (error == 0)							  \
+		memcpy_fromfs (dest, src, size);			  \
+} while (0)
+
+#define PUT_USER(error,value,addr)					   \
+do {									   \
+	error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \
+	if (error == 0)							   \
+		put_user (value, addr);					   \
+} while (0)
+
+#define COPY_TO_USER(error,dest,src,size)				  \
+do {									  \
+	error = verify_area (VERIFY_WRITE, (void *) dest, size);		  \
+	if (error == 0)							  \
+		memcpy_tofs (dest, src, size);				  \
+} while (0)
+
+#endif
+
+#if LINUX_VERSION_CODE < VERSION(2,1,0)
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+#define __init
+#define ioremap(a,b) vremap((a),(b))
+#define iounmap(a) vfree((a))
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+typedef int spinlock_t;
+#define spin_lock_irqsave(a,b) {save_flags((b));cli();}
+#define spin_unlock_irqrestore(a,b) {restore_flags((b));}
+#define spin_lock(a)
+#define spin_unlock(a)
+#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();}
+#define signal_pending(a) ((a)->signal & ~(a)->blocked)
+#endif
+
+
+
+#include "linux/synclink.h"
+
+#define RCLRVALUE 0xffff
+
+MGSL_PARAMS default_params = {
+	MGSL_MODE_HDLC,			/* unsigned long mode */
+	0,				/* unsigned char loopback; */
+	HDLC_FLAG_UNDERRUN_ABORT15,	/* unsigned short flags; */
+	HDLC_ENCODING_NRZI_SPACE,	/* unsigned char encoding; */
+	0,				/* unsigned long clock_speed; */
+	0xff,				/* unsigned char addr_filter; */
+	HDLC_CRC_16_CCITT,		/* unsigned short crc_type; */
+	HDLC_PREAMBLE_LENGTH_8BITS,	/* unsigned char preamble_length; */
+	HDLC_PREAMBLE_PATTERN_NONE,	/* unsigned char preamble; */
+	9600,				/* unsigned long data_rate; */
+	8,				/* unsigned char data_bits; */
+	1,				/* unsigned char stop_bits; */
+	ASYNC_PARITY_NONE		/* unsigned char parity; */
+};
+
+#define SHARED_MEM_ADDRESS_SIZE 0x40000
+#define BUFFERLISTSIZE (PAGE_SIZE)
+#define DMABUFFERSIZE (PAGE_SIZE)
+#define MAXRXFRAMES 7
+
+typedef struct _DMABUFFERENTRY
+{
+	u32 phys_addr;	/* 32-bit flat physical address of data buffer */
+	u16 count;	/* buffer size/data count */
+	u16 status;	/* Control/status field */
+	u16 rcc;	/* character count field */
+	u16 reserved;	/* padding required by 16C32 */
+	u32 link;	/* 32-bit flat link to next buffer entry */
+	char *virt_addr;	/* virtual address of data buffer */
+	u32 phys_entry;	/* physical address of this buffer entry */
+} DMABUFFERENTRY, *DMAPBUFFERENTRY;
+
+/* The queue of BH actions to be performed */
+
+#define BH_TYPE_RECEIVE_DATA	1
+#define BH_TYPE_RECEIVE_STATUS	2
+#define BH_TYPE_RECEIVE_DMA	3
+#define BH_TYPE_TRANSMIT_DATA	4
+#define BH_TYPE_TRANSMIT_STATUS	5
+#define BH_TYPE_STATUS		6
+
+typedef struct _BH_EVENT {
+	unsigned char type;  /* Set by interrupt routines to reqst */
+	u16 status;
+	struct _BH_EVENT *link;
+	
+} BH_EVENT, *BH_QUEUE;     /* Queue of BH actions to be done.  */
+
+#define MAX_BH_QUEUE_ENTRIES 200
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+/*
+ * Device instance data structure
+ */
+ 
+struct mgsl_struct {
+	int			magic;
+	int			flags;
+	int			count;		/* count of opens */
+	int			line;
+	unsigned short		close_delay;
+	unsigned short		closing_wait;	/* time to wait before closing */
+	
+	struct mgsl_icount	icount;
+	
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	
+	struct tty_struct 	*tty;
+	int			timeout;
+	int			x_char;		/* xon/xoff character */
+	int			blocked_open;	/* # of blocked opens */
+	long			session;	/* Session of opening process */
+	long			pgrp;		/* pgrp of opening process */
+	u16			read_status_mask;
+	u16			ignore_status_mask;	
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	
+	struct wait_queue	*status_event_wait_q;
+	struct wait_queue	*event_wait_q;
+	struct timer_list	tx_timer;	/* HDLC transmit timeout timer */
+	struct mgsl_struct	*next_device;	/* device list link */
+	
+	spinlock_t irq_spinlock;		/* spinlock for synchronizing with ISR */
+	struct tq_struct task;		/* task structure for scheduling bh */
+
+	u32 EventMask;			/* event trigger mask */
+	u32 RecordedEvents;		/* pending events */
+
+	u32 max_frame_size;		/* as set by device config */
+
+	BH_EVENT bh_queue[MAX_BH_QUEUE_ENTRIES];		/* Pointer to alloc'ed block */
+	BH_QUEUE bh_queue_head;	/* Queue of BH actions */
+	BH_QUEUE bh_queue_tail;	/* Tail of above for perf. */
+	BH_QUEUE free_bh_queue_head;	/* Queue of Free BH */
+	BH_QUEUE free_bh_queue_tail;	/* Tail of above for perf. */
+	BH_QUEUE bh_action;	/* Action for BH */
+	int bh_running;		/* Protection from multiple */
+	int isr_overflow;
+	int bh_requested;
+
+	char *buffer_list;		/* virtual address of Rx & Tx buffer lists */
+	unsigned long buffer_list_phys;
+
+	unsigned int rx_buffer_count;	/* count of total allocated Rx buffers */
+	DMABUFFERENTRY *rx_buffer_list;	/* list of receive buffer entries */
+	unsigned int current_rx_buffer;
+
+	unsigned int tx_buffer_count;	/* count of total allocated Tx buffers */
+	DMABUFFERENTRY *tx_buffer_list;	/* list of transmit buffer entries */
+
+	int rx_enabled;
+	int rx_overflow;
+
+	int tx_enabled;
+	int tx_active;
+	u32 idle_mode;
+
+	u16 cmr_value;
+
+	char device_name[25];		/* device instance name */
+
+	unsigned int bus_type;	/* expansion bus type (ISA,EISA,PCI) */
+	unsigned char bus;		/* expansion bus number (zero based) */
+	unsigned char function;		/* PCI device number */
+
+	unsigned int io_base;		/* base I/O address of adapter */
+	unsigned int io_addr_size;	/* size of the I/O address range */
+	int io_addr_requested;		/* nonzero if I/O address requested */
+	
+	unsigned int irq_level;		/* interrupt level */
+	unsigned long irq_flags;
+	int irq_requested;		/* nonzero if IRQ requested */
+	
+	unsigned int dma_level;		/* DMA channel */
+	int dma_requested;		/* nonzero if dma channel requested */
+
+	u16 mbre_bit;
+	u16 loopback_bits;
+	u16 usc_idle_mode;
+
+	MGSL_PARAMS params;		/* communications parameters */
+
+	unsigned char serial_signals;	/* current serial signal states */
+
+	int irq_occurred;		/* for diagnostics use */
+	unsigned int init_error;	/* Initialization startup error 		(DIAGS)	*/
+	int	fDiagnosticsmode;	/* Driver in Diagnostic mode?			(DIAGS)	*/
+
+	u32 last_mem_alloc;
+	unsigned char* memory_base;	/* shared memory address (PCI only) */
+	u32 phys_memory_base;
+
+	unsigned char* lcr_base;	/* local config registers (PCI only) */
+	u32 phys_lcr_base;
+	u32 lcr_offset;
+
+	u32 misc_ctrl_value;
+	char flag_buf[HDLC_MAX_FRAME_SIZE];
+	char char_buf[HDLC_MAX_FRAME_SIZE];	
+	BOOLEAN drop_rts_on_tx_done;
+};
+
+#define MGSL_MAGIC 0x5401
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+
+/*
+ * These macros define the offsets used in calculating the
+ * I/O address of the specified USC registers.
+ */
+
+
+#define DCPIN 2		/* Bit 1 of I/O address */
+#define SDPIN 4		/* Bit 2 of I/O address */
+
+#define DCAR 0		/* DMA command/address register */
+#define CCAR SDPIN		/* channel command/address register */
+#define DATAREG DCPIN + SDPIN	/* serial data register */
+#define MSBONLY 0x41
+#define LSBONLY 0x40
+
+/*
+ * These macros define the register address (ordinal number)
+ * used for writing address/value pairs to the USC.
+ */
+
+#define CMR	0x02	/* Channel mode Register */
+#define CCSR	0x04	/* Channel Command/status Register */
+#define CCR	0x06	/* Channel Control Register */
+#define PSR	0x08	/* Port status Register */
+#define PCR	0x0a	/* Port Control Register */
+#define TMDR	0x0c	/* Test mode Data Register */
+#define TMCR	0x0e	/* Test mode Control Register */
+#define CMCR	0x10	/* Clock mode Control Register */
+#define HCR	0x12	/* Hardware Configuration Register */
+#define IVR	0x14	/* Interrupt Vector Register */
+#define IOCR	0x16	/* Input/Output Control Register */
+#define ICR	0x18	/* Interrupt Control Register */
+#define DCCR	0x1a	/* Daisy Chain Control Register */
+#define MISR	0x1c	/* Misc Interrupt status Register */
+#define SICR	0x1e	/* status Interrupt Control Register */
+#define RDR	0x20	/* Receive Data Register */
+#define RMR	0x22	/* Receive mode Register */
+#define RCSR	0x24	/* Receive Command/status Register */
+#define RICR	0x26	/* Receive Interrupt Control Register */
+#define RSR	0x28	/* Receive Sync Register */
+#define RCLR	0x2a	/* Receive count Limit Register */
+#define RCCR	0x2c	/* Receive Character count Register */
+#define TC0R	0x2e	/* Time Constant 0 Register */
+#define TDR	0x30	/* Transmit Data Register */
+#define TMR	0x32	/* Transmit mode Register */
+#define TCSR	0x34	/* Transmit Command/status Register */
+#define TICR	0x36	/* Transmit Interrupt Control Register */
+#define TSR	0x38	/* Transmit Sync Register */
+#define TCLR	0x3a	/* Transmit count Limit Register */
+#define TCCR	0x3c	/* Transmit Character count Register */
+#define TC1R	0x3e	/* Time Constant 1 Register */
+
+
+/*
+ * MACRO DEFINITIONS FOR DMA REGISTERS
+ */
+
+#define DCR	0x06	/* DMA Control Register (shared) */
+#define DACR	0x08	/* DMA Array count Register (shared) */
+#define BDCR	0x12	/* Burst/Dwell Control Register (shared) */
+#define DIVR	0x14	/* DMA Interrupt Vector Register (shared) */	
+#define DICR	0x18	/* DMA Interrupt Control Register (shared) */
+#define CDIR	0x1a	/* Clear DMA Interrupt Register (shared) */
+#define SDIR	0x1c	/* Set DMA Interrupt Register (shared) */
+
+#define TDMR	0x02	/* Transmit DMA mode Register */
+#define TDIAR	0x1e	/* Transmit DMA Interrupt Arm Register */
+#define TBCR	0x2a	/* Transmit Byte count Register */
+#define TARL	0x2c	/* Transmit Address Register (low) */
+#define TARU	0x2e	/* Transmit Address Register (high) */
+#define NTBCR	0x3a	/* Next Transmit Byte count Register */
+#define NTARL	0x3c	/* Next Transmit Address Register (low) */
+#define NTARU	0x3e	/* Next Transmit Address Register (high) */
+
+#define RDMR	0x82	/* Receive DMA mode Register (non-shared) */
+#define RDIAR	0x9e	/* Receive DMA Interrupt Arm Register */
+#define RBCR	0xaa	/* Receive Byte count Register */
+#define RARL	0xac	/* Receive Address Register (low) */
+#define RARU	0xae	/* Receive Address Register (high) */
+#define NRBCR	0xba	/* Next Receive Byte count Register */
+#define NRARL	0xbc	/* Next Receive Address Register (low) */
+#define NRARU	0xbe	/* Next Receive Address Register (high) */
+
+
+/*
+ * MACRO DEFINITIONS FOR MODEM STATUS BITS
+ */
+
+#define MODEMSTATUS_DTR 0x80
+#define MODEMSTATUS_DSR 0x40
+#define MODEMSTATUS_RTS 0x20
+#define MODEMSTATUS_CTS 0x10
+#define MODEMSTATUS_RI  0x04
+#define MODEMSTATUS_DCD 0x01
+
+
+/*
+ * Channel Command/Address Register (CCAR) Command Codes
+ */
+
+#define RTCmd_Null			0x0000
+#define RTCmd_ResetHighestIus		0x1000
+#define RTCmd_TriggerChannelLoadDma	0x2000
+#define RTCmd_TriggerRxDma		0x2800
+#define RTCmd_TriggerTxDma		0x3000
+#define RTCmd_TriggerRxAndTxDma		0x3800
+#define RTCmd_PurgeRxFifo		0x4800
+#define RTCmd_PurgeTxFifo		0x5000
+#define RTCmd_PurgeRxAndTxFifo		0x5800
+#define RTCmd_LoadRcc			0x6800
+#define RTCmd_LoadTcc			0x7000
+#define RTCmd_LoadRccAndTcc		0x7800
+#define RTCmd_LoadTC0			0x8800
+#define RTCmd_LoadTC1			0x9000
+#define RTCmd_LoadTC0AndTC1		0x9800
+#define RTCmd_SerialDataLSBFirst	0xa000
+#define RTCmd_SerialDataMSBFirst	0xa800
+#define RTCmd_SelectBigEndian		0xb000
+#define RTCmd_SelectLittleEndian	0xb800
+
+
+/*
+ * DMA Command/Address Register (DCAR) Command Codes
+ */
+
+#define DmaCmd_Null			0x0000
+#define DmaCmd_ResetTxChannel		0x1000
+#define DmaCmd_ResetRxChannel		0x1200
+#define DmaCmd_StartTxChannel		0x2000
+#define DmaCmd_StartRxChannel		0x2200
+#define DmaCmd_ContinueTxChannel	0x3000
+#define DmaCmd_ContinueRxChannel	0x3200
+#define DmaCmd_PauseTxChannel		0x4000
+#define DmaCmd_PauseRxChannel		0x4200
+#define DmaCmd_AbortTxChannel		0x5000
+#define DmaCmd_AbortRxChannel		0x5200
+#define DmaCmd_InitTxChannel		0x7000
+#define DmaCmd_InitRxChannel		0x7200
+#define DmaCmd_ResetHighestDmaIus	0x8000
+#define DmaCmd_ResetAllChannels		0x9000
+#define DmaCmd_StartAllChannels		0xa000
+#define DmaCmd_ContinueAllChannels	0xb000
+#define DmaCmd_PauseAllChannels		0xc000
+#define DmaCmd_AbortAllChannels		0xd000
+#define DmaCmd_InitAllChannels		0xf000
+
+#define TCmd_Null			0x0000
+#define TCmd_ClearTxCRC			0x2000
+#define TCmd_SelectTicrTtsaData		0x4000
+#define TCmd_SelectTicrTxFifostatus	0x5000
+#define TCmd_SelectTicrIntLevel		0x6000
+#define TCmd_SelectTicrdma_level		0x7000
+#define TCmd_SendFrame			0x8000
+#define TCmd_SendAbort			0x9000
+#define TCmd_EnableDleInsertion		0xc000
+#define TCmd_DisableDleInsertion	0xd000
+#define TCmd_ClearEofEom		0xe000
+#define TCmd_SetEofEom			0xf000
+
+#define RCmd_Null			0x0000
+#define RCmd_ClearRxCRC			0x2000
+#define RCmd_EnterHuntmode		0x3000
+#define RCmd_SelectRicrRtsaData		0x4000
+#define RCmd_SelectRicrRxFifostatus	0x5000
+#define RCmd_SelectRicrIntLevel		0x6000
+#define RCmd_SelectRicrdma_level		0x7000
+
+/*
+ * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR)
+ */
+ 
+#define RECEIVE_STATUS		BIT5
+#define RECEIVE_DATA		BIT4
+#define TRANSMIT_STATUS		BIT3
+#define TRANSMIT_DATA		BIT2
+#define IO_PIN			BIT1
+#define MISC			BIT0
+
+
+/*
+ * Receive status Bits in Receive Command/status Register RCSR
+ */
+
+#define RXSTATUS_SHORT_FRAME		BIT8
+#define RXSTATUS_CODE_VIOLATION		BIT8
+#define RXSTATUS_EXITED_HUNT		BIT7
+#define RXSTATUS_IDLE_RECEIVED		BIT6
+#define RXSTATUS_BREAK_RECEIVED		BIT5
+#define RXSTATUS_ABORT_RECEIVED		BIT5
+#define RXSTATUS_RXBOUND		BIT4
+#define RXSTATUS_CRC_ERROR		BIT3
+#define RXSTATUS_FRAMING_ERROR		BIT3
+#define RXSTATUS_ABORT			BIT2
+#define RXSTATUS_PARITY_ERROR		BIT2
+#define RXSTATUS_OVERRUN		BIT1
+#define RXSTATUS_DATA_AVAILABLE		BIT0
+#define RXSTATUS_ALL			0x01f6
+#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) )
+
+/*
+ * Values for setting transmit idle mode in 
+ * Transmit Control/status Register (TCSR)
+ */
+#define IDLEMODE_FLAGS			0x0000
+#define IDLEMODE_ALT_ONE_ZERO		0x0100
+#define IDLEMODE_ZERO			0x0200
+#define IDLEMODE_ONE			0x0300
+#define IDLEMODE_ALT_MARK_SPACE		0x0500
+#define IDLEMODE_SPACE			0x0600
+#define IDLEMODE_MARK			0x0700
+
+/*
+ * Transmit status Bits in Transmit Command/status Register (TCSR)
+ */
+
+#define TCSR_PRESERVE			0x0700
+
+#define TXSTATUS_PREAMBLE_SENT		BIT7
+#define TXSTATUS_IDLE_SENT		BIT6
+#define TXSTATUS_ABORT_SENT		BIT5
+#define TXSTATUS_EOF_SENT		BIT4
+#define TXSTATUS_EOM_SENT		BIT4
+#define TXSTATUS_CRC_SENT		BIT3
+#define TXSTATUS_ALL_SENT		BIT2
+#define TXSTATUS_UNDERRUN		BIT1
+#define TXSTATUS_FIFO_EMPTY		BIT0
+#define TXSTATUS_ALL			0x00fa
+#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->usc_idle_mode + ((b) & 0x00FF)) )
+				
+
+#define MISCSTATUS_RXC_LATCHED		BIT15
+#define MISCSTATUS_RXC			BIT14
+#define MISCSTATUS_TXC_LATCHED		BIT13
+#define MISCSTATUS_TXC			BIT12
+#define MISCSTATUS_RI_LATCHED		BIT11
+#define MISCSTATUS_RI			BIT10
+#define MISCSTATUS_DSR_LATCHED		BIT9
+#define MISCSTATUS_DSR			BIT8
+#define MISCSTATUS_DCD_LATCHED		BIT7
+#define MISCSTATUS_DCD			BIT6
+#define MISCSTATUS_CTS_LATCHED		BIT5
+#define MISCSTATUS_CTS			BIT4
+#define MISCSTATUS_RCC_UNDERRUN		BIT3
+#define MISCSTATUS_DPLL_NO_SYNC		BIT2
+#define MISCSTATUS_BRG1_ZERO		BIT1
+#define MISCSTATUS_BRG0_ZERO		BIT0
+
+#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0))
+#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f))
+
+#define SICR_RXC_ACTIVE			BIT15
+#define SICR_RXC_INACTIVE		BIT14
+#define SICR_RXC			(BIT15+BIT14)
+#define SICR_TXC_ACTIVE			BIT13
+#define SICR_TXC_INACTIVE		BIT12
+#define SICR_TXC			(BIT13+BIT12)
+#define SICR_RI_ACTIVE			BIT11
+#define SICR_RI_INACTIVE		BIT10
+#define SICR_RI				(BIT11+BIT10)
+#define SICR_DSR_ACTIVE			BIT9
+#define SICR_DSR_INACTIVE		BIT8
+#define SICR_DSR			(BIT9+BIT8)
+#define SICR_DCD_ACTIVE			BIT7
+#define SICR_DCD_INACTIVE		BIT6
+#define SICR_DCD			(BIT7+BIT6)
+#define SICR_CTS_ACTIVE			BIT5
+#define SICR_CTS_INACTIVE		BIT4
+#define SICR_CTS			(BIT5+BIT4)
+#define SICR_RCC_UNDERFLOW		BIT3
+#define SICR_DPLL_NO_SYNC		BIT2
+#define SICR_BRG1_ZERO			BIT1
+#define SICR_BRG0_ZERO			BIT0
+
+void usc_DisableMasterIrqBit( struct mgsl_struct *info );
+void usc_EnableMasterIrqBit( struct mgsl_struct *info );
+void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask );
+void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask );
+void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask );
+
+#define usc_EnableInterrupts( a, b ) \
+	usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) )
+
+#define usc_DisableInterrupts( a, b ) \
+	usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) )
+
+#define usc_EnableMasterIrqBit(a) \
+	usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) )
+
+#define usc_DisableMasterIrqBit(a) \
+	usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) )
+
+#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) )
+
+/*
+ * Transmit status Bits in Transmit Control status Register (TCSR)
+ * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0)
+ */
+
+#define TXSTATUS_PREAMBLE_SENT	BIT7
+#define TXSTATUS_IDLE_SENT	BIT6
+#define TXSTATUS_ABORT_SENT	BIT5
+#define TXSTATUS_EOF		BIT4
+#define TXSTATUS_CRC_SENT	BIT3
+#define TXSTATUS_ALL_SENT	BIT2
+#define TXSTATUS_UNDERRUN	BIT1
+#define TXSTATUS_FIFO_EMPTY	BIT0
+
+#define DICR_MASTER		BIT15
+#define DICR_TRANSMIT		BIT0
+#define DICR_RECEIVE		BIT1
+
+#define usc_EnableDmaInterrupts(a,b) \
+	usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) )
+
+#define usc_DisableDmaInterrupts(a,b) \
+	usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) )
+
+#define usc_EnableStatusIrqs(a,b) \
+	usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) )
+
+#define usc_DisablestatusIrqs(a,b) \
+	usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) )
+
+/* Transmit status Bits in Transmit Control status Register (TCSR) */
+/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */
+
+
+#define DISABLE_UNCONDITIONAL    0
+#define DISABLE_END_OF_FRAME     1
+#define ENABLE_UNCONDITIONAL     2
+#define ENABLE_AUTO_CTS          3
+#define ENABLE_AUTO_DCD          3
+#define usc_EnableTransmitter(a,b) \
+	usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) )
+#define usc_EnableReceiver(a,b) \
+	usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) )
+
+u16  usc_InDmaReg( struct mgsl_struct *info, u16 Port );
+void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value );
+void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd );
+
+u16  usc_InReg( struct mgsl_struct *info, u16 Port );
+void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value );
+void usc_RTCmd( struct mgsl_struct *info, u16 Cmd );
+void usc_RCmd( struct mgsl_struct *info, u16 Cmd );
+void usc_TCmd( struct mgsl_struct *info, u16 Cmd );
+
+#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->usc_idle_mode + (b)))
+#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b))
+
+void usc_start_receiver( struct mgsl_struct *info );
+void usc_stop_receiver( struct mgsl_struct *info );
+
+void usc_start_transmitter( struct mgsl_struct *info );
+void usc_stop_transmitter( struct mgsl_struct *info );
+void usc_set_txidle( struct mgsl_struct *info );
+void usc_load_txfifo( struct mgsl_struct *info );
+
+void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate );
+void usc_enable_loopback( struct mgsl_struct *info, int enable );
+
+void usc_get_serial_signals( struct mgsl_struct *info );
+void usc_set_serial_signals( struct mgsl_struct *info );
+
+void usc_reset( struct mgsl_struct *info );
+
+void usc_set_sync_mode( struct mgsl_struct *info );
+void usc_set_sdlc_mode( struct mgsl_struct *info );
+void usc_set_async_mode( struct mgsl_struct *info );
+void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate );
+
+void usc_loopback_frame( struct mgsl_struct *info );
+
+void mgsl_tx_timeout(unsigned long context);
+
+/*
+ * Defines a BUS descriptor value for the PCI adapter
+ * local bus address ranges.
+ */
+
+#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \
+(0x00400020 + \
+((WrHold) << 30) + \
+((WrDly)  << 28) + \
+((RdDly)  << 26) + \
+((Nwdd)   << 20) + \
+((Nwad)   << 15) + \
+((Nxda)   << 13) + \
+((Nrdd)   << 11) + \
+((Nrad)   <<  6) )
+
+void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit);
+
+/*
+ * Adapter diagnostic routines
+ */
+BOOLEAN mgsl_register_test( struct mgsl_struct *info );
+BOOLEAN mgsl_irq_test( struct mgsl_struct *info );
+BOOLEAN mgsl_dma_test( struct mgsl_struct *info );
+BOOLEAN mgsl_memory_test( struct mgsl_struct *info );
+int mgsl_adapter_test( struct mgsl_struct *info );
+
+/*
+ * device and resource management routines
+ */
+int mgsl_claim_resources(struct mgsl_struct *info);
+void mgsl_release_resources(struct mgsl_struct *info);
+void mgsl_add_device(struct mgsl_struct *info);
+struct mgsl_struct* mgsl_allocate_device(void);
+int mgsl_enumerate_devices(void);
+
+/*
+ * DMA buffer manupulation functions.
+ */
+void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex );
+int  mgsl_get_rx_frame( struct mgsl_struct *info );
+void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info );
+void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize);
+void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count);
+
+/*
+ * DMA and Shared Memory buffer allocation and formatting
+ */
+int  mgsl_allocate_dma_buffers(struct mgsl_struct *info);
+void mgsl_free_dma_buffers(struct mgsl_struct *info);
+int  mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount);
+void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount);
+int  mgsl_alloc_buffer_list_memory(struct mgsl_struct *info);
+void mgsl_free_buffer_list_memory(struct mgsl_struct *info);
+
+/*
+ * Bottom half interrupt handlers
+ */
+void mgsl_bh_handler(void* Context);
+void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status );
+void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount );
+void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status );
+
+void mgsl_format_bh_queue( struct mgsl_struct *info );
+void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status );
+int mgsl_bh_queue_get( struct mgsl_struct *info );
+
+
+/*
+ * Interrupt handler routines and dispatch table.
+ */
+void mgsl_isr_null( struct mgsl_struct *info );
+void mgsl_isr_transmit_data( struct mgsl_struct *info );
+void mgsl_isr_receive_data( struct mgsl_struct *info );
+void mgsl_isr_receive_status( struct mgsl_struct *info );
+void mgsl_isr_transmit_status( struct mgsl_struct *info );
+void mgsl_isr_io_pin( struct mgsl_struct *info );
+void mgsl_isr_misc( struct mgsl_struct *info );
+void mgsl_isr_receive_dma( struct mgsl_struct *info );
+
+typedef void (*isr_dispatch_func)(struct mgsl_struct *);
+
+isr_dispatch_func UscIsrTable[7] =
+{
+	mgsl_isr_null,
+	mgsl_isr_misc,
+	mgsl_isr_io_pin,
+	mgsl_isr_transmit_data,
+	mgsl_isr_transmit_status,
+	mgsl_isr_receive_data,
+	mgsl_isr_receive_status
+};
+
+/*
+ * ioctl call handlers
+ */
+static int set_modem_info(struct mgsl_struct * info, unsigned int cmd,
+			  unsigned int *value);
+static int get_modem_info(struct mgsl_struct * info, unsigned int *value);
+static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount
+	*user_icount);
+static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS *user_params);
+static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS *new_params);
+static int mgsl_get_txidle(struct mgsl_struct * info, int*idle_mode);
+static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode);
+static int mgsl_txenable(struct mgsl_struct * info, int enable);
+static int mgsl_txabort(struct mgsl_struct * info);
+static int mgsl_rxenable(struct mgsl_struct * info, int enable);
+static int mgsl_wait_event(struct mgsl_struct * info, int mask);
+
+#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
+
+/*
+ * Global linked list of SyncLink devices
+ */
+struct mgsl_struct *mgsl_device_list = NULL;
+int mgsl_device_count = 0;
+
+/*
+ * Set this param to non-zero to load eax with the
+ * .text section address and breakpoint on module load.
+ * This is useful for use with gdb and add-symbol-file command.
+ */
+int break_on_load=0;
+
+/*
+ * Driver major number, defaults to zero to get auto
+ * assigned major number. May be forced as module parameter.
+ */
+int ttymajor=0;
+
+int cuamajor=0;
+
+/*
+ * Array of user specified options for ISA adapters.
+ */
+static int io[MAX_ISA_DEVICES] = {0,};
+static int irq[MAX_ISA_DEVICES] = {0,};
+static int dma[MAX_ISA_DEVICES] = {0,};
+static int debug_level = 0;
+
+	
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+MODULE_PARM(break_on_load,"i");
+MODULE_PARM(ttymajor,"i");
+MODULE_PARM(cuamajor,"i");
+MODULE_PARM(io,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i");
+MODULE_PARM(irq,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i");
+MODULE_PARM(dma,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i");
+MODULE_PARM(debug_level,"i");
+#endif
+
+static char *driver_name = "SyncLink serial driver";
+static char *driver_version = "1.00";
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+
+static void mgsl_change_params(struct mgsl_struct *info);
+static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout);
+
+static struct tty_struct **serial_table = NULL;
+static struct termios **serial_termios = NULL;
+static struct termios **serial_termios_locked = NULL;
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * 1st function defined in .text section. Calling this function in
+ * init_module() followed by a breakpoint allows a remote debugger
+ * (gdb) to get the .text address for the add-symbol-file command.
+ * This allows remote debugging of dynamically loadable modules.
+ */
+void* mgsl_get_text_ptr(void);
+void* mgsl_get_text_ptr() {return mgsl_get_text_ptr;}
+
+/*
+ * tmp_buf is used as a temporary buffer by mgsl_write.  We need to
+ * lock it in case the COPY_FROM_USER blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ioports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int mgsl_paranoia_check(struct mgsl_struct *info,
+					kdev_t device, const char *routine)
+{
+#ifdef MGSL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for mgsl struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null mgsl_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != MGSL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/* mgsl_stop()		throttle (stop) transmitter
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static void mgsl_stop(struct tty_struct *tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_stop"))
+		return;
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("mgsl_stop(%s)\n",info->device_name);	
+		
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if (info->tx_enabled)
+	 	usc_stop_transmitter(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+}	/* end of mgsl_stop() */
+
+/* mgsl_start()		release (start) transmitter
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static void mgsl_start(struct tty_struct *tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_start"))
+		return;
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("mgsl_start(%s)\n",info->device_name);	
+		
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if (!info->tx_enabled)
+	 	usc_start_transmitter(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+}	/* end of mgsl_start() */
+
+/*
+ * Bottom half work queue access functions
+ */
+
+/* mgsl_format_bh_queue()
+ * 
+ * 	Initialize the bottom half processing queue
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_format_bh_queue( struct mgsl_struct *info )
+{
+	BH_QUEUE bh_queue = info->bh_queue;
+	int i;
+
+	/* go through sequentially tacking the little bits together */
+
+	for ( i=0; i < MAX_BH_QUEUE_ENTRIES; i++ ) {
+		if ( info->free_bh_queue_tail == NULL )
+			info->free_bh_queue_head = bh_queue;
+		else
+			info->free_bh_queue_tail->link = bh_queue;
+		info->free_bh_queue_tail = bh_queue++;
+	}
+
+	/* As a safety measure, mark the end of the chain with a NULL */
+	info->free_bh_queue_tail->link = NULL;
+
+}	/* end of mgsl_format_bh_queue() */
+
+/* mgsl_bh_queue_put()
+ * 
+ * 	Add a BH event to the BH queue
+ * 
+ * Arguments:		info		pointer to device instance data
+ * 			type	BH event type
+ * 			status		BH event status
+ * 
+ * Return Value:	None
+ */
+void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status )
+{
+	BH_EVENT *event = info->free_bh_queue_head;
+
+	if ( event != NULL ) {
+		/* remove free element from head of free list */
+		info->free_bh_queue_head = event->link;
+		event->link = NULL;
+
+		/* file out new BH event */
+		event->type = type;
+		event->status = status;
+
+		/* add element to tail of pending list */
+		if ( info->bh_queue_head != NULL ){
+			/* BH queue is not empty, add current element to tail */
+			info->bh_queue_tail->link = event;
+		} else {
+			/* the BH queue is empty so this element becomes the head of queue */
+			info->bh_queue_head = event;
+		}
+
+		/* the new element becomes tail of queue */
+		info->bh_queue_tail = event;
+	} else {
+		/* No more free BH action elements in queue. */
+		/* This happens when too many interrupts are occuring */
+		/* for the mgsl_bh_handler to process so set a flag. */
+
+		info->isr_overflow = 1;
+	}
+
+}	/* end of mgsl_bh_queue_put() */
+
+/* mgsl_bh_queue_get()
+ * 
+ *	Free the current work item (if any) and get the
+ * 	next work item from the head of the pending work item queue.
+ *
+ * Effects:
+ * 
+ * 	If a BH action element is available on the BH action queue
+ * 	then the head of the queue is removed and bh_action
+ * 	is set to point to the removed element.
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	1 if BH action removed from queue
+ */
+int mgsl_bh_queue_get( struct mgsl_struct *info )
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+
+	if ( info->bh_action ) {
+		/* free the current work item */
+		if ( info->free_bh_queue_head != NULL ){
+			/* free queue is not empty, add current element to tail */
+			info->free_bh_queue_tail->link = info->bh_action;
+		} else {
+			/* free queue is empty so this element becomes the head of queue */
+			info->free_bh_queue_head = info->bh_action;
+		}
+
+		/* add element to tail of free queue */
+		info->free_bh_queue_tail = info->bh_action;
+		info->free_bh_queue_tail->link = NULL;
+	}
+	
+	/* attempt to remove element from head of queue */
+	info->bh_action = info->bh_queue_head;
+
+	if ( info->bh_action != NULL ){
+		/* BH queue is not empty, remove element from queue head */
+		info->bh_queue_head = info->bh_action->link;
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		return 1;
+	}
+
+	/* Mark BH routine as complete */
+	info->bh_running   = 0;
+	info->bh_requested = 0;
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	return 0;
+
+}	/* end of mgsl_bh_queue_get() */
+
+/* mgsl_bh_handler()
+ * 
+ * 	Perform bottom half processing of work items queued by ISR.
+ *
+ * Arguments:		Context		pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_bh_handler(void* Context)
+{
+	struct mgsl_struct *info = (struct mgsl_struct*)Context;
+	
+	if (!info)
+		return;
+		
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):mgsl_bh_handler(%s) entry\n",
+			__FILE__,__LINE__,info->device_name);
+	
+	info->bh_running = 1;
+
+	/* Attempt to clear out the BH queue */
+
+	while( mgsl_bh_queue_get(info) ) {
+	
+		/* Process work item */
+		if ( debug_level >= DEBUG_LEVEL_BH )
+			printk( "%s(%d):mgsl_bh_handler() work item action=%d\n",
+				__FILE__,__LINE__,info->bh_action->type);
+
+		switch ( info->bh_action->type ) {
+		
+		case BH_TYPE_RECEIVE_DMA:
+			mgsl_bh_receive_dma( info, info->bh_action->status );
+			break;
+
+		case BH_TYPE_TRANSMIT_STATUS:
+		case BH_TYPE_TRANSMIT_DATA:
+			mgsl_bh_transmit_data( info, info->bh_action->status );
+			break;
+
+		case BH_TYPE_STATUS:
+			mgsl_bh_status_handler( info, info->bh_action->status );
+			break;
+
+		default:
+			/* unknown work item ID */
+			printk("Unknown work item ID=%08X!\n",
+				info->bh_action->type );
+			break;
+		}
+	}
+
+	if ( info->isr_overflow ) {
+		printk("ISR overflow detected.\n");
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):mgsl_bh_handler(%s) exit\n",
+			__FILE__,__LINE__,info->device_name);
+	
+}	/* end of mgsl_bh_handler() */
+
+/* mgsl_bh_receive_dma()
+ * 
+ * 	Perform bottom half processing for a receive DMA interrupt
+ * 	This occurs in HDLC mode after a DMA buffer has terminated
+ * 	or the DMA buffers have been exhausted.
+ * 
+ * Arguments:
+ * 
+ * 	info		pointer to device instance data
+ * 	status		status word
+ * 
+ * Return Value:	None
+ */
+void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status )
+{
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):mgsl_bh_receive_dma(%s)\n",
+			__FILE__,__LINE__,info->device_name);
+	
+	while( mgsl_get_rx_frame(info) );
+
+}	/* end of mgsl_bh_receive_dma() */
+
+/* mgsl_bh_transmit_data()
+ * 
+ * 	Process a transmit data interrupt event
+ * 	This occurs in asynchronous communications mode.
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount )
+{
+	struct tty_struct *tty = info->tty;
+	
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n",
+			__FILE__,__LINE__,info->device_name);
+			
+	/* wakeup any waiting write requests */
+ 	if (tty) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup) {
+			if ( debug_level >= DEBUG_LEVEL_BH )
+				printk( "%s(%d):calling ldisc.write_wakeup on %s\n",
+					__FILE__,__LINE__,info->device_name);
+			(tty->ldisc.write_wakeup)(tty);
+		}
+		wake_up_interruptible(&tty->write_wait);
+	}
+	
+}	/* End Of mgsl_bh_transmit_data() */
+
+/* mgsl_bh_status_handler()
+ * 
+ * 	Peform bottom half processing for a status interrupt
+ * 
+ * 	This event is generated when a I/O pin (serial signal)
+ * 	has a transition. If there is a pending WaitEvent call
+ * 	and the status transition is identified in the EventMast
+ * 	of the pending call then complete the pending call.
+ * 
+ * Arguments:
+ * 
+ * 	info		pointer to device instance data
+ * 	status		status word
+ * 
+ * Return Value:	None
+ */
+void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status )
+{
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n",
+			__FILE__,__LINE__,info->device_name);
+
+}	/* End Of mgsl_bh_status_handler() */
+
+/* mgsl_isr_receive_status()
+ * 
+ *	Service a receive status interrupt. The type of status
+ *	interrupt is indicated by the state of the RCSR.
+ *	This is only used for HDLC mode.
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_receive_status( struct mgsl_struct *info )
+{
+	u16 status = usc_InReg( info, RCSR );
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_receive_status status=%04X\n",
+			__FILE__,__LINE__,status);
+			
+	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+	usc_UnlatchRxstatusBits( info, status );
+
+	if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) {
+		if (status & RXSTATUS_EXITED_HUNT)
+			info->icount.exithunt++;
+		if (status & RXSTATUS_IDLE_RECEIVED)
+			info->icount.rxidle++;
+		wake_up_interruptible(&info->event_wait_q);
+	}
+
+	if (status & RXSTATUS_OVERRUN){
+		/* Purge receive FIFO to allow DMA buffer completion
+		 * with overrun status stored in the receive status block.
+		 */
+		usc_RCmd( info, RCmd_EnterHuntmode );
+		usc_RTCmd( info, RTCmd_PurgeRxFifo );
+	}
+
+}	/* end of mgsl_isr_receive_status() */
+
+/* mgsl_isr_transmit_status()
+ * 
+ * 	Service a transmit status interrupt
+ *	HDLC mode :end of transmit frame
+ *	Async mode:all data is sent
+ * 	transmit status is indicated by bits in the TCSR.
+ * 
+ * Arguments:		info	       pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_transmit_status( struct mgsl_struct *info )
+{
+	u16 status = usc_InReg( info, TCSR );
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_transmit_status status=%04X\n",
+			__FILE__,__LINE__,status);
+	
+	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+	usc_UnlatchTxstatusBits( info, status );
+
+	if ( status & TXSTATUS_EOF_SENT )
+		info->icount.txok++;
+	else if ( status & TXSTATUS_UNDERRUN )
+		info->icount.txunder++;
+	else if ( status & TXSTATUS_ABORT_SENT )
+		info->icount.txabort++;
+	else
+		info->icount.txunder++;
+			
+	info->tx_active = 0;
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	del_timer(&info->tx_timer);	
+	
+	if ( info->drop_rts_on_tx_done ) {
+		usc_get_serial_signals( info );
+		if ( info->serial_signals & SerialSignal_RTS ) {
+			info->serial_signals &= ~SerialSignal_RTS;
+			usc_set_serial_signals( info );
+		}
+		info->drop_rts_on_tx_done = 0;
+	}
+		
+	if (info->tty->stopped || info->tty->hw_stopped) {
+		usc_stop_transmitter(info);
+		return;
+	}
+	
+	mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_STATUS, status);
+
+}	/* end of mgsl_isr_transmit_status() */
+
+/* mgsl_isr_io_pin()
+ * 
+ * 	Service an Input/Output pin interrupt. The type of
+ * 	interrupt is indicated by bits in the MISR
+ * 	
+ * Arguments:		info	       pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_io_pin( struct mgsl_struct *info )
+{
+ 	struct	mgsl_icount *icount;
+	u16 status = usc_InReg( info, MISR );
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_io_pin status=%04X\n",
+			__FILE__,__LINE__,status);
+			
+	usc_ClearIrqPendingBits( info, IO_PIN );
+	usc_UnlatchIostatusBits( info, status );
+
+	if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED |
+	              MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
+		icount = &info->icount;
+		/* update input line counters */
+		if (status & MISCSTATUS_RI_LATCHED)
+			icount->rng++;
+		if (status & MISCSTATUS_DSR_LATCHED)
+			icount->dsr++;
+		if (status & MISCSTATUS_DCD_LATCHED) {
+			icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+			if ((info->flags & ASYNC_HARDPPS_CD) &&
+			    (status & MISCSTATUS_DCD_LATCHED))
+				hardpps();
+#endif
+		}
+		if (status & MISCSTATUS_CTS_LATCHED)
+			icount->cts++;
+		wake_up_interruptible(&info->status_event_wait_q);
+		wake_up_interruptible(&info->event_wait_q);
+
+		if ( (info->flags & ASYNC_CHECK_CD) && 
+		     (status & MISCSTATUS_DCD_LATCHED) ) {
+			if ( debug_level >= DEBUG_LEVEL_ISR )
+				printk("%s CD now %s...", info->device_name,
+				       (status & MISCSTATUS_DCD) ? "on" : "off");
+			if (status & MISCSTATUS_DCD)
+				wake_up_interruptible(&info->open_wait);
+			else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+				   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+				if ( debug_level >= DEBUG_LEVEL_ISR )
+					printk("doing serial hangup...");
+				if (info->tty)
+					tty_hangup(info->tty);
+			}
+		}
+	
+		if ( (info->flags & ASYNC_CTS_FLOW) && 
+		     (status & MISCSTATUS_CTS_LATCHED) ) {
+			if (info->tty->hw_stopped) {
+				if (status & MISCSTATUS_CTS) {
+					if ( debug_level >= DEBUG_LEVEL_ISR )
+						printk("CTS tx start...");
+					info->tty->hw_stopped = 0;
+					usc_start_transmitter(info);
+					mgsl_bh_queue_put( info, BH_TYPE_TRANSMIT_DATA, status );
+					return;
+				}
+			} else {
+				if (!(status & MISCSTATUS_CTS)) {
+					if ( debug_level >= DEBUG_LEVEL_ISR )
+						printk("CTS tx stop...");
+					info->tty->hw_stopped = 1;
+					usc_stop_transmitter(info);
+				}
+			}
+		}
+	}
+
+	/* for diagnostics set IRQ flag */
+	if ( status & MISCSTATUS_TXC_LATCHED ){
+		usc_OutReg( info, SICR,
+			(unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) );
+		usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED );
+		info->irq_occurred = 1;
+	}
+
+}	/* end of mgsl_isr_io_pin() */
+
+/* mgsl_isr_transmit_data()
+ * 
+ * 	Service a transmit data interrupt (async mode only).
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_transmit_data( struct mgsl_struct *info )
+{
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n",
+			__FILE__,__LINE__,info->xmit_cnt);
+			
+	usc_ClearIrqPendingBits( info, TRANSMIT_DATA );
+	
+	if (info->tty->stopped || info->tty->hw_stopped) {
+		usc_stop_transmitter(info);
+		return;
+	}
+	
+	if ( info->xmit_cnt )
+		usc_load_txfifo( info );
+	else
+		info->tx_active = 0;
+		
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_DATA, (unsigned short)(info->xmit_cnt));
+
+}	/* end of mgsl_isr_transmit_data() */
+
+/* mgsl_isr_receive_data()
+ * 
+ * 	Service a receive data interrupt. This occurs
+ * 	when operating in asynchronous interrupt transfer mode.
+ *	The receive data FIFO is flushed to the receive data buffers. 
+ * 
+ * Arguments:		info		pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_receive_data( struct mgsl_struct *info )
+{
+	int Fifocount;
+	u16 status;
+	unsigned char DataByte;
+ 	struct tty_struct *tty = info->tty;
+ 	struct	mgsl_icount *icount = &info->icount;
+	
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_receive_data\n",
+			__FILE__,__LINE__);
+
+	usc_ClearIrqPendingBits( info, RECEIVE_DATA );
+	
+	/* select FIFO status for RICR readback */
+	usc_RCmd( info, RCmd_SelectRicrRxFifostatus );
+
+	/* clear the Wordstatus bit so that status readback */
+	/* only reflects the status of this byte */
+	usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 ));
+
+	/* flush the receive FIFO */
+
+	while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) {
+		/* read one byte from RxFIFO */
+		outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY),
+		      info->io_base + CCAR );
+		DataByte = inb( info->io_base + CCAR );
+
+		/* get the status of the received byte */
+		status = usc_InReg(info, RCSR);
+		if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
+				RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) )
+			usc_UnlatchRxstatusBits(info,RXSTATUS_ALL);
+		
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			continue;
+			
+		*tty->flip.char_buf_ptr = DataByte;
+		icount->rx++;
+		
+		*tty->flip.flag_buf_ptr = 0;
+		if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
+				RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) {
+			printk("rxerr=%04X\n",status);					
+			/* update error statistics */
+			if ( status & RXSTATUS_BREAK_RECEIVED ) {
+				status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR);
+				icount->brk++;
+			} else if (status & RXSTATUS_PARITY_ERROR) 
+				icount->parity++;
+			else if (status & RXSTATUS_FRAMING_ERROR)
+				icount->frame++;
+			else if (status & RXSTATUS_OVERRUN) {
+				/* must issue purge fifo cmd before */
+				/* 16C32 accepts more receive chars */
+				usc_RTCmd(info,RTCmd_PurgeRxFifo);
+				icount->overrun++;
+			}
+
+			/* discard char if tty control flags say so */					
+			if (status & info->ignore_status_mask)
+				continue;
+				
+			status &= info->read_status_mask;
+		
+			if (status & RXSTATUS_BREAK_RECEIVED) {
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+				if (info->flags & ASYNC_SAK)
+					do_SAK(tty);
+			} else if (status & RXSTATUS_PARITY_ERROR)
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+			else if (status & RXSTATUS_FRAMING_ERROR)
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+			if (status & RXSTATUS_OVERRUN) {
+				/* Overrun is special, since it's
+				 * reported immediately, and doesn't
+				 * affect the current character
+				 */
+				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+					tty->flip.count++;
+					tty->flip.flag_buf_ptr++;
+					tty->flip.char_buf_ptr++;
+					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				}
+			}
+		}	/* end of if (error) */
+		
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_ISR ) {
+		printk("%s(%d):mgsl_isr_receive_data flip count=%d\n",
+			__FILE__,__LINE__,tty->flip.count);
+		printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
+			__FILE__,__LINE__,icount->rx,icount->brk,
+			icount->parity,icount->frame,icount->overrun);
+	}
+			
+	if ( tty->flip.count ) {
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+		tty_flip_buffer_push(tty);
+#else		
+		queue_task(&tty->flip.tqueue, &tq_timer); 
+#endif		
+	}
+	
+
+}	/* end of mgsl_isr_receive_data() */
+
+/* mgsl_isr_misc()
+ * 
+ * 	Service a miscellaneos interrupt source.
+ * 	
+ * Arguments:		info		pointer to device extension (instance data)
+ * Return Value:	None
+ */
+void mgsl_isr_misc( struct mgsl_struct *info )
+{
+	u16 status = usc_InReg( info, MISR );
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_misc status=%04X\n",
+			__FILE__,__LINE__,status);
+			
+	usc_ClearIrqPendingBits( info, MISC );
+	usc_UnlatchMiscstatusBits( info, status );
+
+}	/* end of mgsl_isr_misc() */
+
+/* mgsl_isr_null()
+ *
+ * 	Services undefined interrupt vectors from the
+ * 	USC. (hence this function SHOULD never be called)
+ * 
+ * Arguments:		info		pointer to device extension (instance data)
+ * Return Value:	None
+ */
+void mgsl_isr_null( struct mgsl_struct *info )
+{
+
+}	/* end of mgsl_isr_null() */
+
+/* mgsl_isr_receive_dma()
+ * 
+ * 	Service a receive DMA channel interrupt.
+ * 	For this driver there are two sources of receive DMA interrupts
+ * 	as identified in the Receive DMA mode Register (RDMR):
+ * 
+ * 	BIT3	EOA/EOL		End of List, all receive buffers in receive
+ * 				buffer list have been filled (no more free buffers
+ * 				available). The DMA controller has shut down.
+ * 
+ * 	BIT2	EOB		End of Buffer. This interrupt occurs when a receive
+ * 				DMA buffer is terminated in response to completion
+ * 				of a good frame or a frame with errors. The status
+ * 				of the frame is stored in the buffer entry in the
+ * 				list of receive buffer entries.
+ * 
+ * Arguments:		info		pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_isr_receive_dma( struct mgsl_struct *info )
+{
+	u16 status;
+	
+	/* clear interrupt pending and IUS bit for Rx DMA IRQ */
+	usc_OutDmaReg( info, CDIR, BIT9+BIT1 );
+
+	/* Read the receive DMA status to identify interrupt type. */
+	/* This also clears the status bits. */
+	status = usc_InDmaReg( info, RDMR );
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n",
+			__FILE__,__LINE__,info->device_name,status);
+			
+	/* Post a receive event for BH processing. */
+	mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status );
+	
+	if ( status & BIT3 )
+		info->rx_overflow = 1;
+
+}	/* end of mgsl_isr_receive_dma() */
+
+/* mgsl_interrupt()
+ * 
+ * 	Interrupt service routine entry point.
+ * 	
+ * Arguments:
+ * 
+ * 	irq		interrupt number that caused interrupt
+ * 	dev_id		device ID supplied during interrupt registration
+ * 	regs		interrupted processor context
+ * 	
+ * Return Value: None
+ */
+static void mgsl_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct mgsl_struct * info;
+	u16 UscVector;
+	u16 DmaVector;
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_interrupt(%d)entry.\n",
+			__FILE__,__LINE__,irq);
+
+	info = (struct mgsl_struct *)dev_id;	
+	if (!info)
+		return;
+		
+	spin_lock(&info->irq_spinlock);
+
+	for(;;) {
+		/* Read the interrupt vectors from hardware. */
+		UscVector = usc_InReg(info, IVR) >> 9;
+		DmaVector = usc_InDmaReg(info, DIVR);
+		
+		if ( debug_level >= DEBUG_LEVEL_ISR )	
+			printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n",
+				__FILE__,__LINE__,info->device_name,UscVector,DmaVector);
+			
+		if ( !UscVector && !DmaVector )
+			break;
+			
+		/* Dispatch interrupt vector */
+		if ( UscVector )
+			(*UscIsrTable[UscVector])(info);
+		else
+			mgsl_isr_receive_dma(info);
+
+		if ( info->isr_overflow ) {
+			printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n",
+				__FILE__,__LINE__,info->device_name, irq);
+				/* Interrupt overflow. Reset adapter and exit. */
+//				UscReset(info);
+//				break;
+		}
+	}
+	
+	/* Request bottom half processing if there's something 
+	 * for it to do and the bh is not already running
+	 */
+
+	if ( info->bh_queue_head && !info->bh_running && !info->bh_requested ) {
+		if ( debug_level >= DEBUG_LEVEL_ISR )	
+			printk("%s(%d):%s queueing bh task.\n",
+				__FILE__,__LINE__,info->device_name);
+		queue_task(&info->task, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+		info->bh_requested = 1;
+	}
+
+	spin_unlock(&info->irq_spinlock);
+	
+	if ( debug_level >= DEBUG_LEVEL_ISR )	
+		printk("%s(%d):mgsl_interrupt(%d)exit.\n",
+			__FILE__,__LINE__,irq);
+
+}	/* end of mgsl_interrupt() */
+
+/* startup()
+ * 
+ * 	Initialize and start device.
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	0 if success, otherwise error code
+ */
+static int startup(struct mgsl_struct * info)
+{
+	int retval = 0;
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name);
+		
+	if (info->flags & ASYNC_INITIALIZED)
+		return 0;
+	
+	if (!info->xmit_buf) {
+		/* allocate a page of memory for a transmit buffer */
+		info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL);
+		if (!info->xmit_buf) {
+			printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
+				__FILE__,__LINE__,info->device_name);
+			return -ENOMEM;
+		}
+	}
+
+	mgsl_format_bh_queue(info);
+	
+	init_timer(&info->tx_timer);
+	info->tx_timer.data = (unsigned long)info;
+	info->tx_timer.function = mgsl_tx_timeout;
+	
+	/* Allocate and claim adapter resources */
+	retval = mgsl_claim_resources(info);
+	
+	/* perform existance check and diagnostics */
+	if ( !retval )
+		retval = mgsl_adapter_test(info);
+		
+	if ( retval ) {
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+  		if (capable(CAP_SYS_ADMIN) && info->tty)
+#else
+  		if (suser() && info->tty)
+#endif		
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		mgsl_release_resources(info);
+  		return retval;
+  	}
+
+	/* program hardware for current parameters */
+	mgsl_change_params(info);
+	
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags |= ASYNC_INITIALIZED;
+	
+	return 0;
+	
+}	/* end of startup() */
+
+/* shutdown()
+ *
+ * Called by mgsl_close() and mgsl_hangup() to shutdown hardware
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+static void shutdown(struct mgsl_struct * info)
+{
+	unsigned long flags;
+	
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_shutdown(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	/* clear status wait queue because status changes */
+	/* can't happen after shutting down the hardware */
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	usc_DisableMasterIrqBit(info);
+	usc_stop_receiver(info);
+	usc_stop_transmitter(info);
+	usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS +
+		TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC );
+	usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE);
+	
+	/* Disable DMAEN (Port 7, Bit 14) */
+	/* This disconnects the DMA request signal from the ISA bus */
+	/* on the ISA adapter. This has no effect for the PCI adapter */
+	usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14));
+	
+	/* Disable INTEN (Port 6, Bit12) */
+	/* This disconnects the IRQ request signal to the ISA bus */
+	/* on the ISA adapter. This has no effect for the PCI adapter */
+	usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12));
+	
+ 	if (!info->tty || info->tty->termios->c_cflag & HUPCL) {
+ 		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+		usc_set_serial_signals(info);
+	}
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	mgsl_release_resources(info);	
+	
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	
+}	/* end of shutdown() */
+
+/* mgsl_change_params()
+ *
+ *	Reconfigure adapter based on new parameters
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+static void mgsl_change_params(struct mgsl_struct *info)
+{
+	unsigned cflag;
+	unsigned long flags;
+	int bits_per_char;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+		
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_change_params(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	cflag = info->tty->termios->c_cflag;
+
+	/* if B0 rate (hangup) specified then negate DTR and RTS */
+	/* otherwise assert DTR and RTS */
+ 	if (cflag & CBAUD)
+		info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+	else
+		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+	
+	/* byte size and parity */
+	
+	switch (cflag & CSIZE) {
+	      case CS5: info->params.data_bits = 5; break;
+	      case CS6: info->params.data_bits = 6; break;
+	      case CS7: info->params.data_bits = 7; break;
+	      case CS8: info->params.data_bits = 8; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  info->params.data_bits = 7; break;
+	      }
+	      
+	if (cflag & CSTOPB)
+		info->params.stop_bits = 2;
+	else
+		info->params.stop_bits = 1;
+
+	info->params.parity = ASYNC_PARITY_NONE;
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			info->params.parity = ASYNC_PARITY_ODD;
+		else
+			info->params.parity = ASYNC_PARITY_EVEN;
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			info->params.parity = ASYNC_PARITY_SPACE;
+#endif
+	}
+
+	/* calculate number of jiffies to transmit a full
+	 * FIFO (32 bytes) at specified data rate
+	 */
+	bits_per_char = info->params.data_bits + 
+			info->params.stop_bits + 1;
+
+	/* if port data rate is set to 460800 or less then
+	 * allow tty settings to override, otherwise keep the
+	 * current data rate.
+	 */
+	if (info->params.data_rate <= 460800) {
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+		info->params.data_rate = tty_get_baud_rate(info->tty);
+#else
+		int i = cflag & CBAUD;
+		if (i & CBAUDEX) {
+			i &= ~CBAUDEX;
+			if (i < 1 || i > 4) 
+				info->tty->termios->c_cflag &= ~CBAUDEX;
+			else
+				i += 15;
+		}
+		info->params.data_rate = baud_table[i];
+#endif	
+	}
+	
+	if ( info->params.data_rate ) {
+		info->timeout = (32*HZ*bits_per_char) / 
+				info->params.data_rate;
+	}
+	info->timeout += HZ/50;		/* Add .02 seconds of slop */
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+		
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	/* process tty input control flags */
+	
+	info->read_status_mask = RXSTATUS_OVERRUN;
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR;
+ 	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ 		info->read_status_mask |= RXSTATUS_BREAK_RECEIVED;
+	
+	if (I_IGNPAR(info->tty))
+		info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR;
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED;
+		/* If ignoring parity and break indicators, ignore 
+		 * overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty))
+			info->ignore_status_mask |= RXSTATUS_OVERRUN;
+	}
+
+	/* reprogram the hardware */
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	
+	usc_stop_receiver(info);
+	usc_stop_transmitter(info);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	
+	if ( info->params.mode == MGSL_MODE_HDLC )
+		usc_set_sync_mode(info);
+	else
+		usc_set_async_mode(info);
+		
+	usc_set_serial_signals(info);
+
+	/* enable modem signal IRQs and read initial signal states */
+	usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);		
+	usc_EnableInterrupts(info, IO_PIN);
+	usc_get_serial_signals(info);
+		
+	if ( cflag & CREAD )
+		usc_start_receiver(info);
+		
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+}	/* end of mgsl_change_params() */
+
+/* mgsl_put_char()
+ * 
+ * 	Add a character to the transmit buffer.
+ * 	
+ * Arguments:		tty	pointer to tty information structure
+ * 			ch	character to add to transmit buffer
+ * 		
+ * Return Value:	None
+ */
+static void mgsl_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO ) {
+		printk( "%s(%d):mgsl_put_char(%d) on %s\n",
+			__FILE__,__LINE__,ch,info->device_name);
+	}		
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_put_char"))
+		return;
+
+	if (!tty || !info->xmit_buf)
+		return;
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	
+	if ( (info->params.mode != MGSL_MODE_HDLC) || 
+	     !info->tx_active ) {
+	
+		if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) {
+			info->xmit_buf[info->xmit_head++] = ch;
+			info->xmit_head &= SERIAL_XMIT_SIZE-1;
+			info->xmit_cnt++;
+		}
+	}
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+}	/* end of mgsl_put_char() */
+
+/* mgsl_flush_chars()
+ * 
+ * 	Enable transmitter so remaining characters in the
+ * 	transmit buffer are sent.
+ * 	
+ * Arguments:		tty	pointer to tty information structure
+ * Return Value:	None
+ */
+static void mgsl_flush_chars(struct tty_struct *tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+				
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n",
+			__FILE__,__LINE__,info->device_name,info->xmit_cnt);
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n",
+			__FILE__,__LINE__,info->device_name );
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	
+	if (!info->tx_active) {
+		if ( (info->params.mode == MGSL_MODE_HDLC) &&
+			info->xmit_cnt ) {
+			/* operating in synchronous (frame oriented) mode */
+			/* copy data from circular xmit_buf to */
+			/* transmit DMA buffer. */
+			mgsl_load_tx_dma_buffer(info,
+				 info->xmit_buf,info->xmit_cnt);
+		}
+	 	usc_start_transmitter(info);
+	}
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+}	/* end of mgsl_flush_chars() */
+
+/* mgsl_write()
+ * 
+ * 	Send a block of data
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty information structure
+ * 	from_user	flag: 1 = from user process
+ * 	buf		pointer to buffer containing send data
+ * 	count		size of send data in bytes
+ * 	
+ * Return Value:	number of characters written
+ */
+static int mgsl_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, ret = 0, err;
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_write(%s) count=%d\n",
+			__FILE__,__LINE__,info->device_name,count);
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_write"))
+		goto cleanup;
+
+	if (!tty || !info->xmit_buf || !tmp_buf)
+		goto cleanup;
+
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		/* operating in synchronous (frame oriented) mode */
+	
+		if (info->tx_active) {
+			ret = 0; goto cleanup; 
+		}
+		
+		if ( info->xmit_cnt ) {
+			/* Send accumulated from send_char() calls */
+			/* as frame and wait before accepting more data. */
+			ret = 0;
+				
+			/* copy data from circular xmit_buf to */
+			/* transmit DMA buffer. */
+			mgsl_load_tx_dma_buffer(info,
+				info->xmit_buf,info->xmit_cnt);
+			if ( debug_level >= DEBUG_LEVEL_INFO )
+				printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n",
+					__FILE__,__LINE__,info->device_name);
+		} else {
+			if ( debug_level >= DEBUG_LEVEL_INFO )
+				printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n",
+					__FILE__,__LINE__,info->device_name);
+			ret = count;
+			info->xmit_cnt = count;
+			if (from_user) {
+				down(&tmp_buf_sem);
+				COPY_FROM_USER(err,tmp_buf, buf, count);
+				if (err) {
+					if ( debug_level >= DEBUG_LEVEL_INFO )
+						printk( "%s(%d):mgsl_write(%s) sync user buf copy failed\n",
+							__FILE__,__LINE__,info->device_name);
+					ret = -EFAULT;
+				} else
+					mgsl_load_tx_dma_buffer(info,tmp_buf,count);
+				up(&tmp_buf_sem);
+			}
+			else
+				mgsl_load_tx_dma_buffer(info,buf,count);
+		}
+	} else {
+		if (from_user) {
+			down(&tmp_buf_sem);
+			while (1) {
+				c = MIN(count,
+					MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+					    SERIAL_XMIT_SIZE - info->xmit_head));
+				if (c <= 0)
+					break;
+
+				COPY_FROM_USER(err,tmp_buf, buf, c);
+				c -= err;
+				if (!c) {
+					if (!ret)
+						ret = -EFAULT;
+					break;
+				}
+				spin_lock_irqsave(&info->irq_spinlock,flags);
+				c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+					       SERIAL_XMIT_SIZE - info->xmit_head));
+				memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+				info->xmit_head = ((info->xmit_head + c) &
+						   (SERIAL_XMIT_SIZE-1));
+				info->xmit_cnt += c;
+				spin_unlock_irqrestore(&info->irq_spinlock,flags);
+				buf += c;
+				count -= c;
+				ret += c;
+			}
+			up(&tmp_buf_sem);
+		} else {
+			while (1) {
+				spin_lock_irqsave(&info->irq_spinlock,flags);
+				c = MIN(count,
+					MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+					    SERIAL_XMIT_SIZE - info->xmit_head));
+				if (c <= 0) {
+					spin_unlock_irqrestore(&info->irq_spinlock,flags);
+					break;
+				}
+				memcpy(info->xmit_buf + info->xmit_head, buf, c);
+				info->xmit_head = ((info->xmit_head + c) &
+						   (SERIAL_XMIT_SIZE-1));
+				info->xmit_cnt += c;
+				spin_unlock_irqrestore(&info->irq_spinlock,flags);
+				buf += c;
+				count -= c;
+				ret += c;
+			}
+		}
+	}	
+	
+ 	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		if (!info->tx_active)
+		 	usc_start_transmitter(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+ 	}
+cleanup:	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_write(%s) returning=%d\n",
+			__FILE__,__LINE__,info->device_name,ret);
+			
+	return ret;
+	
+}	/* end of mgsl_write() */
+
+/* mgsl_write_room()
+ *
+ *	Return the count of free bytes in transmit buffer
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static int mgsl_write_room(struct tty_struct *tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	int	ret;
+				
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+		
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_write_room(%s)=%d\n",
+			 __FILE__,__LINE__, info->device_name,ret );
+			 
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		/* operating in synchronous (frame oriented) mode */
+		if ( info->tx_active )
+			return 0;
+		else
+			return HDLC_MAX_FRAME_SIZE;
+	}
+	
+	return ret;
+	
+}	/* end of mgsl_write_room() */
+
+/* mgsl_chars_in_buffer()
+ *
+ *	Return the count of bytes in transmit buffer
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static int mgsl_chars_in_buffer(struct tty_struct *tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+			 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_chars_in_buffer(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_chars_in_buffer"))
+		return 0;
+		
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n",
+			 __FILE__,__LINE__, info->device_name,info->xmit_cnt );
+			 
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		/* operating in synchronous (frame oriented) mode */
+		if ( info->tx_active )
+			return info->tx_buffer_list[0].rcc;
+		else
+			return 0;
+	}
+			 
+	return info->xmit_cnt;
+}	/* end of mgsl_chars_in_buffer() */
+
+/* mgsl_flush_buffer()
+ *
+ *	Discard all data in the send buffer
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static void mgsl_flush_buffer(struct tty_struct *tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_flush_buffer(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_flush_buffer"))
+		return;
+		
+	spin_lock_irqsave(&info->irq_spinlock,flags); 
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	del_timer(&info->tx_timer);	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+		
+}	/* end of mgsl_flush_buffer() */
+
+/* mgsl_send_xchar()
+ *
+ *	Send a high-priority XON/XOFF character
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ *			ch	character to send
+ * Return Value:	None
+ */
+static void mgsl_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_send_xchar(%s,%d)\n",
+			 __FILE__,__LINE__, info->device_name, ch );
+			 
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_send_xchar"))
+		return;
+
+	info->x_char = ch;
+	if (ch) {
+		/* Make sure transmit interrupts are on */
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		if (!info->tx_enabled)
+		 	usc_start_transmitter(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	}
+}	/* end of mgsl_send_xchar() */
+
+/* mgsl_throttle()
+ * 
+ * 	Signal remote device to throttle send data (our receive data)
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static void mgsl_throttle(struct tty_struct * tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_throttle(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		mgsl_send_xchar(tty, STOP_CHAR(tty));
+ 
+ 	if (tty->termios->c_cflag & CRTSCTS) {
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		info->serial_signals &= ~SerialSignal_RTS;
+	 	usc_set_serial_signals(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	}
+}	/* end of mgsl_throttle() */
+
+/* mgsl_unthrottle()
+ * 
+ * 	Signal remote device to stop throttling send data (our receive data)
+ * 	
+ * Arguments:		tty	pointer to tty info structure
+ * Return Value:	None
+ */
+static void mgsl_unthrottle(struct tty_struct * tty)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_unthrottle(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			mgsl_send_xchar(tty, START_CHAR(tty));
+	}
+	
+ 	if (tty->termios->c_cflag & CRTSCTS) {
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		info->serial_signals |= SerialSignal_RTS;
+	 	usc_set_serial_signals(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	}
+	
+}	/* end of mgsl_unthrottle() */
+
+/* mgsl_get_stats()
+ * 
+ * 	get the current serial parameters information
+ *
+ * Arguments:	info		pointer to device instance data
+ * 		user_icount	pointer to buffer to hold returned stats
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount *user_icount)
+{
+	int err;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_get_params(%s)\n",
+			 __FILE__,__LINE__, info->device_name);
+			
+	COPY_TO_USER(err,user_icount, &info->icount, sizeof(struct mgsl_icount));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):mgsl_get_stats(%s) user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+	
+	return 0;
+	
+}	/* end of mgsl_get_stats() */
+
+/* mgsl_get_params()
+ * 
+ * 	get the current serial parameters information
+ *
+ * Arguments:	info		pointer to device instance data
+ * 		user_params	pointer to buffer to hold returned params
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS *user_params)
+{
+	int err;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_get_params(%s)\n",
+			 __FILE__,__LINE__, info->device_name);
+			
+	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+	
+	return 0;
+	
+}	/* end of mgsl_get_params() */
+
+/* mgsl_set_params()
+ * 
+ * 	set the serial parameters
+ * 	
+ * Arguments:
+ * 
+ * 	info		pointer to device instance data
+ * 	new_params	user buffer containing new serial params
+ *
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS *new_params)
+{
+ 	unsigned long flags;
+	MGSL_PARAMS tmp_params;
+	int err;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__,
+			info->device_name );
+	COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+ 	mgsl_change_params(info);
+	
+	return 0;
+	
+}	/* end of mgsl_set_params() */
+
+/* mgsl_get_txidle()
+ * 
+ * 	get the current transmit idle mode
+ *
+ * Arguments:	info		pointer to device instance data
+ * 		idle_mode	pointer to buffer to hold returned idle mode
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_get_txidle(struct mgsl_struct * info, int*idle_mode)
+{
+	int err;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_get_txidle(%s)=%d\n",
+			 __FILE__,__LINE__, info->device_name, info->idle_mode);
+			
+	COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+	
+	return 0;
+	
+}	/* end of mgsl_get_txidle() */
+
+/* mgsl_set_txidle()	service ioctl to set transmit idle mode
+ * 	
+ * Arguments:	 	info		pointer to device instance data
+ * 			idle_mode	new idle mode
+ *
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__,
+			info->device_name, idle_mode );
+			
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	info->idle_mode = idle_mode;
+	usc_set_txidle( info );
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	return 0;
+	
+}	/* end of mgsl_set_txidle() */
+
+/* mgsl_txenable()
+ * 
+ * 	enable or disable the transmitter
+ * 	
+ * Arguments:
+ * 
+ * 	info		pointer to device instance data
+ * 	enable		1 = enable, 0 = disable
+ *
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_txenable(struct mgsl_struct * info, int enable)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__,
+			info->device_name, enable);
+			
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if ( enable ) {
+		if ( !info->tx_enabled )
+			usc_start_transmitter(info);
+	} else {
+		if ( info->tx_enabled )
+			usc_stop_transmitter(info);
+	}
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	return 0;
+	
+}	/* end of mgsl_txenable() */
+
+/* mgsl_txabort()	abort send HDLC frame
+ * 	
+ * Arguments:	 	info		pointer to device instance data
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_txabort(struct mgsl_struct * info)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__,
+			info->device_name);
+			
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC )
+		usc_TCmd(info,TCmd_SendAbort);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	return 0;
+	
+}	/* end of mgsl_txabort() */
+
+/* mgsl_rxenable() 	enable or disable the receiver
+ * 	
+ * Arguments:	 	info		pointer to device instance data
+ * 			enable		1 = enable, 0 = disable
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_rxenable(struct mgsl_struct * info, int enable)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__,
+			info->device_name, enable);
+			
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	if ( enable ) {
+		if ( !info->rx_enabled )
+			usc_start_receiver(info);
+	} else {
+		if ( info->rx_enabled )
+			usc_stop_receiver(info);
+	}
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	return 0;
+	
+}	/* end of mgsl_rxenable() */
+
+/* mgsl_wait_event() 	wait for specified event to occur
+ * 	
+ * Arguments:	 	info	pointer to device instance data
+ * 			mask	bitmask of events to wait for
+ * Return Value:	bit mask of triggering event, otherwise error code
+ */
+static int mgsl_wait_event(struct mgsl_struct * info, int mask)
+{
+ 	unsigned long flags;
+	int s;
+	int rc=0;
+	u16 regval;
+	struct mgsl_icount cprev, cnow;
+		 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__,
+			info->device_name, mask);
+			
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	
+	/* note the counters on entry */
+	cprev = info->icount;
+	
+	if (mask & MgslEvent_ExitHuntMode) {
+		/* enable exit hunt mode IRQ */
+		regval = usc_InReg(info,RICR);
+		if (!(regval & RXSTATUS_EXITED_HUNT))
+			usc_OutReg(info, RICR, regval | RXSTATUS_EXITED_HUNT);
+	}
+	
+	if (mask & MgslEvent_IdleReceived) {
+		/* enable idle mode received IRQ */
+		regval = usc_InReg(info,RICR);
+		if (!(regval & RXSTATUS_IDLE_RECEIVED))
+			usc_OutReg(info, RICR, regval | RXSTATUS_IDLE_RECEIVED);
+	}
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	while(!rc) {
+		/* sleep until event occurs */
+		interruptible_sleep_on(&info->event_wait_q);
+		
+		/* see if a signal woke us */
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+			
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		/* get icount and serial signal states */
+		cnow = info->icount;
+		s = info->serial_signals;
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		
+		rc = 0;		
+		
+		if (cnow.dsr != cprev.dsr)
+			rc |= (mask & ((s & SerialSignal_DSR) ?
+				MgslEvent_DsrActive:MgslEvent_DsrInactive));
+		
+		if (cnow.dcd != cprev.dcd)
+			rc |= (mask & ((s & SerialSignal_DCD) ?
+				MgslEvent_DcdActive:MgslEvent_DcdInactive));
+				
+		if (cnow.cts != cprev.cts)
+			rc |= (mask & ((s & SerialSignal_CTS) ?
+				MgslEvent_CtsActive:MgslEvent_CtsInactive));
+				
+		if (cnow.rng != cprev.rng)
+			rc |= (mask & ((s & SerialSignal_RI) ?
+				MgslEvent_RiActive:MgslEvent_RiInactive));
+				
+		if (cnow.exithunt != cprev.exithunt)
+			rc |= (mask & MgslEvent_ExitHuntMode);
+			
+		if (cnow.rxidle != cprev.rxidle)
+			rc |= (mask & MgslEvent_ExitHuntMode);
+				
+		if (!rc)
+			rc = -EIO; /* no change => error */
+			
+		cprev = cnow;
+	}
+	
+	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		if (!info->event_wait_q) {
+			/* disable enable exit hunt mode/idle rcvd IRQs */
+			regval = usc_InReg(info,RICR);
+			usc_OutReg(info, RICR, regval & 
+				~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED));
+		}
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	}
+	
+	return rc;
+	
+}	/* end of mgsl_wait_event() */
+
+/* get_modem_info()
+ * 
+ * 	Read the state of the serial control and
+ * 	status signals and return to caller.
+ * 	
+ * Arguments:	 	info	pointer to device instance data
+ * 			value	pointer to int to hold returned info
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int get_modem_info(struct mgsl_struct * info, unsigned int *value)
+{
+	unsigned int result = 0;
+ 	unsigned long flags;
+	int err;
+ 
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+ 	usc_get_serial_signals(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	if (info->serial_signals & SerialSignal_RTS)
+		result |= TIOCM_RTS;
+	if (info->serial_signals & SerialSignal_DTR)
+		result |= TIOCM_DTR;
+	if (info->serial_signals & SerialSignal_DCD)
+		result |= TIOCM_CAR;
+	if (info->serial_signals & SerialSignal_RI)
+		result |= TIOCM_RNG;
+	if (info->serial_signals & SerialSignal_DSR)
+		result |= TIOCM_DSR;
+	if (info->serial_signals & SerialSignal_CTS)
+		result |= TIOCM_CTS;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_get_modem_info %s value=%08X\n",
+			 __FILE__,__LINE__, info->device_name, *value );
+			
+	PUT_USER(err,result,value);
+	return err;
+}	/* end of get_modem_info() */
+
+/* set_modem_info()
+ * 
+ * 	Set the state of the modem control signals (DTR/RTS)
+ * 	
+ * Arguments:
+ * 
+ * 	info	pointer to device instance data
+ * 	cmd	signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
+ *		TIOCMSET = set/clear signal values
+ * 	value	bit mask for command
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int set_modem_info(struct mgsl_struct * info, unsigned int cmd,
+			  unsigned int *value)
+{
+ 	int error;
+ 	unsigned int arg;
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_set_modem_info %s\n", __FILE__,__LINE__,
+			info->device_name );
+			
+ 	GET_USER(error,arg,value);
+ 	if (error)
+ 		return error;
+		
+ 	switch (cmd) {
+ 	case TIOCMBIS: 
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals |= SerialSignal_RTS;
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals |= SerialSignal_DTR;
+ 		break;
+ 	case TIOCMBIC:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals &= ~SerialSignal_RTS;
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals &= ~SerialSignal_DTR;
+ 		break;
+ 	case TIOCMSET:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals |= SerialSignal_RTS;
+		else
+ 			info->serial_signals &= ~SerialSignal_RTS;
+		
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals |= SerialSignal_DTR;
+		else
+ 			info->serial_signals &= ~SerialSignal_DTR;
+ 		break;
+ 	default:
+ 		return -EINVAL;
+ 	}
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+ 	usc_set_serial_signals(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	return 0;
+	
+}	/* end of set_modem_info() */
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+/* mgsl_break()		Set or clear transmit break condition
+ *
+ * Arguments:		tty		pointer to tty instance data
+ *			break_state	-1=set break condition, 0=clear
+ * Return Value:	None
+ */
+static void mgsl_break(struct tty_struct *tty, int break_state)
+{
+	struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_break(%s,%d)\n",
+			 __FILE__,__LINE__, info->device_name, break_state);
+			 
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_break"))
+		return;
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+ 	if (break_state == -1)
+		usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7));
+	else 
+		usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7));
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+}	/* end of mgsl_break() */
+#endif
+
+/* mgsl_ioctl()	Service an IOCTL request
+ * 	
+ * Arguments:
+ * 
+ * 	tty	pointer to tty instance data
+ * 	file	pointer to associated file object for device
+ * 	cmd	IOCTL command code
+ * 	arg	command argument/context
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
+	struct mgsl_icount cprev, cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
+			info->device_name, cmd );
+	
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+	
+	switch (cmd) {
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case MGSL_IOCGPARAMS:
+			return mgsl_get_params(info,(MGSL_PARAMS *)arg);
+		case MGSL_IOCSPARAMS:
+			return mgsl_set_params(info,(MGSL_PARAMS *)arg);
+		case MGSL_IOCGTXIDLE:
+			return mgsl_get_txidle(info,(int*)arg);
+		case MGSL_IOCSTXIDLE:
+			return mgsl_set_txidle(info,(int)arg);
+		case MGSL_IOCTXENABLE:
+			return mgsl_txenable(info,(int)arg);
+		case MGSL_IOCRXENABLE:
+			return mgsl_rxenable(info,(int)arg);
+		case MGSL_IOCTXABORT:
+			return mgsl_txabort(info);
+		case MGSL_IOCGSTATS:
+			return mgsl_get_stats(info,(struct mgsl_icount*)arg);
+		case MGSL_IOCWAITEVENT:
+			return mgsl_wait_event(info,(int)arg);
+		case MGSL_IOCCLRMODCOUNT:
+			while(MOD_IN_USE)
+				MOD_DEC_USE_COUNT;
+			return 0;
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+ 		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+		case TIOCMIWAIT:
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			/* note the counters on entry */
+			cprev = info->icount;
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+			while (1) {
+				interruptible_sleep_on(&info->status_event_wait_q);
+				/* see if a signal did it */
+				if (signal_pending(current))
+					return -ERESTARTSYS;
+				save_flags(flags); cli();
+				cnow = info->icount; /* atomic copy */
+				restore_flags(flags);
+				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+					return -EIO; /* no change => error */
+				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+					return 0;
+				}
+				cprev = cnow;
+			}
+			/* NOTREACHED */
+
+		/* 
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+		case TIOCGICOUNT:
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			cnow = info->icount;
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+			p_cuser = (struct serial_icounter_struct *) arg;
+			PUT_USER(error,cnow.cts, &p_cuser->cts);
+			if (error) return error;
+			PUT_USER(error,cnow.dsr, &p_cuser->dsr);
+			if (error) return error;
+			PUT_USER(error,cnow.rng, &p_cuser->rng);
+			if (error) return error;
+			PUT_USER(error,cnow.dcd, &p_cuser->dcd);
+			if (error) return error;
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+			PUT_USER(error,cnow.rx, &p_cuser->rx);
+			if (error) return error;
+			PUT_USER(error,cnow.tx, &p_cuser->tx);
+			if (error) return error;
+			PUT_USER(error,cnow.frame, &p_cuser->frame);
+			if (error) return error;
+			PUT_USER(error,cnow.overrun, &p_cuser->overrun);
+			if (error) return error;
+			PUT_USER(error,cnow.parity, &p_cuser->parity);
+			if (error) return error;
+			PUT_USER(error,cnow.brk, &p_cuser->brk);
+			if (error) return error;
+			PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun);
+			if (error) return error;
+#endif			
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+/* mgsl_set_termios()
+ * 
+ * 	Set new termios settings
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty structure
+ * 	termios		pointer to buffer to hold returned old termios
+ * 	
+ * Return Value:		None
+ */
+static void mgsl_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__,
+			tty->driver.name );
+	
+	/* just return if nothing has changed */
+	if ((tty->termios->c_cflag == old_termios->c_cflag)
+	    && (RELEVANT_IFLAG(tty->termios->c_iflag) 
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+	mgsl_change_params(info);
+
+	/* Handle transition to B0 status */
+	if (old_termios->c_cflag & CBAUD &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+	 	usc_set_serial_signals(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	}
+	
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    tty->termios->c_cflag & CBAUD) {
+		info->serial_signals |= SerialSignal_DTR;
+ 		if (!(tty->termios->c_cflag & CRTSCTS) || 
+ 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
+			info->serial_signals |= SerialSignal_RTS;
+ 		}
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+	 	usc_set_serial_signals(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	}
+	
+	/* Handle turning off CRTSCTS */
+	if (old_termios->c_cflag & CRTSCTS &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		mgsl_start(tty);
+	}
+
+}	/* end of mgsl_set_termios() */
+
+/* mgsl_close()
+ * 
+ * 	Called when port is closed. Wait for remaining data to be
+ * 	sent. Disable port and free resources.
+ * 	
+ * Arguments:
+ * 
+ * 	tty	pointer to open tty structure
+ * 	filp	pointer to open file object
+ * 	
+ * Return Value:	None
+ */
+static void mgsl_close(struct tty_struct *tty, struct file * filp)
+{
+	struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
+
+	if (!info || mgsl_paranoia_check(info, tty->device, "mgsl_close"))
+		return;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_close(%s) entry, count=%d\n",
+			 __FILE__,__LINE__, info->device_name, info->count);
+			 
+	if (!info->count || tty_hung_up_p(filp))
+		goto cleanup;
+			
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * tty->count is 1 and the tty structure will be freed.
+		 * info->count should be one in this case.
+		 * if it's not, correct it so that the port is shutdown.
+		 */
+		printk("mgsl_close: bad refcount; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	
+	info->count--;
+	
+	/* if at least one open remaining, leave hardware active */
+	if (info->count)
+		goto cleanup;
+	
+	info->flags |= ASYNC_CLOSING;
+	
+	/* Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+		
+	/* set tty->closing to notify line discipline to 
+	 * only process XON/XOFF characters. Only the N_TTY
+	 * discipline appears to use this (ppp does not).
+	 */
+	tty->closing = 1;
+	
+	/* wait for transmit data to clear all layers */
+	
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n",
+				 __FILE__,__LINE__, info->device_name );
+		tty_wait_until_sent(tty, info->closing_wait);
+	}
+		
+ 	if (info->flags & ASYNC_INITIALIZED)
+ 		mgsl_wait_until_sent(tty, info->timeout);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+		
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+		
+	shutdown(info);
+	
+	tty->closing = 0;
+	info->tty = 0;
+	
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(info->close_delay);
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+			 
+	wake_up_interruptible(&info->close_wait);
+	
+cleanup:			
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__,
+			tty->driver.name, info->count);
+	if(MOD_IN_USE)
+		MOD_DEC_USE_COUNT;
+			
+}	/* end of mgsl_close() */
+
+/* mgsl_wait_until_sent()
+ *
+ *	Wait until the transmitter is empty.
+ *
+ * Arguments:
+ *
+ *	tty		pointer to tty info structure
+ *	timeout		time to wait for send completion
+ *
+ * Return Value:	None
+ */
+static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+
+	if (!info )
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_wait_until_sent(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+      
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_wait_until_sent"))
+		return;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		goto exit;
+	 
+	orig_jiffies = jiffies;
+      
+	/* Set check interval to 1/5 of estimated time to
+	 * send a character, and make it at least 1. The check
+	 * interval should also be less than the timeout.
+	 * Note: use tight timings here to satisfy the NIST-PCTS.
+	 */ 
+       
+	if ( info->params.data_rate ) {
+	       	char_time = info->timeout/(32 * 5);
+		if (!char_time)
+			char_time++;
+	} else
+		char_time = 1;
+		
+	if (timeout)
+		char_time = MIN(char_time, timeout);
+		
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		while (info->tx_active) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->counter = 0;   /* make us low-priority */
+			schedule_timeout(char_time);
+			if (signal_pending(current))
+				break;
+			if (timeout && ((orig_jiffies + timeout) < jiffies))
+				break;
+		}
+	} else {
+		while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) &&
+			info->tx_enabled) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->counter = 0;   /* make us low-priority */
+			schedule_timeout(char_time);
+			if (signal_pending(current))
+				break;
+			if (timeout && ((orig_jiffies + timeout) < jiffies))
+				break;
+		}
+	}
+      
+	current->state = TASK_RUNNING;
+exit:
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_wait_until_sent(%s) exit\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+}	/* end of mgsl_wait_until_sent() */
+
+/* mgsl_hangup()
+ *
+ *	Called by tty_hangup() when a hangup is signaled.
+ *	This is the same as to closing all open files for the port.
+ *
+ * Arguments:		tty	pointer to associated tty object
+ * Return Value:	None
+ */
+static void mgsl_hangup(struct tty_struct *tty)
+{
+	struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_hangup(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_hangup"))
+		return;
+
+	mgsl_flush_buffer(tty);
+	shutdown(info);
+	
+	info->count = 0;	
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+
+	wake_up_interruptible(&info->open_wait);
+	
+}	/* end of mgsl_hangup() */
+
+/* block_til_ready()
+ * 
+ * 	Block the current process until the specified port
+ * 	is ready to be opened.
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty info structure
+ * 	filp		pointer to open file object
+ * 	info		pointer to device instance data
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct mgsl_struct *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 0, extra_count = 0;
+	unsigned long	flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):block_til_ready on %s\n",
+			 __FILE__,__LINE__, tty->driver.name );
+
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		/* this is a callout device */
+		/* just verify that normal device is not in use */
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+		/* nonblock mode is set or port is not enabled */
+		/* just verify that callout device is not active */
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/* Wait for carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * mgsl_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	 
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):block_til_ready before block on %s count=%d\n",
+			 __FILE__,__LINE__, tty->driver.name, info->count );
+
+	save_flags(flags); cli();
+	if (!tty_hung_up_p(filp)) {
+		extra_count = 1;
+		info->count--;
+	}
+	restore_flags(flags);
+	info->blocked_open++;
+	
+	while (1) {
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ 		    (tty->termios->c_cflag & CBAUD)) {
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+		 	usc_set_serial_signals(info);
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		}
+		
+		current->state = TASK_INTERRUPTIBLE;
+		
+		if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)){
+			retval = (info->flags & ASYNC_HUP_NOTIFY) ?
+					-EAGAIN : -ERESTARTSYS;
+			break;
+		}
+		
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+	 	usc_get_serial_signals(info);
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		
+ 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ 		    !(info->flags & ASYNC_CLOSING) &&
+ 		    (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) {
+ 			break;
+		}
+			
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):block_til_ready blocking on %s count=%d\n",
+				 __FILE__,__LINE__, tty->driver.name, info->count );
+				 
+		schedule();
+	}
+	
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	
+	if (extra_count)
+		info->count++;
+	info->blocked_open--;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):block_til_ready after blocking on %s count=%d\n",
+			 __FILE__,__LINE__, tty->driver.name, info->count );
+			 
+	if (!retval)
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		
+	return retval;
+	
+}	/* end of block_til_ready() */
+
+/* mgsl_open()
+ *
+ *	Called when a port is opened.  Init and enable port.
+ *	Perform serial-specific initialization for the tty structure.
+ *
+ * Arguments:		tty	pointer to tty info structure
+ *			filp	associated file pointer
+ *
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgsl_open(struct tty_struct *tty, struct file * filp)
+{
+	struct mgsl_struct	*info;
+	int 			retval, line;
+	unsigned long		page;
+
+	/* verify range of specified line number */	
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= mgsl_device_count)) {
+		printk("%s(%d):mgsl_open with illegal line #%d.\n",
+			__FILE__,__LINE__,line);
+		return -ENODEV;
+	}
+
+	/* find the info structure for the specified line */
+	info = mgsl_device_list;
+	while(info && info->line != line)
+		info = info->next_device;
+	if ( !info ){
+		printk("%s(%d):Can't find specified device on open (line=%d)\n",
+			__FILE__,__LINE__,line);
+		return -ENODEV;
+	}
+	
+	tty->driver_data = info;
+	info->tty = tty;
+	if (mgsl_paranoia_check(info, tty->device, "mgsl_open"))
+		return -ENODEV;
+		
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_open(%s), old ref count = %d\n",
+			 __FILE__,__LINE__,tty->driver.name, info->count);
+
+	MOD_INC_USE_COUNT;
+	
+	/* If port is closing, signal caller to try again */
+	if (tty_hung_up_p(filp) || info->flags & ASYNC_CLOSING){
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+		goto cleanup;
+	}
+	
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page) {
+			retval = -ENOMEM;
+			goto cleanup;
+		}
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+	
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+#endif
+
+	info->count++;
+	if (info->count == 1) {
+		/* 1st open on this device, init hardware */
+		retval = startup(info);
+		if (retval < 0)
+			goto cleanup;
+	}
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):block_til_ready(%s) returned %d\n",
+				 __FILE__,__LINE__, info->device_name, retval);
+		goto cleanup;
+	}
+
+	if ((info->count == 1) &&
+	    info->flags & ASYNC_SPLIT_TERMIOS) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		mgsl_change_params(info);
+	}
+	
+	info->session = current->session;
+	info->pgrp    = current->pgrp;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgsl_open(%s) success\n",
+			 __FILE__,__LINE__, info->device_name);
+	retval = 0;
+	
+cleanup:			
+	if (retval) {
+		if(MOD_IN_USE)
+			MOD_DEC_USE_COUNT;
+		if(info->count)
+			info->count--;
+	}
+	
+	return retval;
+	
+}	/* end of mgsl_open() */
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct mgsl_struct *info)
+{
+	char	stat_buf[30];
+	int	ret;
+	unsigned long flags;
+
+	if (info->bus_type == MGSL_BUS_TYPE_PCI) {
+		ret = sprintf(buf, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X",
+			info->device_name, info->io_base, info->irq_level,
+			info->phys_memory_base, info->phys_lcr_base);
+	} else {
+		ret = sprintf(buf, "%s:(E)ISA io:%04X irq:%d dma:%d",
+			info->device_name, info->io_base, 
+			info->irq_level, info->dma_level);
+	}
+
+	/* output current serial signal states */
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+ 	usc_get_serial_signals(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (info->serial_signals & SerialSignal_RTS)
+		strcat(stat_buf, "|RTS");
+	if (info->serial_signals & SerialSignal_CTS)
+		strcat(stat_buf, "|CTS");
+	if (info->serial_signals & SerialSignal_DTR)
+		strcat(stat_buf, "|DTR");
+	if (info->serial_signals & SerialSignal_DSR)
+		strcat(stat_buf, "|DSR");
+	if (info->serial_signals & SerialSignal_DCD)
+		strcat(stat_buf, "|CD");
+	if (info->serial_signals & SerialSignal_RI)
+		strcat(stat_buf, "|RI");
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d",
+			      info->icount.txok, info->icount.rxok);
+		if (info->icount.txunder)
+			ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder);
+		if (info->icount.txabort)
+			ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort);
+		if (info->icount.rxshort)
+			ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort);	
+		if (info->icount.rxlong)
+			ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong);
+		if (info->icount.rxover)
+			ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover);
+		if (info->icount.rxcrc)
+			ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxcrc);
+	} else {
+		ret += sprintf(buf+ret, " ASYNC tx:%d rx:%d",
+			      info->icount.tx, info->icount.rx);
+		if (info->icount.frame)
+			ret += sprintf(buf+ret, " fe:%d", info->icount.frame);
+		if (info->icount.parity)
+			ret += sprintf(buf+ret, " pe:%d", info->icount.parity);
+		if (info->icount.brk)
+			ret += sprintf(buf+ret, " brk:%d", info->icount.brk);	
+		if (info->icount.overrun)
+			ret += sprintf(buf+ret, " oe:%d", info->icount.overrun);
+	}
+	
+	/* Append serial signal status to end */
+	ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+	
+	ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d bh_q=%p\n",
+	 info->tx_active,info->bh_requested,info->bh_running,
+	 info->bh_queue_head);
+	 
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	{	
+	u16 Tscr = usc_InReg( info, TCSR );
+	u16 Tdmr = usc_InDmaReg( info, TDMR );
+	u16 Ticr = usc_InReg( info, TICR );
+	u16 Rscr = usc_InReg( info, RCSR );
+	u16 Rdmr = usc_InDmaReg( info, RDMR );
+	u16 Ricr = usc_InReg( info, RICR );
+	u16 Icr = usc_InReg( info, ICR );
+	u16 Dccr = usc_InReg( info, DCCR );
+	u16 Tmr = usc_InReg( info, TMR );
+	u16 Tccr = usc_InReg( info, TCCR );
+	u16 Ccar = inw( info->io_base + CCAR );
+	ret += sprintf(buf+ret, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n"
+                        "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n",
+	 		Tscr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar );
+	}
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+#if 0 && LINUX_VERSION_CODE >= VERSION(2,1,0)
+	ret += sprintf(buf+ret, "irq_spinlock=%08X\n",
+	 		info->irq_spinlock.lock );
+#endif
+	
+	return ret;
+	
+}	/* end of line_info() */
+
+/* mgsl_read_proc()
+ * 
+ * Called to print information about devices
+ * 
+ * Arguments:
+ * 	page	page of memory to hold returned info
+ * 	start	
+ * 	off
+ * 	count
+ * 	eof
+ * 	data
+ * 	
+ * Return Value:
+ */
+int mgsl_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int len = 0, l;
+	off_t	begin = 0;
+	struct mgsl_struct *info;
+	
+	len += sprintf(page, "synclink driver:%s\n", driver_version);
+	
+	info = mgsl_device_list;
+	while( info ) {
+		l = line_info(page + len, info);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+		info = info->next_device;
+	}
+
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+	
+}	/* end of mgsl_read_proc() */
+
+/* mgsl_allocate_dma_buffers()
+ * 
+ * 	Allocate and format DMA buffers (ISA adapter)
+ * 	or format shared memory buffers (PCI adapter).
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	0 if success, otherwise error
+ */
+int mgsl_allocate_dma_buffers(struct mgsl_struct *info)
+{
+	unsigned short BuffersPerFrame;
+
+	info->last_mem_alloc = 0;
+	
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		/*
+		 * The PCI adapter has 256KBytes of shared memory to use.
+		 * This is 64 PAGE_SIZE buffers. 1 is used for the buffer
+		 * list. 2 are used for the transmit and one is left as
+		 * a spare. The 4K buffer list can hold 128 DMA_BUFFER
+		 * structures at 32bytes each.
+		 */
+
+		info->rx_buffer_count = 60;
+		info->tx_buffer_count = 2;
+	} else {
+		/* Calculate the number of PAGE_SIZE buffers needed for */
+		/* receive and transmit DMA buffers. */
+
+		/* Calculate the number of DMA buffers necessary to hold the */
+		/* largest allowable frame size. Note: If the max frame size is */
+		/* not an even multiple of the DMA buffer size then we need to */
+		/* round the buffer count per frame up one. */
+
+		BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE);
+		if ( info->max_frame_size % DMABUFFERSIZE )
+			BuffersPerFrame++;
+
+		/* Calculate the number of DMA buffers necessary to */
+		/* hold 7 max size receive frames and one max size transmit frame. */
+		/* The receive buffer count is bumped by one so we avoid an */
+		/* End of List condition if all receive buffers are used when */
+		/* using linked list DMA buffers. */
+
+		info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6;
+		info->tx_buffer_count = BuffersPerFrame;
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n",
+			__FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count);
+	
+	if ( mgsl_alloc_buffer_list_memory( info ) < 0 ||
+		  mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || 
+		  mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0) {
+		printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__);
+		return -ENOMEM;
+	}
+	
+	mgsl_reset_rx_dma_buffers( info );
+
+	return 0;
+
+}	/* end of mgsl_allocate_dma_buffers() */
+
+/*
+ * mgsl_alloc_buffer_list_memory()
+ * 
+ * Allocate a common DMA buffer for use as the
+ * receive and transmit buffer lists.
+ * 
+ * A buffer list is a set of buffer entries where each entry contains
+ * a pointer to an actual buffer and a pointer to the next buffer entry
+ * (plus some other info about the buffer).
+ * 
+ * The buffer entries for a list are built to form a circular list so
+ * that when the entire list has been traversed you start back at the
+ * beginning.
+ * 
+ * This function allocates memory for just the buffer entries.
+ * The links (pointer to next entry) are filled in with the physical
+ * address of the next entry so the adapter can navigate the list
+ * using bus master DMA. The pointers to the actual buffers are filled
+ * out later when the actual buffers are allocated.
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	0 if success, otherwise error
+ */
+int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info )
+{
+	unsigned int i;
+
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		/* PCI adapter uses shared memory. */
+		info->buffer_list = info->memory_base + info->last_mem_alloc;
+		info->buffer_list_phys = info->last_mem_alloc;
+		info->last_mem_alloc += BUFFERLISTSIZE;
+	} else {
+		/* ISA adapter uses system memory. */
+		/* The buffer lists are allocated as a common buffer that both */
+		/* the processor and adapter can access. This allows the driver to */
+		/* inspect portions of the buffer while other portions are being */
+		/* updated by the adapter using Bus Master DMA. */
+
+		info->buffer_list = kmalloc(BUFFERLISTSIZE, GFP_KERNEL | GFP_DMA);
+		if ( info->buffer_list == NULL )
+			return -ENOMEM;
+			
+		info->buffer_list_phys = virt_to_bus(info->buffer_list);
+	}
+
+	/* We got the memory for the buffer entry lists. */
+	/* Initialize the memory block to all zeros. */
+	memset( info->buffer_list, 0, BUFFERLISTSIZE );
+
+	/* Save virtual address pointers to the receive and */
+	/* transmit buffer lists. (Receive 1st). These pointers will */
+	/* be used by the processor to access the lists. */
+	info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list;
+	info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list;
+	info->tx_buffer_list += info->rx_buffer_count;
+
+	/*
+	 * Build the links for the buffer entry lists such that
+	 * two circular lists are built. (Transmit and Receive).
+	 *
+	 * Note: the links are physical addresses
+	 * which are read by the adapter to determine the next
+	 * buffer entry to use.
+	 */
+
+	for ( i = 0; i < info->rx_buffer_count; i++ ) {
+		/* calculate and store physical address of this buffer entry */
+		info->rx_buffer_list[i].phys_entry =
+			info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY));
+
+		/* calculate and store physical address of */
+		/* next entry in cirular list of entries */
+
+		info->rx_buffer_list[i].link = info->buffer_list_phys;
+
+		if ( i < info->rx_buffer_count - 1 )
+			info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY);
+	}
+
+	for ( i = 0; i < info->tx_buffer_count; i++ ) {
+		/* calculate and store physical address of this buffer entry */
+		info->tx_buffer_list[i].phys_entry = info->buffer_list_phys +
+			((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY));
+
+		/* calculate and store physical address of */
+		/* next entry in cirular list of entries */
+
+		info->tx_buffer_list[i].link = info->buffer_list_phys +
+			info->rx_buffer_count * sizeof(DMABUFFERENTRY);
+
+		if ( i < info->tx_buffer_count - 1 )
+			info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY);
+	}
+
+	return 0;
+
+}	/* end of mgsl_alloc_buffer_list_memory() */
+
+/*
+ * mgsl_free_buffer_list_memory()
+ * 
+ * 	Free the common DMA buffer allocated for use as the
+ * 	receive and transmit buffer lists. The associated Memory
+ * 	Descriptor List (MDL) is also freed.
+ * 
+ * Warning:
+ * 
+ * 	The data transfer buffers associated with the buffer list
+ * 	MUST be freed before freeing the buffer list itself because
+ * 	the buffer list contains the information necessary to free
+ * 	the individual buffers!
+ * 
+ * Arguments:		info	pointer to device extension
+ * Return Value:	None
+ */
+void mgsl_free_buffer_list_memory( struct mgsl_struct *info )
+{
+	if ( info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI )
+		kfree_s(info->buffer_list, BUFFERLISTSIZE);
+		
+	info->buffer_list = NULL;
+	info->rx_buffer_list = NULL;
+	info->tx_buffer_list = NULL;
+
+}	/* end of mgsl_free_buffer_list_memory() */
+
+/*
+ * mgsl_alloc_frame_memory()
+ * 
+ * 	Allocate the frame DMA buffers used by the specified buffer list.
+ * 	Each DMA buffer will be one memory page in size. This is necessary
+ * 	because memory can fragment enough that it may be impossible
+ * 	contiguous pages.
+ * 
+ * Arguments:
+ * 
+ *	info		pointer to device instance data
+ * 	BufferList	pointer to list of buffer entries
+ * 	Buffercount	count of buffer entries in buffer list
+ * 
+ * Return Value:	0 if success, otherwise -ENOMEM
+ */
+int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount)
+{
+	int i;
+	unsigned long phys_addr;
+
+	/* Allocate page sized buffers for the receive buffer list */
+
+	for ( i = 0; i < Buffercount; i++ ) {
+		if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+			/* PCI adapter uses shared memory buffers. */
+			BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc;
+			phys_addr = info->last_mem_alloc;
+			info->last_mem_alloc += DMABUFFERSIZE;
+		} else {
+			/* ISA adapter uses system memory. */
+			BufferList[i].virt_addr = 
+				kmalloc(DMABUFFERSIZE, GFP_KERNEL | GFP_DMA);
+			if ( BufferList[i].virt_addr == NULL )
+				return -ENOMEM;
+			phys_addr = virt_to_bus(BufferList[i].virt_addr);
+		}
+		BufferList[i].phys_addr = phys_addr;
+	}
+
+	return 0;
+
+}	/* end of mgsl_alloc_frame_memory() */
+
+/*
+ * mgsl_free_frame_memory()
+ * 
+ * 	Free the buffers associated with
+ * 	each buffer entry of a buffer list.
+ * 
+ * Arguments:
+ * 
+ *	info		pointer to device instance data
+ * 	BufferList	pointer to list of buffer entries
+ * 	Buffercount	count of buffer entries in buffer list
+ * 
+ * Return Value:	None
+ */
+void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount)
+{
+	int i;
+
+	if ( BufferList ) {
+		for ( i = 0 ; i < Buffercount ; i++ ) {
+			if ( BufferList[i].virt_addr ) {
+				if ( info->bus_type != MGSL_BUS_TYPE_PCI )
+					kfree_s(BufferList[i].virt_addr, DMABUFFERSIZE);
+				BufferList[i].virt_addr = NULL;
+			}
+		}
+	}
+
+}	/* end of mgsl_free_frame_memory() */
+
+/* mgsl_free_dma_buffers()
+ * 
+ * 	Free DMA buffers
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_free_dma_buffers( struct mgsl_struct *info )
+{
+	mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count );
+	mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count );
+	mgsl_free_buffer_list_memory( info );
+
+}	/* end of mgsl_free_dma_buffers() */
+
+/* mgsl_claim_resources()
+ * 
+ * 	Claim all resources used by a device
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	0 if success, otherwise -ENODEV
+ */
+int mgsl_claim_resources(struct mgsl_struct *info)
+{
+	/* claim 16C32 I/O base address */
+	
+	if ( check_region(info->io_base,info->io_addr_size) < 0 ) {
+		printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->io_base );
+		return -ENODEV;
+	}
+	request_region(info->io_base,info->io_addr_size,"synclink.o");
+	info->io_addr_requested = 1;
+	
+	/* claim interrupt level */
+	
+	if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags,
+		info->device_name, info ) < 0 ) {
+		printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n",
+			__FILE__,__LINE__,info->device_name, info->irq_level );
+		mgsl_release_resources( info );
+		return -ENODEV;
+	}
+	info->irq_requested = 1;
+	
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		/* claim shared memory range */
+		info->memory_base = ioremap(info->phys_memory_base,0x40000);
+		if (!info->memory_base) {
+			printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n",
+				__FILE__,__LINE__,info->device_name, info->phys_memory_base );
+			mgsl_release_resources( info );
+			return -ENODEV;
+		}
+		
+		/* test the shared memory range */
+		if ( !mgsl_memory_test(info) ) {
+			printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n",
+				__FILE__,__LINE__,info->device_name, info->phys_memory_base );
+			mgsl_release_resources( info );
+			return -ENODEV;
+		}
+		
+		/* claim LCR memory range */
+		info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset;
+		if (!info->lcr_base) {
+			printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n",
+				__FILE__,__LINE__,info->device_name, info->phys_lcr_base );
+			mgsl_release_resources( info );
+			return -ENODEV;
+		}
+		
+	} else {
+		/* claim DMA channel */
+		
+		if (request_dma(info->dma_level,info->device_name) < 0){
+			printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n",
+				__FILE__,__LINE__,info->device_name, info->dma_level );
+			mgsl_release_resources( info );
+			return -ENODEV;
+		}
+		info->dma_requested = 1;
+
+		/* ISA adapter uses bus master DMA */		
+		set_dma_mode(info->dma_level,DMA_MODE_CASCADE);
+		enable_dma(info->dma_level);
+	}
+	
+	if ( mgsl_allocate_dma_buffers(info) < 0 ) {
+		printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n",
+			__FILE__,__LINE__,info->device_name, info->dma_level );
+		mgsl_release_resources( info );
+		return -ENODEV;
+	}	
+	
+	return 0;
+		
+}	/* end of mgsl_claim_resources() */
+
+/* mgsl_release_resources()
+ * 
+ * 	Release all resources used by a device
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_release_resources(struct mgsl_struct *info)
+{
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_release_resources(%s) entry\n",
+			__FILE__,__LINE__,info->device_name );
+			
+	if ( info->irq_requested ) {
+		free_irq(info->irq_level, info);
+		info->irq_requested = 0;
+	}
+	
+	if ( info->dma_requested ) {
+		disable_dma(info->dma_level);
+		free_dma(info->dma_level);
+		info->dma_requested = 0;
+	}
+	mgsl_free_dma_buffers(info);
+	
+	if ( info->io_addr_requested ) {
+		release_region(info->io_base,info->io_addr_size);
+		info->io_addr_requested = 0;
+	}
+		
+	if (info->memory_base){
+		iounmap(info->memory_base);
+		info->memory_base = 0;
+	}
+		
+	if (info->lcr_base){
+		iounmap(info->lcr_base - info->lcr_offset);
+		info->lcr_base = 0;
+	}
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_release_resources(%s) exit\n",
+			__FILE__,__LINE__,info->device_name );
+			
+}	/* end of mgsl_release_resources() */
+
+/* mgsl_add_device()
+ * 
+ * 	Add the specified device instance data structure to the
+ * 	global linked list of devices and increment the device count.
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_add_device( struct mgsl_struct *info )
+{
+	info->next_device = NULL;
+	info->line = mgsl_device_count;
+	sprintf(info->device_name,"ttySL%d",info->line);
+	
+	mgsl_device_count++;
+	
+	if ( !mgsl_device_list )
+		mgsl_device_list = info;
+	else {	
+		struct mgsl_struct *current_dev = mgsl_device_list;
+		while( current_dev->next_device )
+			current_dev = current_dev->next_device;
+		current_dev->next_device = info;
+	}
+	
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		printk( "SyncLink device %s added:PCI bus IO=%04X IRQ=%d Mem=%08X LCR=%08X\n",
+			info->device_name, info->io_base, info->irq_level,
+			info->phys_memory_base, info->phys_lcr_base );
+	} else {
+		printk( "SyncLink device %s added:ISA bus IO=%04X IRQ=%d DMA=%d\n",
+			info->device_name, info->io_base, info->irq_level, info->dma_level );
+	}
+	
+}	/* end of mgsl_add_device() */
+
+/* mgsl_allocate_device()
+ * 
+ * 	Allocate and initialize a device instance structure
+ * 	
+ * Arguments:		None
+ * Return Value:	pointer to mgsl_struct if success, otherwise NULL
+ */
+struct mgsl_struct* mgsl_allocate_device()
+{
+	struct mgsl_struct *info;
+	
+	info = (struct mgsl_struct *)kmalloc(sizeof(struct mgsl_struct),
+		 GFP_KERNEL);
+		 
+	if (!info) {
+		printk("Error can't allocate device instance data\n");
+	} else {
+		memset(info, 0, sizeof(struct mgsl_struct));
+		info->magic = MGSL_MAGIC;
+		info->task.sync = 0;
+		info->task.routine = mgsl_bh_handler;
+		info->task.data    = info;
+		info->max_frame_size = 4096;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+
+		memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+		info->idle_mode = HDLC_TXIDLE_FLAGS;		
+	}
+	
+	return info;
+
+}	/* end of mgsl_allocate_device()*/
+
+/* mgsl_enumerate_devices()
+ * 
+ * 	Enumerate SyncLink serial devices based on user specified
+ *	options for ISA adapters and autodetected PCI adapters.
+ * 	
+ * Arguments:		None
+ * Return Value:	0 if success, otherwise error code
+ */
+int mgsl_enumerate_devices()
+{
+	struct mgsl_struct *info;
+	int i;
+		
+	/* Check for user specified ISA devices */
+	
+	for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk("ISA device specified io=%04X,irq=%d,dma=%d\n",
+				io[i], irq[i], dma[i] );
+		
+		info = mgsl_allocate_device();
+		if ( !info ) {
+			/* error allocating device instance data */
+			if ( debug_level >= DEBUG_LEVEL_ERROR )
+				printk( "can't allocate device instance data.\n");
+			continue;
+		}
+		
+		/* Copy user configuration info to device instance data */
+		info->io_base = (unsigned int)io[i];
+		info->irq_level = (unsigned int)irq[i];
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+		info->irq_level = irq_cannonicalize(info->irq_level);
+#else		
+		if (info->irq_level == 2)
+			info->irq_level = 9;
+#endif			
+		info->dma_level = (unsigned int)dma[i];
+		info->bus_type = MGSL_BUS_TYPE_ISA;
+		info->io_addr_size = 16;
+		info->irq_flags = 0;
+				
+		/* add new device to device list */
+		mgsl_add_device( info );
+	}
+	
+	
+#ifdef CONFIG_PCI
+	/* Auto detect PCI adapters */
+	
+	if ( pcibios_present() ) {
+		unsigned char bus;
+		unsigned char func;
+		unsigned int  shared_mem_base;
+		unsigned int  lcr_mem_base;
+		unsigned int  io_base;
+		unsigned char irq_line;
+		
+		for(i=0;;i++){
+			if ( PCIBIOS_SUCCESSFUL == pcibios_find_device(
+				MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) {
+				
+				if (pcibios_read_config_dword(bus,func,
+					PCI_BASE_ADDRESS_3,&shared_mem_base) ) {
+					printk( "%s(%d):Shared mem addr not set.\n",
+						__FILE__,__LINE__);
+					continue;
+				}
+							
+				if (pcibios_read_config_dword(bus,func,
+					PCI_BASE_ADDRESS_0,&lcr_mem_base) ) {
+					printk( "%s(%d):LCR mem addr not set.\n",
+						__FILE__,__LINE__);
+					continue;
+				}
+				
+				if (pcibios_read_config_dword(bus,func,
+					PCI_BASE_ADDRESS_2,&io_base) ) {
+					printk( "%s(%d):USC I/O addr not set.\n",
+						__FILE__,__LINE__);
+					continue;
+				}
+				
+				if (pcibios_read_config_byte(bus,func,
+					PCI_INTERRUPT_LINE,&irq_line) ) {
+					printk( "%s(%d):USC I/O addr not set.\n",
+						__FILE__,__LINE__);
+					continue;
+				}
+				
+				info = mgsl_allocate_device();
+				if ( !info ) {
+					/* error allocating device instance data */
+					if ( debug_level >= DEBUG_LEVEL_ERROR )
+						printk( "can't allocate device instance data.\n");
+					continue;
+				}
+		
+				/* Copy user configuration info to device instance data */
+		
+				info->io_base = io_base & PCI_BASE_ADDRESS_IO_MASK;
+				info->irq_level = (unsigned int)irq_line;
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+				info->irq_level = irq_cannonicalize(info->irq_level);
+#else		
+				if (info->irq_level == 2)
+					info->irq_level = 9;
+#endif			
+				info->phys_memory_base = shared_mem_base & PCI_BASE_ADDRESS_MEM_MASK;
+				
+				/* Because veremap only works on page boundaries we must map
+				 * a larger area than is actually implemented for the LCR
+				 * memory range. We map a full page starting at the page boundary.
+				 */
+				info->phys_lcr_base = lcr_mem_base & PCI_BASE_ADDRESS_MEM_MASK;
+				info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
+				info->phys_lcr_base &= ~(PAGE_SIZE-1);
+				
+				info->bus_type = MGSL_BUS_TYPE_PCI;
+				info->io_addr_size = 8;
+				info->irq_flags = SA_SHIRQ;
+				info->bus = bus;
+				info->function = func;
+				
+		/* Store the PCI9050 misc control register value because a flaw
+		 * in the PCI9050 prevents LCR registers from being read if 
+		 * BIOS assigns an LCR base address with bit 7 set.
+		 *  
+		 * Only the misc control register is accessed for which only 
+		 * write access is needed, so set an initial value and change 
+		 * bits to the device instance data as we write the value
+		 * to the actual misc control register.
+		 */
+				info->misc_ctrl_value = 0x087e4546;
+				
+				/* add new device to device list */
+				mgsl_add_device( info );
+			} else {
+				break;
+			}
+		}
+	}
+#endif
+
+	/*
+	 * Allocate memory to hold the following tty/termios arrays
+	 * with an element for each enumerated device.
+	 */	
+	
+	serial_table = (struct tty_struct**)kmalloc(sizeof(struct tty_struct*)*mgsl_device_count, GFP_KERNEL);
+	serial_termios = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL);
+	serial_termios_locked = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL);
+	
+	if (!serial_table || !serial_termios || !serial_termios_locked){
+		printk("%s(%d):Can't allocate tty/termios arrays.\n",
+			__FILE__,__LINE__);
+		return -ENOMEM;
+	}
+	
+	memset(serial_table,0,sizeof(struct tty_struct*)*mgsl_device_count);
+	memset(serial_termios,0,sizeof(struct termios*)*mgsl_device_count);
+	memset(serial_termios_locked,0,sizeof(struct termios*)*mgsl_device_count);
+
+	return 0;
+	
+}	/* end of mgsl_enumerate_devices() */
+
+/* mgsl_init()
+ * 
+ * 	Driver initialization entry point.
+ * 	
+ * Arguments:	None
+ * Return Value:	0 if success, otherwise error code
+ */
+int __init mgsl_init(void)
+{
+	struct mgsl_struct *info;
+
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+	EXPORT_NO_SYMBOLS;
+#else
+	register_symtab(NULL);
+#endif		
+	
+ 	printk("%s version %s\n", driver_name, driver_version);
+	
+	/* determine how many SyncLink devices are installed */
+	mgsl_enumerate_devices();
+	if ( !mgsl_device_list ) {
+		printk("%s(%d):No SyncLink devices found.\n",__FILE__,__LINE__);
+		return -ENODEV;
+	}
+
+	/* Initialize the tty_driver structure */
+	
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+	serial_driver.driver_name = "synclink";
+#endif	
+	serial_driver.name = "ttySL";
+	serial_driver.major = ttymajor;
+	serial_driver.minor_start = 64;
+	serial_driver.num = mgsl_device_count;
+	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	serial_driver.init_termios = tty_std_termios;
+	serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = mgsl_open;
+	serial_driver.close = mgsl_close;
+	serial_driver.write = mgsl_write;
+	serial_driver.put_char = mgsl_put_char;
+	serial_driver.flush_chars = mgsl_flush_chars;
+	serial_driver.write_room = mgsl_write_room;
+	serial_driver.chars_in_buffer = mgsl_chars_in_buffer;
+	serial_driver.flush_buffer = mgsl_flush_buffer;
+	serial_driver.ioctl = mgsl_ioctl;
+	serial_driver.throttle = mgsl_throttle;
+	serial_driver.unthrottle = mgsl_unthrottle;
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+	serial_driver.send_xchar = mgsl_send_xchar;
+	serial_driver.break_ctl = mgsl_break;
+	serial_driver.wait_until_sent = mgsl_wait_until_sent;
+ 	serial_driver.read_proc = mgsl_read_proc;
+#endif	
+	serial_driver.set_termios = mgsl_set_termios;
+	serial_driver.stop = mgsl_stop;
+	serial_driver.start = mgsl_start;
+	serial_driver.hangup = mgsl_hangup;
+	
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cuaSL";
+	callout_driver.major = cuamajor;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+#if LINUX_VERSION_CODE >= VERSION(2,1,0)
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
+#endif	
+
+	if (tty_register_driver(&serial_driver) < 0)
+		printk("%s(%d):Couldn't register serial driver\n",
+			__FILE__,__LINE__);
+			
+	if (tty_register_driver(&callout_driver) < 0)
+		printk("%s(%d):Couldn't register callout driver\n",
+			__FILE__,__LINE__);
+
+ 	printk("%s version %s, tty major#%d callout major#%d\n",
+		driver_name, driver_version,
+		serial_driver.major, callout_driver.major);
+		
+	/* Propagate these values to all device instances */
+	
+	info = mgsl_device_list;
+	while(info){
+		info->callout_termios = callout_driver.init_termios;
+		info->normal_termios  = serial_driver.init_termios;
+		info = info->next_device;
+	}
+	
+	return 0;
+	
+}	/* end of mgsl_init() */
+
+#ifdef MODULE
+int init_module(void)
+{
+/* Uncomment this to kernel debug module.
+ * mgsl_get_text_ptr() leaves the .text address in eax
+ * which can be used with add-symbol-file with gdb.
+ */
+	if (break_on_load) {
+	 	mgsl_get_text_ptr();
+  		BREAKPOINT();
+	}
+	
+	return mgsl_init();
+}
+
+void cleanup_module(void) 
+{
+	unsigned long flags;
+	int rc;
+	struct mgsl_struct *info;
+
+	printk("Unloading %s: version %s\n", driver_name, driver_version);
+	save_flags(flags);
+	cli();
+	if ((rc = tty_unregister_driver(&serial_driver)))
+		printk("%s(%d) failed to unregister tty driver err=%d\n",
+		       __FILE__,__LINE__,rc);
+	if ((rc = tty_unregister_driver(&callout_driver)))
+		printk("%s(%d) failed to unregister callout driver err=%d\n",
+		       __FILE__,__LINE__,rc);
+	restore_flags(flags);
+
+	info = mgsl_device_list;
+	while(info) {
+		mgsl_release_resources(info);
+		info = info->next_device;
+	}
+	
+	if (tmp_buf) {
+		free_page((unsigned long) tmp_buf);
+		tmp_buf = NULL;
+	}
+	
+	if (serial_table)
+		kfree_s(serial_table,sizeof(struct tty_struct*)*mgsl_device_count);
+		
+	if (serial_termios)
+		kfree_s(serial_termios,sizeof(struct termios*)*mgsl_device_count);
+		
+	if (serial_termios_locked)
+		kfree_s(serial_termios_locked,sizeof(struct termios*)*mgsl_device_count);
+	
+}	/* end of cleanup_module() */
+
+#endif /* MODULE */
+
+
+/*
+ * usc_RTCmd()
+ *
+ * Issue a USC Receive/Transmit command to the
+ * Channel Command/Address Register (CCAR).
+ *
+ * Notes:
+ *
+ *    The command is encoded in the most significant 5 bits <15..11>
+ *    of the CCAR value. Bits <10..7> of the CCAR must be preserved
+ *    and Bits <6..0> must be written as zeros.
+ *
+ * Arguments:
+ *
+ *    info   pointer to device information structure
+ *    Cmd    command mask (use symbolic macros)
+ *
+ * Return Value:
+ *
+ *    None
+ */
+void usc_RTCmd( struct mgsl_struct *info, u16 Cmd )
+{
+	/* output command to CCAR in bits <15..11> */
+	/* preserve bits <10..7>, bits <6..0> must be zero */
+
+	outw( Cmd + info->loopback_bits, info->io_base + CCAR );
+
+	/* Read to flush write to CCAR */
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+		inw( info->io_base + CCAR );
+
+}	/* end of usc_RTCmd() */
+
+/*
+ * usc_DmaCmd()
+ *
+ *    Issue a DMA command to the DMA Command/Address Register (DCAR).
+ *
+ * Arguments:
+ *
+ *    info   pointer to device information structure
+ *    Cmd    DMA command mask (usc_DmaCmd_XX Macros)
+ *
+ * Return Value:
+ *
+ *       None
+ */
+void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd )
+{
+	/* write command mask to DCAR */
+	outw( Cmd + info->mbre_bit, info->io_base );
+
+	/* Read to flush write to DCAR */
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+		inw( info->io_base );
+
+}	/* end of usc_DmaCmd() */
+
+/*
+ * usc_OutDmaReg()
+ *
+ *    Write a 16-bit value to a USC DMA register
+ *
+ * Arguments:
+ *
+ *    info      pointer to device info structure
+ *    RegAddr   register address (number) for write
+ *    RegValue  16-bit value to write to register
+ *
+ * Return Value:
+ *
+ *    None
+ *
+ */
+void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue )
+{
+	/* Note: The DCAR is located at the adapter base address */
+	/* Note: must preserve state of BIT8 in DCAR */
+
+	outw( RegAddr + info->mbre_bit, info->io_base );
+	outw( RegValue, info->io_base );
+
+	/* Read to flush write to DCAR */
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+		inw( info->io_base );
+
+}	/* end of usc_OutDmaReg() */
+ 
+/*
+ * usc_InDmaReg()
+ *
+ *    Read a 16-bit value from a DMA register
+ *
+ * Arguments:
+ *
+ *    info     pointer to device info structure
+ *    RegAddr  register address (number) to read from
+ *
+ * Return Value:
+ *
+ *    The 16-bit value read from register
+ *
+ */
+u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr )
+{
+	/* Note: The DCAR is located at the adapter base address */
+	/* Note: must preserve state of BIT8 in DCAR */
+
+	outw( RegAddr + info->mbre_bit, info->io_base );
+	return inw( info->io_base );
+
+}	/* end of usc_InDmaReg() */
+
+/*
+ *
+ * usc_OutReg()
+ *
+ *    Write a 16-bit value to a USC serial channel register 
+ *
+ * Arguments:
+ *
+ *    info      pointer to device info structure
+ *    RegAddr   register address (number) to write to
+ *    RegValue  16-bit value to write to register
+ *
+ * Return Value:
+ *
+ *    None
+ *
+ */
+void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue )
+{
+	outw( RegAddr + info->loopback_bits, info->io_base + CCAR );
+	outw( RegValue, info->io_base + CCAR );
+
+	/* Read to flush write to CCAR */
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+		inw( info->io_base + CCAR );
+
+}	/* end of usc_OutReg() */
+
+/*
+ * usc_InReg()
+ *
+ *    Reads a 16-bit value from a USC serial channel register
+ *
+ * Arguments:
+ *
+ *    info       pointer to device extension
+ *    RegAddr    register address (number) to read from
+ *
+ * Return Value:
+ *
+ *    16-bit value read from register
+ */
+u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr )
+{
+	outw( RegAddr + info->loopback_bits, info->io_base + CCAR );
+	return inw( info->io_base + CCAR );
+
+}	/* end of usc_InReg() */
+
+/* usc_set_sdlc_mode()
+ *
+ *    Set up the adapter for SDLC DMA communications.
+ *
+ * Arguments:		info    pointer to device instance data
+ * Return Value: 	NONE
+ */
+void usc_set_sdlc_mode( struct mgsl_struct *info )
+{
+	u16 RegValue;
+
+	/* Channel mode Register (CMR)
+	 *
+	 * <15..14>  00    Tx Sub modes, Underrun Action
+	 * <13>      0     1 = Send Preamble before opening flag
+	 * <12>      0     1 = Consecutive Idles share common 0
+	 * <11..8>   0110  Transmitter mode = HDLC/SDLC
+	 * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
+	 * <3..0>    0110  Receiver mode = HDLC/SDLC
+	 *
+	 * 0000 0110 0000 0110 = 0x0606
+	 */
+
+	RegValue = 0x0606;
+
+	if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
+		RegValue |= BIT14;
+	else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
+		RegValue |= BIT15;
+	else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
+		RegValue |= BIT15 + BIT14;
+
+	if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
+		RegValue |= BIT13;
+
+	if ( info->params.flags & HDLC_FLAG_SHARE_ZERO )
+		RegValue |= BIT12;
+
+	if ( info->params.addr_filter != 0xff )
+	{
+		/* set up receive address filtering */
+		usc_OutReg( info, RSR, info->params.addr_filter );
+		RegValue |= BIT4;
+	}
+
+	usc_OutReg( info, CMR, RegValue );
+	info->cmr_value = RegValue;
+
+	/* Receiver mode Register (RMR)
+	 *
+	 * <15..13>  000    encoding
+	 * <12..11>  00     FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1)
+	 * <10>      1      1 = Set CRC to all 1s (use for SDLC/HDLC)
+	 * <9>       0      1 = Include Receive chars in CRC
+	 * <8>       1      1 = Use Abort/PE bit as abort indicator
+	 * <7..6>    00     Even parity
+	 * <5>       0      parity disabled
+	 * <4..2>    000    Receive Char Length = 8 bits
+	 * <1..0>    00     Disable Receiver
+	 *
+	 * 0000 0101 0000 0000 = 0x0500
+	 */
+
+	RegValue = 0x0500;
+
+	switch ( info->params.encoding ) {
+	case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
+	case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
+	case HDLC_ENCODING_NRZI_SPACE:	       RegValue |= BIT14 + BIT13; break;
+	case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
+	case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
+	case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
+	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
+	}
+
+	if ( info->params.crc_type == HDLC_CRC_16_CCITT )
+		RegValue |= BIT9;
+
+	usc_OutReg( info, RMR, RegValue );
+
+
+ 
+	/* Set the Receive count Limit Register (RCLR) to 0xffff. */
+	/* When an opening flag of an SDLC frame is recognized the */
+	/* Receive Character count (RCC) is loaded with the value in */
+	/* RCLR. The RCC is decremented for each received byte.  The */
+	/* value of RCC is stored after the closing flag of the frame */
+	/* allowing the frame size to be computed. */
+
+	usc_OutReg( info, RCLR, RCLRVALUE );
+
+	usc_RCmd( info, RCmd_SelectRicrdma_level );
+
+	/* Receive Interrupt Control Register (RICR)
+	 *
+	 * <15..8>	?	RxFIFO DMA Request Level
+	 * <7>		0	Exited Hunt IA (Interrupt Arm)
+	 * <6>		0	Idle Received IA
+	 * <5>		0	Break/Abort IA
+	 * <4>		0	Rx Bound IA
+	 * <3>		1	Queued status reflects oldest 2 bytes in FIFO
+	 * <2>		0	Abort/PE IA
+	 * <1>		1	Rx Overrun IA
+	 * <0>		0	Select TC0 value for readback
+	 *
+	 *	0000 0000 0000 1000 = 0x000a
+	 */
+
+	/* Carry over the Exit Hunt and Idle Received bits */
+	/* in case they have been armed by usc_ArmEvents.   */
+
+	RegValue = usc_InReg( info, RICR ) & 0xc0;
+
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+		usc_OutReg( info, RICR, (u16)(0x030a | RegValue) );
+	else
+		usc_OutReg( info, RICR, (u16)(0x140a | RegValue) );
+
+	/* Unlatch all Rx status bits and clear Rx status IRQ Pending */
+
+	usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+
+	/* Transmit mode Register (TMR)
+	 *	
+	 * <15..13>	000	encoding
+	 * <12..11>	00	FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1)
+	 * <10>		1	1 = Start CRC as all 1s (use for SDLC/HDLC)
+	 * <9>		0	1 = Tx CRC Enabled
+	 * <8>		0	1 = Append CRC to end of transmit frame
+	 * <7..6>	00	Transmit parity Even
+	 * <5>		0	Transmit parity Disabled
+	 * <4..2>	000	Tx Char Length = 8 bits
+	 * <1..0>	00	Disable Transmitter
+	 *
+	 * 	0000 0100 0000 0000 = 0x0400
+	 */
+
+	RegValue = 0x0400;
+
+	switch ( info->params.encoding ) {
+	case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
+	case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
+	case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
+	case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
+	case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
+	case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
+	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
+	}
+
+	if ( info->params.crc_type == HDLC_CRC_16_CCITT )
+		RegValue |= BIT9 + BIT8;
+
+	usc_OutReg( info, TMR, RegValue );
+
+	usc_set_txidle( info );
+
+
+	usc_TCmd( info, TCmd_SelectTicrdma_level );
+
+	/* Transmit Interrupt Control Register (TICR)
+	 *
+	 * <15..8>	?	Transmit FIFO DMA Level
+	 * <7>		0	Present IA (Interrupt Arm)
+	 * <6>		0	Idle Sent IA
+	 * <5>		1	Abort Sent IA
+	 * <4>		1	EOF/EOM Sent IA
+	 * <3>		0	CRC Sent IA
+	 * <2>		1	1 = Wait for SW Trigger to Start Frame
+	 * <1>		1	Tx Underrun IA
+	 * <0>		0	TC0 constant on read back
+	 *
+	 *	0000 0000 0011 0110 = 0x0036
+	 */
+
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+		usc_OutReg( info, TICR, 0x0736 );
+	else								
+		usc_OutReg( info, TICR, 0x1436 );
+
+	usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+
+	/* Clock mode Control Register (CMCR)
+	 *
+	 * <15..14>	00	counter 1 Source = Disabled
+	 * <13..12> 	00	counter 0 Source = Disabled
+	 * <11..10> 	11	BRG1 Input is TxC Pin
+	 * <9..8>	11	BRG0 Input is TxC Pin
+	 * <7..6>	01	DPLL Input is BRG1 Output
+	 * <5..3>	XXX	TxCLK comes from Port 0
+	 * <2..0>   	XXX	RxCLK comes from Port 1
+	 *
+	 *	0000 1111 0111 0111 = 0x0f77
+	 */
+
+	RegValue = 0x0f40;
+
+	if ( info->params.flags & HDLC_FLAG_RXC_DPLL )
+		RegValue |= 0x0003;	/* RxCLK from DPLL */
+	else if ( info->params.flags & HDLC_FLAG_RXC_BRG )
+		RegValue |= 0x0004;	/* RxCLK from BRG0 */
+	else
+		RegValue |= 0x0007;	/* RxCLK from Port1 */
+
+	if ( info->params.flags & HDLC_FLAG_TXC_DPLL )
+		RegValue |= 0x0018;	/* TxCLK from DPLL */
+	else if ( info->params.flags & HDLC_FLAG_TXC_BRG )
+		RegValue |= 0x0020;	/* TxCLK from BRG0 */
+	else
+		RegValue |= 0x0030;	/* TxCLK from Port0 */
+
+	usc_OutReg( info, CMCR, RegValue );
+
+
+	/* Hardware Configuration Register (HCR)
+	 *
+	 * <15..14>	00	CTR0 Divisor:00=32,01=16,10=8,11=4
+	 * <13>		0	CTR1DSel:0=CTR0Div determines CTR0Div
+	 * <12>		0	CVOK:0=report code violation in biphase
+	 * <11..10>	00	DPLL Divisor:00=32,01=16,10=8,11=4
+	 * <9..8>	XX	DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level
+	 * <7..6>	00	reserved
+	 * <5>		0	BRG1 mode:0=continuous,1=single cycle
+	 * <4>		X	BRG1 Enable
+	 * <3..2>	00	reserved
+	 * <1>		0	BRG0 mode:0=continuous,1=single cycle
+	 * <0>		0	BRG0 Enable
+	 */
+
+	RegValue = 0x0000;
+
+	if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) {
+		u32 XtalSpeed;
+		u32 DpllDivisor;
+		u16 Tc;
+
+		/*  DPLL is enabled. Use BRG1 to provide continuous reference clock  */
+		/*  for DPLL. DPLL mode in HCR is dependent on the encoding used. */
+
+		if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+			XtalSpeed = 11059200;
+		else
+			XtalSpeed = 14745600;
+
+		if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) {
+			DpllDivisor = 16;
+			RegValue |= BIT10;
+		}
+		else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) {
+			DpllDivisor = 8;
+			RegValue |= BIT11;
+		}
+		else
+			DpllDivisor = 32;
+
+		/*  Tc = (Xtal/Speed) - 1 */
+		/*  If twice the remainder of (Xtal/Speed) is greater than Speed */
+		/*  then rounding up gives a more precise time constant. Instead */
+		/*  of rounding up and then subtracting 1 we just don't subtract */
+		/*  the one in this case. */
+
+		Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
+		if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
+		       / info->params.clock_speed) )
+			Tc--;
+
+		/* Write 16-bit Time Constant for BRG1 */
+		usc_OutReg( info, TC1R, Tc );
+
+		RegValue |= BIT4;		/* enable BRG1 */
+
+		switch ( info->params.encoding ) {
+		case HDLC_ENCODING_NRZ:
+		case HDLC_ENCODING_NRZB:
+		case HDLC_ENCODING_NRZI_MARK:
+		case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break;
+		case HDLC_ENCODING_BIPHASE_MARK:
+		case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break;
+		case HDLC_ENCODING_BIPHASE_LEVEL:
+		case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break;
+		}
+	}
+
+	usc_OutReg( info, HCR, RegValue );
+
+
+	/* Channel Control/status Register (CCSR)
+	 *
+	 * <15>		X	RCC FIFO Overflow status (RO)
+	 * <14>		X	RCC FIFO Not Empty status (RO)
+	 * <13>		0	1 = Clear RCC FIFO (WO)
+	 * <12>		X	DPLL Sync (RW)
+	 * <11>		X	DPLL 2 Missed Clocks status (RO)
+	 * <10>		X	DPLL 1 Missed Clock status (RO)
+	 * <9..8>	00	DPLL Resync on rising and falling edges (RW)
+	 * <7>		X	SDLC Loop On status (RO)
+	 * <6>		X	SDLC Loop Send status (RO)
+	 * <5>		1	Bypass counters for TxClk and RxClk (RW)
+	 * <4..2>   	000	Last Char of SDLC frame has 8 bits (RW)
+	 * <1..0>   	00	reserved
+	 *
+	 *	0000 0000 0010 0000 = 0x0020
+	 */
+
+	usc_OutReg( info, CCSR, 0x1020 );
+
+
+	if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) {
+		usc_OutReg( info, SICR,
+			    (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) );
+	}
+	
+
+	/* enable Master Interrupt Enable bit (MIE) */
+	usc_EnableMasterIrqBit( info );
+
+	usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA +
+				TRANSMIT_STATUS + TRANSMIT_DATA );
+
+	info->mbre_bit = 0;
+	outw( 0, info->io_base ); 			/* clear Master Bus Enable (DCAR) */
+	usc_DmaCmd( info, DmaCmd_ResetAllChannels );	/* disable both DMA channels */
+	info->mbre_bit = BIT8;
+	outw( BIT8, info->io_base );			/* set Master Bus Enable (DCAR) */
+
+	/* Enable DMAEN (Port 7, Bit 14) */
+	/* This connects the DMA request signal to the ISA bus */
+	/* on the ISA adapter. This has no effect for the PCI adapter */
+	usc_OutReg( info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14) );
+
+	/* DMA Control Register (DCR)
+	 *
+	 * <15..14>	10	Priority mode = Alternating Tx/Rx
+	 *		01	Rx has priority
+	 *		00	Tx has priority
+	 *
+	 * <13>		1	Enable Priority Preempt per DCR<15..14>
+	 *			(WARNING DCR<11..10> must be 00 when this is 1)
+	 *		0	Choose activate channel per DCR<11..10>
+	 *
+	 * <12>		0	Little Endian for Array/List
+	 * <11..10>	00	Both Channels can use each bus grant
+	 * <9..6>	0000	reserved
+	 * <5>		0	7 CLK - Minimum Bus Re-request Interval
+	 * <4>		0	1 = drive D/C and S/D pins
+	 * <3>		1	1 = Add one wait state to all DMA cycles.
+	 * <2>		0	1 = Strobe /UAS on every transfer.
+	 * <1..0>	11	Addr incrementing only affects LS24 bits
+	 *
+	 *	0110 0000 0000 1011 = 0x600b
+	 */
+
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		/* PCI adapter does not need DMA wait state */
+		usc_OutDmaReg( info, DCR, 0xa00b );
+	}
+	else
+		usc_OutDmaReg( info, DCR, 0x800b );
+
+
+	/* Receive DMA mode Register (RDMR)
+	 *
+	 * <15..14>	11	DMA mode = Linked List Buffer mode
+	 * <13>		1	RSBinA/L = store Rx status Block in Arrary/List entry
+	 * <12>		1	Clear count of List Entry after fetching
+	 * <11..10>	00	Address mode = Increment
+	 * <9>		1	Terminate Buffer on RxBound
+	 * <8>		0	Bus Width = 16bits
+	 * <7..0>	?	status Bits (write as 0s)
+	 *
+	 * 1111 0010 0000 0000 = 0xf200
+	 */
+
+	usc_OutDmaReg( info, RDMR, 0xf200 );
+
+
+	/* Transmit DMA mode Register (TDMR)
+	 *
+	 * <15..14>	11	DMA mode = Linked List Buffer mode
+	 * <13>		1	TCBinA/L = fetch Tx Control Block from List entry
+	 * <12>		1	Clear count of List Entry after fetching
+	 * <11..10>	00	Address mode = Increment
+	 * <9>		1	Terminate Buffer on end of frame
+	 * <8>		0	Bus Width = 16bits
+	 * <7..0>	?	status Bits (Read Only so write as 0)
+	 *
+	 *	1111 0010 0000 0000 = 0xf200
+	 */
+
+	usc_OutDmaReg( info, TDMR, 0xf200 );
+
+
+	/* DMA Interrupt Control Register (DICR)
+	 *
+	 * <15>		1	DMA Interrupt Enable
+	 * <14>		0	1 = Disable IEO from USC
+	 * <13>		0	1 = Don't provide vector during IntAck
+	 * <12>		1	1 = Include status in Vector
+	 * <10..2>	0	reserved, Must be 0s
+	 * <1>		0	1 = Rx DMA Interrupt Enabled
+	 * <0>		0	1 = Tx DMA Interrupt Enabled
+	 *
+	 *	1001 0000 0000 0000 = 0x9000
+	 */
+
+	usc_OutDmaReg( info, DICR, 0x9000 );
+
+	usc_InDmaReg( info, RDMR );		/* clear pending receive DMA IRQ bits */
+	usc_InDmaReg( info, TDMR );		/* clear pending transmit DMA IRQ bits */
+	usc_OutDmaReg( info, CDIR, 0x0303 );	/* clear IUS and Pending for Tx and Rx */
+
+	/* Channel Control Register (CCR)
+	 *
+	 * <15..14>	10	Use 32-bit Tx Control Blocks (TCBs)
+	 * <13>		0	Trigger Tx on SW Command Disabled
+	 * <12>		0	Flag Preamble Disabled
+	 * <11..10>	00	Preamble Length
+	 * <9..8>	00	Preamble Pattern
+	 * <7..6>	10	Use 32-bit Rx status Blocks (RSBs)
+	 * <5>		0	Trigger Rx on SW Command Disabled
+	 * <4..0>	0	reserved
+	 *
+	 *	1000 0000 1000 0000 = 0x8080
+	 */
+
+	RegValue = 0x8080;
+
+	switch ( info->params.preamble_length ) {
+	case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break;
+	case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break;
+	case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break;
+	}
+
+	switch ( info->params.preamble ) {
+	case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break;
+	case HDLC_PREAMBLE_PATTERN_ONES:  RegValue |= BIT8; break;
+	case HDLC_PREAMBLE_PATTERN_10:    RegValue |= BIT9; break;
+	case HDLC_PREAMBLE_PATTERN_01:    RegValue |= BIT9 + BIT8; break;
+	}
+
+	usc_OutReg( info, CCR, RegValue );
+
+
+	/*
+	 * Burst/Dwell Control Register
+	 *
+	 * <15..8>	0x20	Maximum number of transfers per bus grant
+	 * <7..0>	0x00	Maximum number of clock cycles per bus grant
+	 */
+
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		/* don't limit bus occupancy on PCI adapter */
+		usc_OutDmaReg( info, BDCR, 0x0000 );
+	}
+	else
+		usc_OutDmaReg( info, BDCR, 0x2000 );
+
+	usc_stop_transmitter(info);
+	usc_stop_receiver(info);
+	
+}	/* end of usc_set_sdlc_mode() */
+
+/* usc_enable_loopback()
+ *
+ * Set the 16C32 for internal loopback mode.
+ * The TxCLK and RxCLK signals are generated from the BRG0 and
+ * the TxD is looped back to the RxD internally.
+ *
+ * Arguments:		info	pointer to device instance data
+ *			enable	1 = enable loopback, 0 = disable
+ * Return Value:	None
+ */
+void usc_enable_loopback(struct mgsl_struct *info, int enable)
+{
+	if (enable) {
+		/* blank external TXD output */
+		usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6));
+	
+		/* Clock mode Control Register (CMCR)
+		 *
+		 * <15..14>	00	counter 1 Disabled
+		 * <13..12> 	00	counter 0 Disabled
+		 * <11..10> 	11	BRG1 Input is TxC Pin
+		 * <9..8>	11	BRG0 Input is TxC Pin
+		 * <7..6>	01	DPLL Input is BRG1 Output
+		 * <5..3>	100	TxCLK comes from BRG0
+		 * <2..0>   	100	RxCLK comes from BRG0
+		 *
+		 * 0000 1111 0110 0100 = 0x0f64
+		 */
+
+		usc_OutReg( info, CMCR, 0x0f64 );
+
+		/* Write 16-bit Time Constant for BRG0 */
+		/* use clock speed if available, otherwise use 8 for diagnostics */
+		if (info->params.clock_speed) {
+			if (info->bus_type == MGSL_BUS_TYPE_PCI)
+				usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1));
+			else
+				usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1));
+		} else
+			usc_OutReg(info, TC0R, (u16)8);
+
+		/* Hardware Configuration Register (HCR) Clear Bit 1, BRG0
+		   mode = Continuous Set Bit 0 to enable BRG0.  */
+		usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
+
+		/* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
+		usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004));
+
+		/* set Internal Data loopback mode */
+		info->loopback_bits = 0x300;
+		outw( 0x0300, info->io_base + CCAR );
+	} else {
+		/* enable external TXD output */
+		usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6));
+	
+		/* clear Internal Data loopback mode */
+		info->loopback_bits = 0;
+		outw( 0,info->io_base + CCAR );
+	}
+	
+}	/* end of usc_enable_loopback() */
+
+/* usc_enable_aux_clock()
+ *
+ * Enabled the AUX clock output at the specified frequency.
+ *
+ * Arguments:
+ *
+ *	info		pointer to device extension
+ *	data_rate	data rate of clock in bits per second
+ *			A data rate of 0 disables the AUX clock.
+ *
+ * Return Value:	None
+ */
+void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate )
+{
+	u32 XtalSpeed;
+	u16 Tc;
+
+	if ( data_rate ) {
+		if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+			XtalSpeed = 11059200;
+		else
+			XtalSpeed = 14745600;
+
+
+		/* Tc = (Xtal/Speed) - 1 */
+		/* If twice the remainder of (Xtal/Speed) is greater than Speed */
+		/* then rounding up gives a more precise time constant. Instead */
+		/* of rounding up and then subtracting 1 we just don't subtract */
+		/* the one in this case. */
+
+
+		Tc = (u16)(XtalSpeed/data_rate);
+		if ( !(((XtalSpeed % data_rate) * 2) / data_rate) )
+			Tc--;
+
+		/* Write 16-bit Time Constant for BRG0 */
+		usc_OutReg( info, TC0R, Tc );
+
+		/*
+		 * Hardware Configuration Register (HCR)
+		 * Clear Bit 1, BRG0 mode = Continuous
+		 * Set Bit 0 to enable BRG0.
+		 */
+
+		usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
+
+		/* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
+		usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) );
+	} else {
+		/* data rate == 0 so turn off BRG0 */
+		usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) );
+	}
+
+}	/* end of usc_enable_aux_clock() */
+
+/* usc_stop_receiver()
+ *
+ *	Disable USC receiver
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_stop_receiver( struct mgsl_struct *info )
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):usc_stop_receiver(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	/* Disable receive DMA channel. */
+	/* This also disables receive DMA channel interrupts */
+	usc_DmaCmd( info, DmaCmd_ResetRxChannel );
+
+	usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+	usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+	usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS );
+
+	usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
+
+	/* This empties the receive FIFO and loads the RCC with RCLR */
+	usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
+	usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+	info->rx_enabled = 0;
+	info->rx_overflow = 0;
+	
+}	/* end of stop_receiver() */
+
+/* usc_start_receiver()
+ *
+ *	Enable the USC receiver 
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_start_receiver( struct mgsl_struct *info )
+{
+	u32 phys_addr;
+	
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):usc_start_receiver(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	mgsl_reset_rx_dma_buffers( info );
+	usc_stop_receiver( info );
+
+	usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
+	usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		/* DMA mode Transfers */
+		/* Program the DMA controller. */
+		/* Enable the DMA controller end of buffer interrupt. */
+
+		/* program 16C32 with physical address of 1st DMA buffer entry */
+		phys_addr = info->rx_buffer_list[0].phys_entry;
+		usc_OutDmaReg( info, NRARL, (u16)phys_addr );
+		usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
+
+		usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+		usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+		usc_EnableInterrupts( info, RECEIVE_STATUS );
+
+		/* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
+		/* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
+
+		usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
+		usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
+		usc_DmaCmd( info, DmaCmd_InitRxChannel );
+		if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
+			usc_EnableReceiver(info,ENABLE_AUTO_DCD);
+		else
+			usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+	} else {
+		usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
+		usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
+		usc_EnableInterrupts(info, RECEIVE_DATA);
+
+		usc_RTCmd( info, RTCmd_PurgeRxFifo );
+		usc_RCmd( info, RCmd_EnterHuntmode );
+
+		usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+	}
+
+	usc_OutReg( info, CCSR, 0x1020 );
+
+	info->rx_enabled = 1;
+
+}	/* end of usc_start_receiver() */
+
+/* usc_start_transmitter()
+ *
+ *	Enable the USC transmitter and send a transmit frame if
+ *	one is loaded in the DMA buffers.
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_start_transmitter( struct mgsl_struct *info )
+{
+	u32 phys_addr;
+	unsigned int FrameSize;
+
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):usc_start_transmitter(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	if ( info->xmit_cnt ) {
+
+		/* If auto RTS enabled and RTS is inactive, then assert */
+		/* RTS and set a flag indicating that the driver should */
+		/* negate RTS when the transmission completes. */
+
+		info->drop_rts_on_tx_done = 0;
+
+		if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) {
+			usc_get_serial_signals( info );
+			if ( !(info->serial_signals & SerialSignal_RTS) ) {
+				info->serial_signals |= SerialSignal_RTS;
+				usc_set_serial_signals( info );
+				info->drop_rts_on_tx_done = 1;
+			}
+		}
+
+
+		if ( info->params.mode == MGSL_MODE_ASYNC ) {
+			if ( !info->tx_active ) {
+				usc_UnlatchTxstatusBits(info, TXSTATUS_ALL);
+				usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA);
+				usc_EnableInterrupts(info, TRANSMIT_DATA);
+				usc_load_txfifo(info);
+			}
+		} else {
+			/* Disable transmit DMA controller while programming. */
+			usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+			
+			/* Transmit DMA buffer is loaded, so program USC */
+			/* to send the frame contained in the buffers.	 */
+
+
+			FrameSize = info->tx_buffer_list[0].rcc;
+
+			/* Program the Transmit Character Length Register (TCLR) */
+			/* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
+			usc_OutReg( info, TCLR, (u16)FrameSize );
+
+			usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+			/* Program the address of the 1st DMA Buffer Entry in linked list */
+			phys_addr = info->tx_buffer_list[0].phys_entry;
+			usc_OutDmaReg( info, NTARL, (u16)phys_addr );
+			usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) );
+
+			usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+			usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+			usc_EnableInterrupts( info, TRANSMIT_STATUS );
+
+			/* Initialize Transmit DMA Channel */
+			usc_DmaCmd( info, DmaCmd_InitTxChannel );
+			
+			usc_TCmd( info, TCmd_SendFrame );
+			
+			info->tx_timer.expires = jiffies + jiffies_from_ms(5000);
+			add_timer(&info->tx_timer);	
+		}
+		info->tx_active = 1;
+	}
+
+	if ( !info->tx_enabled ) {
+		info->tx_enabled = 1;
+		if ( info->params.flags & HDLC_FLAG_AUTO_CTS )
+			usc_EnableTransmitter(info,ENABLE_AUTO_CTS);
+		else
+			usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL);
+	}
+
+}	/* end of usc_start_transmitter() */
+
+/* usc_stop_transmitter()
+ *
+ *	Stops the transmitter and DMA
+ *
+ * Arguments:		info	pointer to device isntance data
+ * Return Value:	None
+ */
+void usc_stop_transmitter( struct mgsl_struct *info )
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):usc_stop_transmitter(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	del_timer(&info->tx_timer);	
+			 
+	usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA );
+	usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA );
+
+	usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL);
+	usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+	usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+	info->tx_enabled = 0;
+	info->tx_active  = 0;
+
+}	/* end of usc_stop_transmitter() */
+
+/* usc_load_txfifo()
+ *
+ *	Fill the transmit FIFO until the FIFO is full or
+ *	there is no more data to load.
+ *
+ * Arguments:		info	pointer to device extension (instance data)
+ * Return Value:	None
+ */
+void usc_load_txfifo( struct mgsl_struct *info )
+{
+	int Fifocount;
+	u8 TwoBytes[2];
+	
+	if ( !info->xmit_cnt && !info->x_char )
+		return; 
+		
+	/* Select transmit FIFO status readback in TICR */
+	usc_TCmd( info, TCmd_SelectTicrTxFifostatus );
+
+	/* load the Transmit FIFO until FIFOs full or all data sent */
+
+	while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) {
+		/* there is more space in the transmit FIFO and */
+		/* there is more data in transmit buffer */
+
+		if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) {
+ 			/* write a 16-bit word from transmit buffer to 16C32 */
+				
+			TwoBytes[0] = info->xmit_buf[info->xmit_tail++];
+			info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+			TwoBytes[1] = info->xmit_buf[info->xmit_tail++];
+			info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+			
+			outw( *((u16 *)TwoBytes), info->io_base + DATAREG);
+				
+			info->xmit_cnt -= 2;
+			info->icount.tx += 2;
+		} else {
+			/* only 1 byte left to transmit or 1 FIFO slot left */
+			
+			outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY),
+				info->io_base + CCAR );
+			
+			if (info->x_char) {
+				/* transmit pending high priority char */
+				outw( info->x_char,info->io_base + CCAR );
+				info->x_char = 0;
+			} else {
+				outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR );
+				info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+				info->xmit_cnt--;
+			}
+			info->icount.tx++;
+		}
+	}
+
+}	/* end of usc_load_txfifo() */
+
+/* usc_reset()
+ *
+ *	Reset the adapter to a known state and prepare it for further use.
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_reset( struct mgsl_struct *info )
+{
+	if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+		int i;
+		volatile u32 readval;
+
+		/* Set BIT30 of Misc Control Register */
+		/* (Local Control Register 0x50) to force reset of USC. */
+
+		u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50);
+		u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28);
+
+		info->misc_ctrl_value |= BIT30;
+		*MiscCtrl = info->misc_ctrl_value;
+
+		/*
+		 * Force at least 170ns delay before clearing 
+		 * reset bit. Each read from LCR takes at least 
+		 * 30ns so 10 times for 300ns to be safe.
+		 */
+		for(i=0;i<10;i++)
+			readval = *MiscCtrl;
+
+		info->misc_ctrl_value &= ~BIT30;
+		*MiscCtrl = info->misc_ctrl_value;
+
+		*LCR0BRDR = BUS_DESCRIPTOR(
+			1,		// Write Strobe Hold (0-3)
+			2,		// Write Strobe Delay (0-3)
+			2,		// Read Strobe Delay  (0-3)
+			0,		// NWDD (Write data-data) (0-3)
+			4,		// NWAD (Write Addr-data) (0-31)
+			0,		// NXDA (Read/Write Data-Addr) (0-3)
+			0,		// NRDD (Read Data-Data) (0-3)
+			5		// NRAD (Read Addr-Data) (0-31)
+			);
+	} else {
+		/* do HW reset */
+		outb( 0,info->io_base + 8 );
+	}
+
+	info->mbre_bit = 0;
+	info->loopback_bits = 0;
+	info->usc_idle_mode = 0;
+
+	/*
+	 * Program the Bus Configuration Register (BCR)
+	 *
+	 * <15>		0	Don't use seperate address
+	 * <14..6>	0	reserved
+	 * <5..4>	00	IAckmode = Default, don't care
+	 * <3>		1	Bus Request Totem Pole output
+	 * <2>		1	Use 16 Bit data bus
+	 * <1>		0	IRQ Totem Pole output
+	 * <0>		0	Don't Shift Right Addr
+	 *
+	 * 0000 0000 0000 1100 = 0x000c
+	 *
+	 * By writing to io_base + SDPIN the Wait/Ack pin is
+	 * programmed to work as a Wait pin.
+	 */
+	
+	outw( 0x000c,info->io_base + SDPIN );
+
+
+	outw( 0,info->io_base );
+	outw( 0,info->io_base + CCAR );
+
+	/* select little endian byte ordering */
+	usc_RTCmd( info, RTCmd_SelectLittleEndian );
+
+
+	/* Port Control Register (PCR)
+	 *
+	 * <15..14>	11	Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled)
+	 * <13..12>	11	Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled)
+	 * <11..10> 	00	Port 5 is Input (No Connect, Don't Care)
+	 * <9..8> 	00	Port 4 is Input (No Connect, Don't Care)
+	 * <7..6>	11	Port 3 is Output (~RTS, Bit 6 : 0 = Enabled )
+	 * <5..4>	11	Port 2 is Output (~DTR, Bit 4 : 0 = Enabled )
+	 * <3..2>	01	Port 1 is Input (Dedicated RxC)
+	 * <1..0>	01	Port 0 is Input (Dedicated TxC)
+	 *
+	 *	1111 0000 1111 0101 = 0xf0f5
+	 */
+
+	usc_OutReg( info, PCR, 0xf0f5 );
+
+
+	/*
+	 * Input/Output Control Register
+	 *
+	 * <15..14>	00	CTS is active low input
+	 * <13..12>	00	DCD is active low input
+	 * <11..10>	00	TxREQ pin is input (DSR)
+	 * <9..8>	00	RxREQ pin is input (RI)
+	 * <7..6>	00	TxD is output (Transmit Data)
+	 * <5..3>	000	TxC Pin in Input (14.7456MHz Clock)
+	 * <2..0>	100	RxC is Output (drive with BRG0)
+	 *
+	 *	0000 0000 0000 0100 = 0x0004
+	 */
+
+	usc_OutReg( info, IOCR, 0x0004 );
+
+}	/* end of usc_reset() */
+
+/* usc_set_async_mode()
+ *
+ *	Program adapter for asynchronous communications.
+ *
+ * Arguments:		info		pointer to device instance data
+ * Return Value:	None
+ */
+void usc_set_async_mode( struct mgsl_struct *info )
+{
+	u16 RegValue;
+
+	/* disable interrupts while programming USC */
+	usc_DisableMasterIrqBit( info );
+
+	outw( 0, info->io_base ); 			/* clear Master Bus Enable (DCAR) */
+	usc_DmaCmd( info, DmaCmd_ResetAllChannels );	/* disable both DMA channels */
+
+	usc_loopback_frame( info );
+
+	/* Channel mode Register (CMR)
+	 *
+	 * <15..14>	00	Tx Sub modes, 00 = 1 Stop Bit
+	 * <13..12>	00	              00 = 16X Clock
+	 * <11..8>	0000	Transmitter mode = Asynchronous
+	 * <7..6>	00	reserved?
+	 * <5..4>	00	Rx Sub modes, 00 = 16X Clock
+	 * <3..0>	0000	Receiver mode = Asynchronous
+	 *
+	 * 0000 0000 0000 0000 = 0x0
+	 */
+
+	RegValue = 0;
+	if ( info->params.stop_bits != 1 )
+		RegValue |= BIT14;
+	usc_OutReg( info, CMR, RegValue );
+
+	
+	/* Receiver mode Register (RMR)
+	 *
+	 * <15..13>	000	encoding = None
+	 * <12..08>	00000	reserved (Sync Only)
+	 * <7..6>   	00	Even parity
+	 * <5>		0	parity disabled
+	 * <4..2>	000	Receive Char Length = 8 bits
+	 * <1..0>	00	Disable Receiver
+	 *
+	 * 0000 0000 0000 0000 = 0x0
+	 */
+
+	RegValue = 0;
+
+	if ( info->params.data_bits != 8 )
+		RegValue |= BIT4+BIT3+BIT2;
+
+	if ( info->params.parity != ASYNC_PARITY_NONE ) {
+		RegValue |= BIT5;
+		if ( info->params.parity != ASYNC_PARITY_ODD )
+			RegValue |= BIT6;
+	}
+
+	usc_OutReg( info, RMR, RegValue );
+
+
+	/* Set IRQ trigger level */
+
+	usc_RCmd( info, RCmd_SelectRicrIntLevel );
+
+	
+	/* Receive Interrupt Control Register (RICR)
+	 *
+	 * <15..8>	?		RxFIFO IRQ Request Level
+	 *
+	 * Note: For async mode the receive FIFO level must be set
+	 * to 0 to aviod the situation where the FIFO contains fewer bytes
+	 * than the trigger level and no more data is expected.
+	 *
+	 * <7>		0		Exited Hunt IA (Interrupt Arm)
+	 * <6>		0		Idle Received IA
+	 * <5>		0		Break/Abort IA
+	 * <4>		0		Rx Bound IA
+	 * <3>		0		Queued status reflects oldest byte in FIFO
+	 * <2>		0		Abort/PE IA
+	 * <1>		0		Rx Overrun IA
+	 * <0>		0		Select TC0 value for readback
+	 *
+	 * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB)
+	 */
+	
+	usc_OutReg( info, RICR, 0x0000 );
+
+	usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+	usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+
+	
+	/* Transmit mode Register (TMR)
+	 *
+	 * <15..13>	000	encoding = None
+	 * <12..08>	00000	reserved (Sync Only)
+	 * <7..6>	00	Transmit parity Even
+	 * <5>		0	Transmit parity Disabled
+	 * <4..2>	000	Tx Char Length = 8 bits
+	 * <1..0>	00	Disable Transmitter
+	 *
+	 * 0000 0000 0000 0000 = 0x0
+	 */
+
+	RegValue = 0;
+
+	if ( info->params.data_bits != 8 )
+		RegValue |= BIT4+BIT3+BIT2;
+
+	if ( info->params.parity != ASYNC_PARITY_NONE ) {
+		RegValue |= BIT5;
+		if ( info->params.parity != ASYNC_PARITY_ODD )
+			RegValue |= BIT6;
+	}
+
+	usc_OutReg( info, TMR, RegValue );
+
+	usc_set_txidle( info );
+
+
+	/* Set IRQ trigger level */
+
+	usc_TCmd( info, TCmd_SelectTicrIntLevel );
+
+	
+	/* Transmit Interrupt Control Register (TICR)
+	 *
+	 * <15..8>	?	Transmit FIFO IRQ Level
+	 * <7>		0	Present IA (Interrupt Arm)
+	 * <6>		1	Idle Sent IA
+	 * <5>		0	Abort Sent IA
+	 * <4>		0	EOF/EOM Sent IA
+	 * <3>		0	CRC Sent IA
+	 * <2>		0	1 = Wait for SW Trigger to Start Frame
+	 * <1>		0	Tx Underrun IA
+	 * <0>		0	TC0 constant on read back
+	 *
+	 *	0000 0000 0100 0000 = 0x0040
+	 */
+
+	usc_OutReg( info, TICR, 0x1f40 );
+
+	usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+
+	usc_enable_async_clock( info, info->params.data_rate );
+
+	
+	/* Channel Control/status Register (CCSR)
+	 *
+	 * <15>		X	RCC FIFO Overflow status (RO)
+	 * <14>		X	RCC FIFO Not Empty status (RO)
+	 * <13>		0	1 = Clear RCC FIFO (WO)
+	 * <12>		X	DPLL in Sync status (RO)
+	 * <11>		X	DPLL 2 Missed Clocks status (RO)
+	 * <10>		X	DPLL 1 Missed Clock status (RO)
+	 * <9..8>	00	DPLL Resync on rising and falling edges (RW)
+	 * <7>		X	SDLC Loop On status (RO)
+	 * <6>		X	SDLC Loop Send status (RO)
+	 * <5>		1	Bypass counters for TxClk and RxClk (RW)
+	 * <4..2>   	000	Last Char of SDLC frame has 8 bits (RW)
+	 * <1..0>   	00	reserved
+	 *
+	 *	0000 0000 0010 0000 = 0x0020
+	 */
+	
+	usc_OutReg( info, CCSR, 0x0020 );
+
+	usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA +
+			      RECEIVE_DATA + RECEIVE_STATUS );
+
+	usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA +
+				RECEIVE_DATA + RECEIVE_STATUS );
+
+	usc_EnableMasterIrqBit( info );
+
+	/* Enable INTEN (Port 6, Bit12) */
+	/* This connects the IRQ request signal to the ISA bus */
+	/* on the ISA adapter. This has no effect for the PCI adapter */
+	usc_OutReg( info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12) );
+
+}	/* end of usc_set_async_mode() */
+
+/* usc_loopback_frame()
+ *
+ *	Loop back a small (2 byte) dummy SDLC frame.
+ *	Interrupts and DMA are NOT used. The purpose of this is to
+ *	clear any 'stale' status info left over from running in	async mode.
+ *
+ *	The 16C32 shows the strange behaviour of marking the 1st
+ *	received SDLC frame with a CRC error even when there is no
+ *	CRC error. To get around this a small dummy from of 2 bytes
+ *	is looped back when switching from async to sync mode.
+ *
+ * Arguments:		info		pointer to device instance data
+ * Return Value:	None
+ */
+void usc_loopback_frame( struct mgsl_struct *info )
+{
+	int i;
+	
+	usc_DisableMasterIrqBit( info );
+
+	usc_set_sdlc_mode( info );
+	usc_enable_loopback( info, 1 );
+
+	/* Write 16-bit Time Constant for BRG0 */
+	usc_OutReg( info, TC0R, 0 );
+	
+	/* Channel Control Register (CCR)
+	 *
+	 * <15..14>	00	Don't use 32-bit Tx Control Blocks (TCBs)
+	 * <13>		0	Trigger Tx on SW Command Disabled
+	 * <12>		0	Flag Preamble Disabled
+	 * <11..10>	00	Preamble Length = 8-Bits
+	 * <9..8>	01	Preamble Pattern = flags
+	 * <7..6>	10	Don't use 32-bit Rx status Blocks (RSBs)
+	 * <5>		0	Trigger Rx on SW Command Disabled
+	 * <4..0>	0	reserved
+	 *
+	 *	0000 0001 0000 0000 = 0x0100
+	 */
+
+	usc_OutReg( info, CCR, 0x0100 );
+
+	/* SETUP RECEIVER */
+	usc_RTCmd( info, RTCmd_PurgeRxFifo );
+	usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+
+	/* SETUP TRANSMITTER */
+	/* Program the Transmit Character Length Register (TCLR) */
+	/* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
+	usc_OutReg( info, TCLR, 2 );
+	usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+	/* unlatch Tx status bits, and start transmit channel. */
+	usc_UnlatchTxstatusBits(info,TXSTATUS_ALL);
+	outw(0,info->io_base + DATAREG);
+
+	/* ENABLE TRANSMITTER */
+	usc_TCmd( info, TCmd_SendFrame );
+	usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL);
+							
+	/* WAIT FOR RECEIVE COMPLETE */
+	for (i=0 ; i<1000 ; i++)
+		if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1))
+			break;
+
+	/* clear Internal Data loopback mode */
+	usc_enable_loopback(info, 0);
+
+	usc_EnableMasterIrqBit(info);
+
+}	/* end of usc_loopback_frame() */
+
+/* usc_set_sync_mode()	Programs the USC for SDLC communications.
+ *
+ * Arguments:		info	pointer to adapter info structure
+ * Return Value:	None
+ */
+void usc_set_sync_mode( struct mgsl_struct *info )
+{
+	usc_loopback_frame( info );
+	usc_set_sdlc_mode( info );
+
+	/* Enable INTEN (Port 6, Bit12) */
+	/* This connects the IRQ request signal to the ISA bus */
+	/* on the ISA adapter. This has no effect for the PCI adapter */
+	usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
+
+	usc_enable_aux_clock(info, info->params.clock_speed);
+
+	if (info->params.loopback)
+		usc_enable_loopback(info,1);
+
+}	/* end of mgsl_set_sync_mode() */
+
+/* usc_set_txidle()	Set the HDLC idle mode for the transmitter.
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_set_txidle( struct mgsl_struct *info )
+{
+	u16 usc_idle_mode = IDLEMODE_FLAGS;
+
+	/* Map API idle mode to USC register bits */
+
+	switch( info->idle_mode ){
+	case HDLC_TXIDLE_FLAGS:			usc_idle_mode = IDLEMODE_FLAGS; break;
+	case HDLC_TXIDLE_ALT_ZEROS_ONES:	usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break;
+	case HDLC_TXIDLE_ZEROS:			usc_idle_mode = IDLEMODE_ZERO; break;
+	case HDLC_TXIDLE_ONES:			usc_idle_mode = IDLEMODE_ONE; break;
+	case HDLC_TXIDLE_ALT_MARK_SPACE:	usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break;
+	case HDLC_TXIDLE_SPACE:			usc_idle_mode = IDLEMODE_SPACE; break;
+	case HDLC_TXIDLE_MARK:			usc_idle_mode = IDLEMODE_MARK; break;
+	}
+
+	info->usc_idle_mode = usc_idle_mode;
+	usc_OutReg(info, TCSR, usc_idle_mode);
+
+}	/* end of usc_set_txidle() */
+
+/* usc_get_serial_signals()
+ *
+ *	Query the adapter for the state of the V24 status (input) signals.
+ *
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_get_serial_signals( struct mgsl_struct *info )
+{
+	u16 status;
+
+	/* clear all serial signals except DTR and RTS */
+	info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
+
+	/* Read the Misc Interrupt status Register (MISR) to get */
+	/* the V24 status signals. */
+
+	status = usc_InReg( info, MISR );
+
+	/* set serial signal bits to reflect MISR */
+
+	if ( status & MISCSTATUS_CTS )
+		info->serial_signals |= SerialSignal_CTS;
+
+	if ( status & MISCSTATUS_DCD )
+		info->serial_signals |= SerialSignal_DCD;
+
+	if ( status & MISCSTATUS_RI )
+		info->serial_signals |= SerialSignal_RI;
+
+	if ( status & MISCSTATUS_DSR )
+		info->serial_signals |= SerialSignal_DSR;
+
+}	/* end of usc_get_serial_signals() */
+
+/* usc_set_serial_signals()
+ *
+ *	Set the state of DTR and RTS based on contents of
+ *	serial_signals member of device extension.
+ *	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void usc_set_serial_signals( struct mgsl_struct *info )
+{
+	u16 Control;
+	unsigned char V24Out = info->serial_signals;
+
+	/* get the current value of the Port Control Register (PCR) */
+
+	Control = usc_InReg( info, PCR );
+
+	if ( V24Out & SerialSignal_RTS )
+		Control &= ~(BIT6);
+	else
+		Control |= BIT6;
+
+	if ( V24Out & SerialSignal_DTR )
+		Control &= ~(BIT4);
+	else
+		Control |= BIT4;
+
+	usc_OutReg( info, PCR, Control );
+
+}	/* end of usc_set_serial_signals() */
+
+/* usc_enable_async_clock()
+ *
+ *	Enable the async clock at the specified frequency.
+ *
+ * Arguments:		info		pointer to device instance data
+ *			data_rate	data rate of clock in bps
+ *					0 disables the AUX clock.
+ * Return Value:	None
+ */
+void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate )
+{
+	if ( data_rate )	{
+		/*
+		 * Clock mode Control Register (CMCR)
+		 * 
+		 * <15..14>     00      counter 1 Disabled
+		 * <13..12>     00      counter 0 Disabled
+		 * <11..10>     11      BRG1 Input is TxC Pin
+		 * <9..8>       11      BRG0 Input is TxC Pin
+		 * <7..6>       01      DPLL Input is BRG1 Output
+		 * <5..3>       100     TxCLK comes from BRG0
+		 * <2..0>       100     RxCLK comes from BRG0
+		 *
+		 * 0000 1111 0110 0100 = 0x0f64
+		 */
+		
+		usc_OutReg( info, CMCR, 0x0f64 );
+
+
+		/*
+		 * Write 16-bit Time Constant for BRG0
+		 * Time Constant = (ClkSpeed / data_rate) - 1
+		 * ClkSpeed = 921600 (ISA), 691200 (PCI)
+		 */
+
+		if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+			usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) );
+		else
+			usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) );
+
+		
+		/*
+		 * Hardware Configuration Register (HCR)
+		 * Clear Bit 1, BRG0 mode = Continuous
+		 * Set Bit 0 to enable BRG0.
+		 */
+
+		usc_OutReg( info, HCR,
+			    (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
+
+
+		/* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
+
+		usc_OutReg( info, IOCR,
+			    (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) );
+	} else {
+		/* data rate == 0 so turn off BRG0 */
+		usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) );
+	}
+
+}	/* end of usc_enable_async_clock() */
+
+/*
+ * Buffer Structures:
+ *
+ * Normal memory access uses virtual addresses that can make discontiguous
+ * physical memory pages appear to be contiguous in the virtual address
+ * space (the processors memory mapping handles the conversions).
+ *
+ * DMA transfers require physically contiguous memory. This is because
+ * the DMA system controller and DMA bus masters deal with memory using
+ * only physical addresses.
+ *
+ * This causes a problem under Windows NT when large DMA buffers are
+ * needed. Fragmentation of the nonpaged pool prevents allocations of
+ * physically contiguous buffers larger than the PAGE_SIZE.
+ *
+ * However the 16C32 supports Bus Master Scatter/Gather DMA which
+ * allows DMA transfers to physically discontiguous buffers. Information
+ * about each data transfer buffer is contained in a memory structure
+ * called a 'buffer entry'. A list of buffer entries is maintained
+ * to track and control the use of the data transfer buffers.
+ *
+ * To support this strategy we will allocate sufficient PAGE_SIZE
+ * contiguous memory buffers to allow for the total required buffer
+ * space.
+ *
+ * The 16C32 accesses the list of buffer entries using Bus Master
+ * DMA. Control information is read from the buffer entries by the
+ * 16C32 to control data transfers. status information is written to
+ * the buffer entries by the 16C32 to indicate the status of completed
+ * transfers.
+ *
+ * The CPU writes control information to the buffer entries to control
+ * the 16C32 and reads status information from the buffer entries to
+ * determine information about received and transmitted frames.
+ *
+ * Because the CPU and 16C32 (adapter) both need simultaneous access
+ * to the buffer entries, the buffer entry memory is allocated with
+ * HalAllocateCommonBuffer(). This restricts the size of the buffer
+ * entry list to PAGE_SIZE.
+ *
+ * The actual data buffers on the other hand will only be accessed
+ * by the CPU or the adapter but not by both simultaneously. This allows
+ * Scatter/Gather packet based DMA procedures for using physically
+ * discontiguous pages.
+ */
+
+/*
+ * mgsl_reset_rx_dma_buffers()
+ * 
+ * 	Set the count for all receive buffers to DMABUFFERSIZE
+ * 	and set the current buffer to the first buffer. This effectively
+ * 	makes all buffers free and discards any data in buffers.
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info )
+{
+	unsigned int i;
+
+	for ( i = 0; i < info->rx_buffer_count; i++ ) {
+		*((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE;
+//		info->rx_buffer_list[i].count = DMABUFFERSIZE;
+//		info->rx_buffer_list[i].status = 0;
+	}
+
+	info->current_rx_buffer = 0;
+
+}	/* end of mgsl_reset_rx_dma_buffers() */
+
+/*
+ * mgsl_free_rx_frame_buffers()
+ * 
+ * 	Free the receive buffers used by a received SDLC
+ * 	frame such that the buffers can be reused.
+ * 
+ * Arguments:
+ * 
+ * 	info			pointer to device instance data
+ * 	StartIndex		index of 1st receive buffer of frame
+ * 	EndIndex		index of last receive buffer of frame
+ * 
+ * Return Value:	None
+ */
+void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex )
+{
+	int Done = 0;
+	DMABUFFERENTRY *pBufEntry;
+	unsigned int Index;
+
+	/* Starting with 1st buffer entry of the frame clear the status */
+	/* field and set the count field to DMA Buffer Size. */
+
+	Index = StartIndex;
+
+	while( !Done ) {
+		pBufEntry = &(info->rx_buffer_list[Index]);
+
+		if ( Index == EndIndex ) {
+			/* This is the last buffer of the frame! */
+			Done = 1;
+		}
+
+		/* reset current buffer for reuse */
+//		pBufEntry->status = 0;
+//		pBufEntry->count = DMABUFFERSIZE;
+		*((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE;
+
+		/* advance to next buffer entry in linked list */
+		Index++;
+		if ( Index == info->rx_buffer_count )
+			Index = 0;
+	}
+
+	/* set current buffer to next buffer after last buffer of frame */
+	info->current_rx_buffer = Index;
+
+}	/* end of free_rx_frame_buffers() */
+
+/* mgsl_get_rx_frame()
+ * 
+ * 	This function attempts to return a received SDLC frame from the
+ * 	receive DMA buffers. Only frames received without errors are returned.
+ *
+ * Arguments:	 	info	pointer to device extension
+ * Return Value:	1 if frame returned, otherwise 0
+ */
+int mgsl_get_rx_frame(struct mgsl_struct *info)
+{
+	unsigned int StartIndex, EndIndex;	/* index of 1st and last buffers of Rx frame */
+	unsigned short status;
+	DMABUFFERENTRY *pBufEntry;
+	unsigned int framesize;
+	int ReturnCode = 0;
+	unsigned long flags;
+	struct tty_struct *tty = info->tty;
+	
+	/*
+	 * current_rx_buffer points to the 1st buffer of the next available
+	 * receive frame. To find the last buffer of the frame look for
+	 * a non-zero status field in the buffer entries. (The status
+	 * field is set by the 16C32 after completing a receive frame.
+	 */
+
+	StartIndex = EndIndex = info->current_rx_buffer;
+
+	while( !info->rx_buffer_list[EndIndex].status ) {
+		/*
+		 * If the count field of the buffer entry is non-zero then
+		 * this buffer has not been used. (The 16C32 clears the count
+		 * field when it starts using the buffer.) If an unused buffer
+		 * is encountered then there are no frames available.
+		 */
+
+		if ( info->rx_buffer_list[EndIndex].count )
+			goto Cleanup;
+
+		/* advance to next buffer entry in linked list */
+		EndIndex++;
+		if ( EndIndex == info->rx_buffer_count )
+			EndIndex = 0;
+
+		/* if entire list searched then no frame available */
+		if ( EndIndex == StartIndex ) {
+			/* If this occurs then something bad happened,
+			 * all buffers have been 'used' but none mark
+			 * the end of a frame. Reset buffers and receiver.
+			 */
+
+			if ( info->rx_enabled ){
+				spin_lock_irqsave(&info->irq_spinlock,flags);
+				usc_start_receiver(info);
+				spin_unlock_irqrestore(&info->irq_spinlock,flags);
+			}
+			goto Cleanup;
+		}
+	}
+
+
+	/* check status of receive frame */
+	
+	status = info->rx_buffer_list[EndIndex].status;
+
+	if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
+			RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
+		if ( status & RXSTATUS_SHORT_FRAME )
+			info->icount.rxshort++;
+		else if ( status & RXSTATUS_ABORT )
+			info->icount.rxabort++;
+		else if ( status & RXSTATUS_OVERRUN )
+			info->icount.rxover++;
+		else 
+			info->icount.rxcrc++;
+		framesize = 0;
+	} else {
+		/* receive frame has no errors, get frame size.
+		 * The frame size is the starting value of the RCC (which was
+		 * set to 0xffff) minus the ending value of the RCC (decremented
+		 * once for each receive character) minus 2 for the 16-bit CRC.
+		 */
+
+		framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc;
+
+		/* adjust frame size for CRC if any */
+		if ( info->params.crc_type == HDLC_CRC_16_CCITT )
+			framesize -= 2;
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n",
+			__FILE__,__LINE__,info->device_name,status,framesize);
+			
+	if ( debug_level >= DEBUG_LEVEL_DATA )
+		mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr,
+			framesize,0);	
+		
+	if (framesize) {
+		if (framesize > HDLC_MAX_FRAME_SIZE)
+			info->icount.rxlong++;
+		else {
+			info->icount.rxok++;
+			pBufEntry = &(info->rx_buffer_list[StartIndex]);
+			/* Call the line discipline receive callback directly. */
+			tty->ldisc.receive_buf(tty, pBufEntry->virt_addr, info->flag_buf, framesize);
+		}
+	}
+	/* Free the buffers used by this frame. */
+	mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex );
+
+	ReturnCode = 1;
+
+Cleanup:
+
+	if ( info->rx_enabled && info->rx_overflow ) {
+		/* The receiver needs to restarted because of 
+		 * a receive overflow (buffer or FIFO). If the 
+		 * receive buffers are now empty, then restart receiver.
+		 */
+
+		if ( !info->rx_buffer_list[info->current_rx_buffer].status &&
+			info->rx_buffer_list[info->current_rx_buffer].count ) {
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			usc_start_receiver(info);
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		}
+	}
+
+	return ReturnCode;
+
+}	/* end of mgsl_get_rx_frame() */
+
+/* mgsl_load_tx_dma_buffer()
+ * 
+ * 	Load the transmit DMA buffer with the specified data.
+ * 
+ * Arguments:
+ * 
+ * 	info		pointer to device extension
+ * 	Buffer		pointer to buffer containing frame to load
+ * 	BufferSize	size in bytes of frame in Buffer
+ * 
+ * Return Value: 	None
+ */
+void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, const char *Buffer,
+	 unsigned int BufferSize)
+{
+	unsigned short Copycount;
+	unsigned int i = 0;
+	DMABUFFERENTRY *pBufEntry;
+	
+	if ( debug_level >= DEBUG_LEVEL_DATA )
+		mgsl_trace_block(info,Buffer,BufferSize,1);	
+
+	/* Setup the status and RCC (Frame Size) fields of the 1st */
+	/* buffer entry in the transmit DMA buffer list. */
+
+	info->tx_buffer_list[0].status = info->cmr_value & 0xf000;
+	info->tx_buffer_list[0].rcc    = BufferSize;
+	info->tx_buffer_list[0].count  = BufferSize;
+
+	/* Copy frame data from 1st source buffer to the DMA buffers. */
+	/* The frame data may span multiple DMA buffers. */
+
+	while( BufferSize ){
+		/* Get a pointer to next DMA buffer entry. */
+		pBufEntry = &info->tx_buffer_list[i++];
+			
+		/* Calculate the number of bytes that can be copied from */
+		/* the source buffer to this DMA buffer. */
+		if ( BufferSize > DMABUFFERSIZE )
+			Copycount = DMABUFFERSIZE;
+		else
+			Copycount = BufferSize;
+
+		/* Actually copy data from source buffer to DMA buffer. */
+		/* Also set the data count for this individual DMA buffer. */
+		if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+			mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount);
+		else
+			memcpy(pBufEntry->virt_addr, Buffer, Copycount);
+
+		pBufEntry->count = Copycount;
+
+		/* Advance source pointer and reduce remaining data count. */
+		Buffer += Copycount;
+		BufferSize -= Copycount;
+	}
+
+}	/* end of mgsl_load_tx_dma_buffer() */
+
+/*
+ * mgsl_register_test()
+ * 
+ * 	Performs a register test of the 16C32.
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:		TRUE if test passed, otherwise FALSE
+ */
+BOOLEAN mgsl_register_test( struct mgsl_struct *info )
+{
+	static unsigned short BitPatterns[] =
+		{ 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f };
+	static unsigned int Patterncount = sizeof(BitPatterns)/sizeof(unsigned short);
+	unsigned int i;
+	BOOLEAN rc = TRUE;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	usc_reset(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	/* Verify the reset state of some registers. */
+
+	if ( (usc_InReg( info, SICR ) != 0) ||
+		  (usc_InReg( info, IVR  ) != 0) ||
+		  (usc_InDmaReg( info, DIVR ) != 0) ){
+		rc = FALSE;
+	}
+
+	if ( rc == TRUE ){
+		/* Write bit patterns to various registers but do it out of */
+		/* sync, then read back and verify values. */
+
+		for ( i = 0 ; i < Patterncount ; i++ ) {
+			usc_OutReg( info, TC0R, BitPatterns[i] );
+			usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] );
+			usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] );
+			usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] );
+			usc_OutReg( info, RSR,  BitPatterns[(i+4)%Patterncount] );
+			usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] );
+
+			if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) ||
+				  (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) ||
+				  (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) ||
+				  (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) ||
+				  (usc_InReg( info, RSR )  != BitPatterns[(i+4)%Patterncount]) ||
+				  (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){
+				rc = FALSE;
+				break;
+			}
+		}
+	}
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	usc_reset(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	return rc;
+
+}	/* end of mgsl_register_test() */
+
+/* mgsl_irq_test() 	Perform interrupt test of the 16C32.
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	TRUE if test passed, otherwise FALSE
+ */
+BOOLEAN mgsl_irq_test( struct mgsl_struct *info )
+{
+	unsigned long EndTime;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	usc_reset(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+	/*
+	 * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. 
+	 * The ISR sets irq_occurred to 1. 
+	 */
+
+	info->irq_occurred = FALSE;
+
+	/* Enable INTEN gate for ISA adapter (Port 6, Bit12) */
+	/* Enable INTEN (Port 6, Bit12) */
+	/* This connects the IRQ request signal to the ISA bus */
+	/* on the ISA adapter. This has no effect for the PCI adapter */
+	usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) );
+
+	usc_EnableMasterIrqBit(info);
+	usc_EnableInterrupts(info, IO_PIN);
+	usc_ClearIrqPendingBits(info, IO_PIN);
+	
+	usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED);
+	usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE);
+
+	EndTime=100;
+	while( EndTime-- && !info->irq_occurred ) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(jiffies_from_ms(10));
+		current->state = TASK_RUNNING;
+	}
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	usc_reset(info);
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	if ( !info->irq_occurred )
+		return FALSE;
+	else
+		return TRUE;
+
+}	/* end of mgsl_irq_test() */
+
+/* mgsl_dma_test()
+ * 
+ * 	Perform a DMA test of the 16C32. A small frame is
+ * 	transmitted via DMA from a transmit buffer to a receive buffer
+ * 	using single buffer DMA mode.
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	TRUE if test passed, otherwise FALSE
+ */
+BOOLEAN mgsl_dma_test( struct mgsl_struct *info )
+{
+	unsigned short FifoLevel;
+	unsigned long phys_addr;
+	unsigned int FrameSize;
+	unsigned int i;
+	char *TmpPtr;
+	BOOLEAN rc = TRUE;
+	volatile unsigned short status;
+	volatile unsigned long EndTime;
+	unsigned long flags;
+	MGSL_PARAMS tmp_params;
+	
+	/* save current port options */
+	memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS));
+	/* load default port options */
+	memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+	
+#define TESTFRAMESIZE 40
+
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	
+	/* setup 16C32 for SDLC DMA transfer mode */
+
+	usc_reset(info);
+	usc_set_sdlc_mode(info);
+	usc_enable_loopback(info,1);
+	
+	/* Reprogram the RDMR so that the 16C32 does NOT clear the count
+	 * field of the buffer entry after fetching buffer address. This
+	 * way we can detect a DMA failure for a DMA read (which should be
+	 * non-destructive to system memory) before we try and write to
+	 * memory (where a failure could corrupt system memory).
+	 */
+
+	/* Receive DMA mode Register (RDMR)
+	 * 
+	 * <15..14>	11	DMA mode = Linked List Buffer mode
+	 * <13>		1	RSBinA/L = store Rx status Block in List entry
+	 * <12>		0	1 = Clear count of List Entry after fetching
+	 * <11..10>	00	Address mode = Increment
+	 * <9>		1	Terminate Buffer on RxBound
+	 * <8>		0	Bus Width = 16bits
+	 * <7..0>		?	status Bits (write as 0s)
+	 * 
+	 * 1110 0010 0000 0000 = 0xe200
+	 */
+
+	usc_OutDmaReg( info, RDMR, 0xe200 );
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+
+	/* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */
+
+	FrameSize = TESTFRAMESIZE;
+
+	/* setup 1st transmit buffer entry: */
+	/* with frame size and transmit control word */
+
+	info->tx_buffer_list[0].count  = FrameSize;
+	info->tx_buffer_list[0].rcc    = FrameSize;
+	info->tx_buffer_list[0].status = 0x4000;
+
+	/* build a transmit frame in 1st transmit DMA buffer */
+
+	TmpPtr = info->tx_buffer_list[0].virt_addr;
+	for (i = 0; i < FrameSize; i++ )
+		*TmpPtr++ = i;
+
+	/* setup 1st receive buffer entry: */
+	/* clear status, set max receive buffer size */
+
+	info->rx_buffer_list[0].status = 0;
+	info->rx_buffer_list[0].count = FrameSize + 4;
+
+	/* zero out the 1st receive buffer */
+
+	memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 );
+
+	/* Set count field of next buffer entries to prevent */
+	/* 16C32 from using buffers after the 1st one. */
+
+	info->tx_buffer_list[1].count = 0;
+	info->rx_buffer_list[1].count = 0;
+	
+
+	/***************************/
+	/* Program 16C32 receiver. */
+	/***************************/
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+
+	/* setup DMA transfers */
+	usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+	/* program 16C32 receiver with physical address of 1st DMA buffer entry */
+	phys_addr = info->rx_buffer_list[0].phys_entry;
+	usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr );
+	usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) );
+
+	/* Clear the Rx DMA status bits (read RDMR) and start channel */
+	usc_InDmaReg( info, RDMR );
+	usc_DmaCmd( info, DmaCmd_InitRxChannel );
+
+	/* Enable Receiver (RMR <1..0> = 10) */
+	usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) );
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+
+	/*************************************************************/
+	/* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */
+	/*************************************************************/
+
+	/* Wait 100ms for interrupt. */
+	EndTime = jiffies + jiffies_from_ms(100);
+
+	for(;;) {
+		if ( jiffies > EndTime ) {
+			rc = FALSE;
+			break;
+		}
+
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		status = usc_InDmaReg( info, RDMR );
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+		if ( !(status & BIT4) && (status & BIT5) ) {
+			/* INITG (BIT 4) is inactive (no entry read in progress) AND */
+			/* BUSY  (BIT 5) is active (channel still active). */
+			/* This means the buffer entry read has completed. */
+			break;
+		}
+	}
+
+
+	/******************************/
+	/* Program 16C32 transmitter. */
+	/******************************/
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+
+	/* Program the Transmit Character Length Register (TCLR) */
+	/* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
+
+	usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count );
+	usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+	/* Program the address of the 1st DMA Buffer Entry in linked list */
+
+	phys_addr = info->tx_buffer_list[0].phys_entry;
+	usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr );
+	usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) );
+
+	/* unlatch Tx status bits, and start transmit channel. */
+
+	usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0700) | 0xfa) );
+	usc_DmaCmd( info, DmaCmd_InitTxChannel );
+
+	/* wait for DMA controller to fill transmit FIFO */
+
+	usc_TCmd( info, TCmd_SelectTicrTxFifostatus );
+	
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+
+	/**********************************/
+	/* WAIT FOR TRANSMIT FIFO TO FILL */
+	/**********************************/
+															 
+	/* Wait 100ms */
+	EndTime = jiffies + jiffies_from_ms(100);
+
+	for(;;) {
+		if ( jiffies > EndTime ) {
+			rc = FALSE;
+			break;
+		}
+
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		FifoLevel = usc_InReg(info, TICR) >> 8;
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+			
+		if ( FifoLevel < 16 )
+			break;
+		else
+			if ( FrameSize < 32 ) {
+				/* This frame is smaller than the entire transmit FIFO */
+				/* so wait for the entire frame to be loaded. */
+				if ( FifoLevel <= (32 - FrameSize) )
+					break;
+			}
+	}
+
+
+	if ( rc == TRUE )
+	{
+		/* Enable 16C32 transmitter. */
+
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		
+		/* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */
+		usc_TCmd( info, TCmd_SendFrame );
+		usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) );
+		
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+						
+		/******************************/
+		/* WAIT FOR TRANSMIT COMPLETE */
+		/******************************/
+
+		/* Wait 100ms */
+		EndTime = jiffies + jiffies_from_ms(100);
+
+		/* While timer not expired wait for transmit complete */
+
+		spin_lock_irqsave(&info->irq_spinlock,flags);
+		status = usc_InReg( info, TCSR );
+		spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+		while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) {
+			if ( jiffies > EndTime ) {
+				rc = FALSE;
+				break;
+			}
+
+			spin_lock_irqsave(&info->irq_spinlock,flags);
+			status = usc_InReg( info, TCSR );
+			spin_unlock_irqrestore(&info->irq_spinlock,flags);
+		}
+	}
+
+
+	if ( rc == TRUE ){
+		/* CHECK FOR TRANSMIT ERRORS */
+		if ( status & (BIT5 + BIT1) )
+			rc = FALSE;
+	}
+
+	if ( rc == TRUE ) {
+		/* WAIT FOR RECEIVE COMPLETE */
+
+		/* Wait 100ms */
+		EndTime = jiffies + jiffies_from_ms(100);
+
+		/* Wait for 16C32 to write receive status to buffer entry. */
+		status=info->rx_buffer_list[0].status;
+		while ( status == 0 ) {
+			if ( jiffies > EndTime ) {
+			printk(KERN_ERR"mark 4\n");
+				rc = FALSE;
+				break;
+			}
+			status=info->rx_buffer_list[0].status;
+		}
+	}
+
+
+	if ( rc == TRUE ) {
+		/* CHECK FOR RECEIVE ERRORS */
+		status = info->rx_buffer_list[0].status;
+
+		if ( status & (BIT8 + BIT3 + BIT1) ) {
+			/* receive error has occured */
+			rc = FALSE;
+		} else {
+			if ( memcmp( info->tx_buffer_list[0].virt_addr ,
+				info->rx_buffer_list[0].virt_addr, FrameSize ) ){
+				rc = FALSE;
+			}
+		}
+	}
+
+	usc_reset( info );
+
+	/* restore current port options */
+	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+	
+	return rc;
+
+}	/* end of mgsl_dma_test() */
+
+/* mgsl_adapter_test()
+ * 
+ * 	Perform the register, IRQ, and DMA tests for the 16C32.
+ * 	
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	0 if success, otherwise -ENODEV
+ */
+int mgsl_adapter_test( struct mgsl_struct *info )
+{
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):Testing device %s\n",
+			__FILE__,__LINE__,info->device_name );
+			
+	if ( !mgsl_register_test( info ) ) {
+		info->init_error = DiagStatus_AddressFailure;
+		printk( "%s(%d):Register test failure for device %s Addr=%04X\n",
+			__FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) );
+		return -ENODEV;
+	}
+
+	if ( !mgsl_irq_test( info ) ) {
+		info->init_error = DiagStatus_IrqFailure;
+		printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
+			__FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
+		return -ENODEV;
+	}
+
+	if ( !mgsl_dma_test( info ) ) {
+		info->init_error = DiagStatus_DmaFailure;
+		printk( "%s(%d):DMA test failure for device %s DMA=%d\n",
+			__FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) );
+		return -ENODEV;
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):device %s passed diagnostics\n",
+			__FILE__,__LINE__,info->device_name );
+			
+	return 0;
+
+}	/* end of mgsl_adapter_test() */
+
+/* mgsl_memory_test()
+ * 
+ * 	Test the shared memory on a PCI adapter.
+ * 
+ * Arguments:		info	pointer to device instance data
+ * Return Value:	TRUE if test passed, otherwise FALSE
+ */
+BOOLEAN mgsl_memory_test( struct mgsl_struct *info )
+{
+	static unsigned long BitPatterns[] = { 0x0, 0x55555555, 0xaaaaaaaa,
+											0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
+	unsigned long Patterncount = sizeof(BitPatterns)/sizeof(unsigned long);
+	unsigned long i;
+	unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long);
+	unsigned long * TestAddr;
+
+	if ( info->bus_type != MGSL_BUS_TYPE_PCI )
+		return TRUE;
+
+	TestAddr = (unsigned long *)info->memory_base;
+
+	/* Test data lines with test pattern at one location. */
+
+	for ( i = 0 ; i < Patterncount ; i++ ) {
+		*TestAddr = BitPatterns[i];
+		if ( *TestAddr != BitPatterns[i] )
+			return FALSE;
+	}
+
+	/* Test address lines with incrementing pattern over */
+	/* entire address range. */
+
+	for ( i = 0 ; i < TestLimit ; i++ ) {
+		*TestAddr = i * 4;
+		TestAddr++;
+	}
+
+	TestAddr = (unsigned long *)info->memory_base;
+
+	for ( i = 0 ; i < TestLimit ; i++ ) {
+		if ( *TestAddr != i * 4 )
+			return FALSE;
+		TestAddr++;
+	}
+
+	memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE );
+
+	return TRUE;
+
+}	/* End Of mgsl_memory_test() */
+
+
+#pragma optimize( "", off )
+/* mgsl_load_pci_memory()
+ * 
+ * 	Load a large block of data into the PCI shared memory.
+ * 	Use this instead of memcpy() or memmove() to move data
+ * 	into the PCI shared memory.
+ * 
+ * Notes:
+ * 
+ * 	This function prevents the PCI9050 interface chip from hogging
+ * 	the adapter local bus, which can starve the 16C32 by preventing
+ * 	16C32 bus master cycles.
+ * 
+ * 	The PCI9050 documentation says that the 9050 will always release
+ * 	control of the local bus after completing the current read
+ * 	or write operation.
+ * 
+ * 	It appears that as long as the PCI9050 write FIFO is full, the
+ * 	PCI9050 treats all of the writes as a single burst transaction
+ * 	and will not release the bus. This causes DMA latency problems
+ * 	at high speeds when copying large data blocks to the shared
+ * 	memory.
+ * 
+ * 	This function in effect, breaks the a large shared memory write
+ * 	into multiple transations by interleaving a shared memory read
+ * 	which will flush the write FIFO and 'complete' the write
+ * 	transation. This allows any pending DMA request to gain control
+ * 	of the local bus in a timely fasion.
+ * 
+ * Arguments:
+ * 
+ * 	TargetPtr	pointer to target address in PCI shared memory
+ * 	SourcePtr	pointer to source buffer for data
+ * 	count		count in bytes of data to copy
+ *
+ * Return Value:	None
+ */
+void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, 
+	unsigned short count )
+{
+	/*******************************************************/
+	/* A load interval of 16 allows for 4 32-bit writes at */
+	/* 60ns each for a maximum latency of 240ns on the		 */
+	/* local bus.														 */
+	/*******************************************************/
+
+#define PCI_LOAD_INTERVAL 64
+
+	unsigned short Intervalcount = count / PCI_LOAD_INTERVAL;
+	unsigned short Index;
+	unsigned long Dummy;
+
+	for ( Index = 0 ; Index < Intervalcount ; Index++ )
+	{
+		memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL);
+		Dummy = *((unsigned long *)TargetPtr);
+		TargetPtr += PCI_LOAD_INTERVAL;
+		SourcePtr += PCI_LOAD_INTERVAL;
+	}
+
+	memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL );
+
+}	/* End Of mgsl_load_pci_memory() */
+#pragma optimize( "", on )
+
+void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit)
+{
+	int i;
+	int linecount;
+	if (xmit)
+		printk("%s tx data:\n",info->device_name);
+	else
+		printk("%s rx data:\n",info->device_name);
+		
+	while(count) {
+		if (count > 16)
+			linecount = 16;
+		else
+			linecount = count;
+			
+		for(i=0;i<linecount;i++)
+			printk("%02X ",(unsigned char)data[i]);
+		for(;i<17;i++)
+			printk("   ");
+		for(i=0;i<linecount;i++) {
+			if (data[i]>=040 && data[i]<=0176)
+				printk("%c",data[i]);
+			else
+				printk(".");
+		}
+		printk("\n");
+		
+		data  += linecount;
+		count -= linecount;
+	}
+}	/* end of mgsl_trace_block() */
+
+/* mgsl_tx_timeout()
+ * 
+ * 	called when HDLC frame times out
+ * 	update stats and do tx completion processing
+ * 	
+ * Arguments:	context		pointer to device instance data
+ * Return Value:	None
+ */
+void mgsl_tx_timeout(unsigned long context)
+{
+	struct mgsl_struct *info = (struct mgsl_struct*)context;
+	unsigned long flags;
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):mgsl_tx_timeout(%s)\n",
+			__FILE__,__LINE__,info->device_name);
+	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
+		info->icount.txtimeout++;
+	}
+	
+	spin_lock_irqsave(&info->irq_spinlock,flags);
+	info->tx_active = 0;
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+	
+	mgsl_bh_transmit_data(info,0);
+	
+}	/* end of mgsl_tx_timeout() */
+
diff -u --recursive --new-file v2.2.4/linux/drivers/fc4/soc.c linux/drivers/fc4/soc.c
--- v2.2.4/linux/drivers/fc4/soc.c	Tue Mar 23 14:35:47 1999
+++ linux/drivers/fc4/soc.c	Fri Mar 26 13:57:41 1999
@@ -41,7 +41,6 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/auxio.h>
-#include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 
diff -u --recursive --new-file v2.2.4/linux/drivers/fc4/socal.c linux/drivers/fc4/socal.c
--- v2.2.4/linux/drivers/fc4/socal.c	Tue Mar 23 14:35:47 1999
+++ linux/drivers/fc4/socal.c	Sun Mar 28 09:07:47 1999
@@ -73,6 +73,7 @@
 	}
 }
 
+#ifdef HAVE_SOCAL_UCODE
 static void socal_bzero(void *d, int size)
 {
 	u32 *dp = (u32 *)d;
@@ -81,6 +82,7 @@
 		size -= sizeof(u32);
 	}
 }
+#endif
 
 static inline void socal_disable(struct socal *s)
 {
diff -u --recursive --new-file v2.2.4/linux/drivers/net/hamradio/baycom_epp.c linux/drivers/net/hamradio/baycom_epp.c
--- v2.2.4/linux/drivers/net/hamradio/baycom_epp.c	Tue Feb 23 15:21:33 1999
+++ linux/drivers/net/hamradio/baycom_epp.c	Fri Mar 26 13:57:41 1999
@@ -48,7 +48,6 @@
 #include <linux/string.h>
 #include <linux/parport.h>
 #include <linux/bitops.h>
-#include <linux/sched.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/processor.h>
diff -u --recursive --new-file v2.2.4/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.2.4/linux/drivers/net/myri_sbus.c	Tue Mar 23 14:35:47 1999
+++ linux/drivers/net/myri_sbus.c	Fri Mar 26 13:57:41 1999
@@ -34,7 +34,6 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/auxio.h>
-#include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 
diff -u --recursive --new-file v2.2.4/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
--- v2.2.4/linux/drivers/sbus/char/sab82532.c	Tue Mar 23 14:35:48 1999
+++ linux/drivers/sbus/char/sab82532.c	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.28 1999/01/02 16:47:35 davem Exp $
+/* $Id: sab82532.c,v 1.30 1999/03/24 11:34:52 davem Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -2136,7 +2136,7 @@
 
 __initfunc(static inline void show_serial_version(void))
 {
-	char *revision = "$Revision: 1.28 $";
+	char *revision = "$Revision: 1.30 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2359,8 +2359,10 @@
 	restore_flags(flags);
 
 	for (i = 0; i < NR_PORTS; i++) {
-		if (sab82532_table[i].type != PORT_UNKNOWN)
-			release_region(sab82532_table[i].port, 8);
+		struct sab82532 *info = (struct sab82532 *)sab82532_table[i]->driver_data;
+		if (info->type != PORT_UNKNOWN)
+			release_region((unsigned long)info->regs,
+				       sizeof(union sab82532_async_regs));
 	}
 	if (tmp_buf) {
 		free_page((unsigned long) tmp_buf);
diff -u --recursive --new-file v2.2.4/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.2.4/linux/drivers/scsi/ppa.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/ppa.c	Fri Mar 26 13:57:41 1999
@@ -48,7 +48,6 @@
 }
 
 #include  "ppa.h"
-#include <linux/parport.h>
 
 #define NO_HOSTS 4
 static ppa_struct ppa_hosts[NO_HOSTS] =
diff -u --recursive --new-file v2.2.4/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.2.4/linux/drivers/sound/es1370.c	Tue Mar 23 14:35:48 1999
+++ linux/drivers/sound/es1370.c	Fri Mar 26 14:52:34 1999
@@ -83,6 +83,10 @@
  *    16.12.98   0.16  Don't wake up app until there are fragsize bytes to read/write
  *    06.01.99   0.17  remove the silly SA_INTERRUPT flag.
  *                     hopefully killed the egcs section type conflict
+ *    12.03.99   0.18  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                     reported by Johan Maes <joma@telindus.be>
+ *    22.03.99   0.19  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                     read/write cannot be executed
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -1097,7 +1101,7 @@
 		if (cnt <= 0) {
 			start_adc(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_adc.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1152,7 +1156,7 @@
 		if (cnt <= 0) {
 			start_dac2(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_dac2.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1455,7 +1459,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -1468,7 +1472,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
                 cinfo.bytes = s->dma_dac2.total_bytes;
-                cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift;
+                cinfo.blocks = s->dma_dac2.count >> s->dma_dac2.fragshift;
                 cinfo.ptr = s->dma_dac2.hwptr;
 		if (s->dma_dac2.mapped)
 			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
@@ -1664,7 +1668,7 @@
 		if (cnt <= 0) {
 			start_dac1(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_dac1.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1866,7 +1870,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		es1370_update_ptr(s);
                 cinfo.bytes = s->dma_dac1.total_bytes;
-                cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift;
+                cinfo.blocks = s->dma_dac1.count >> s->dma_dac1.fragshift;
                 cinfo.ptr = s->dma_dac1.hwptr;
 		if (s->dma_dac1.mapped)
 			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
@@ -2021,7 +2025,7 @@
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->midi.iwait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -2068,7 +2072,7 @@
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->midi.owait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -2271,7 +2275,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.17 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.19 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --recursive --new-file v2.2.4/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- v2.2.4/linux/drivers/sound/es1371.c	Wed Mar 10 15:29:47 1999
+++ linux/drivers/sound/es1371.c	Fri Mar 26 14:52:34 1999
@@ -54,6 +54,10 @@
  *                     Don't wake up app until there are fragsize bytes to read/write
  *    06.01.99   0.8   remove the silly SA_INTERRUPT flag.
  *                     hopefully killed the egcs section type conflict
+ *    12.03.99   0.9   cinfo.blocks should be reset after GETxPTR ioctl.
+ *                     reported by Johan Maes <joma@telindus.be>
+ *    22.03.99   0.10  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                     read/write cannot be executed
  *
  */
 
@@ -1542,7 +1546,7 @@
 		if (cnt <= 0) {
 			start_adc(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_adc.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1597,7 +1601,7 @@
 		if (cnt <= 0) {
 			start_dac2(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_dac2.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1897,7 +1901,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -1910,7 +1914,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
                 cinfo.bytes = s->dma_dac2.total_bytes;
-                cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift;
+                cinfo.blocks = s->dma_dac2.count >> s->dma_dac2.fragshift;
                 cinfo.ptr = s->dma_dac2.hwptr;
 		if (s->dma_dac2.mapped)
 			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
@@ -2108,7 +2112,7 @@
 		if (cnt <= 0) {
 			start_dac1(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_dac1.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -2301,7 +2305,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		es1371_update_ptr(s);
                 cinfo.bytes = s->dma_dac1.total_bytes;
-                cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift;
+                cinfo.blocks = s->dma_dac1.count >> s->dma_dac1.fragshift;
                 cinfo.ptr = s->dma_dac1.hwptr;
 		if (s->dma_dac1.mapped)
 			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
@@ -2455,7 +2459,7 @@
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->midi.iwait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -2502,7 +2506,7 @@
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->midi.owait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -2708,7 +2712,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1371: version v0.8 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1371: version v0.10 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --recursive --new-file v2.2.4/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- v2.2.4/linux/drivers/sound/sonicvibes.c	Wed Mar 10 15:29:47 1999
+++ linux/drivers/sound/sonicvibes.c	Fri Mar 26 14:52:34 1999
@@ -51,6 +51,10 @@
  *    16.12.98   0.9   Fix a few f_file & FMODE_ bugs
  *    06.01.99   0.10  remove the silly SA_INTERRUPT flag.
  *                     hopefully killed the egcs section type conflict
+ *    12.03.99   0.11  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                     reported by Johan Maes <joma@telindus.be>
+ *    22.03.99   0.12  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                     read/write cannot be executed
  *
  */
 
@@ -1293,7 +1297,7 @@
 		if (cnt <= 0) {
 			start_adc(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_adc.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1353,7 +1357,7 @@
 		if (cnt <= 0) {
 			start_dac(s);
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->dma_dac.wait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1646,7 +1650,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
                 cinfo.bytes = s->dma_adc.total_bytes;
-                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
                 cinfo.ptr = s->dma_adc.hwptr;
 		if (s->dma_adc.mapped)
 			s->dma_adc.count &= s->dma_adc.fragsize-1;
@@ -1659,7 +1663,7 @@
 		spin_lock_irqsave(&s->lock, flags);
 		sv_update_ptr(s);
                 cinfo.bytes = s->dma_dac.total_bytes;
-                cinfo.blocks = s->dma_dac.total_bytes >> s->dma_dac.fragshift;
+                cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
                 cinfo.ptr = s->dma_dac.hwptr;
 		if (s->dma_dac.mapped)
 			s->dma_dac.count &= s->dma_dac.fragsize-1;
@@ -1839,7 +1843,7 @@
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->midi.iwait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -1886,7 +1890,7 @@
 			cnt = count;
 		if (cnt <= 0) {
 			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EBUSY;
+				return ret ? ret : -EAGAIN;
 			interruptible_sleep_on(&s->midi.owait);
 			if (signal_pending(current))
 				return ret ? ret : -ERESTARTSYS;
@@ -2271,7 +2275,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.10 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.12 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
diff -u --recursive --new-file v2.2.4/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.2.4/linux/drivers/video/fbcon.c	Wed Mar 10 15:29:48 1999
+++ linux/drivers/video/fbcon.c	Sun Mar 28 09:07:47 1999
@@ -1587,7 +1587,7 @@
         if (CON_IS_VISIBLE(conp) && softback_buf) {
 	    int l = fbcon_softback_size / conp->vc_size_row;
 	    if (l > 5)
-		softback_end += l * conp->vc_size_row;
+		softback_end = softback_buf + l * conp->vc_size_row;
 	    else {
 		/* Smaller scrollback makes no sense, and 0 would screw
 		   the operation totally */
diff -u --recursive --new-file v2.2.4/linux/fs/adfs/inode.c linux/fs/adfs/inode.c
--- v2.2.4/linux/fs/adfs/inode.c	Fri Jan 30 11:28:08 1998
+++ linux/fs/adfs/inode.c	Fri Mar 26 13:57:41 1999
@@ -27,7 +27,7 @@
 #define inode_dirindex(idx)	(((idx) & 0xff) * 26 - 21)
 
 #define frag_id(x)		(((x) >> 8) & 0x7fff)
-#define off(x)			(((x) & 0xff) ? ((x) & 0xff) - 1 : 0)
+#define off(x)			(((x) & 0xff) ? (((x) & 0xff) - 1) << sb->u.adfs_sb.s_dr->log2sharesize : 0)
 
 static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no)
 {
@@ -83,11 +83,20 @@
 		return 0;
 	}
 
+	if (block < 0) {
+		adfs_error(sb, "adfs_bmap", "block(%d) < 0", block);
+		return 0;
+	}
+
+	if (block > inode->i_blocks)
+		return 0;
+
+	block += off(inode->u.adfs_i.file_id);
+
 	if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG)
-		blk = sb->u.adfs_sb.s_map_block + off(inode_frag (inode->i_ino)) + block;
+		blk = sb->u.adfs_sb.s_map_block + block;
 	else
-		blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id),
-				       off (inode->u.adfs_i.file_id) + block);
+		blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), block);
 	return blk;
 }
 
@@ -105,13 +114,13 @@
 
 	fragment = inode_frag (inode->i_ino);
 	if (frag_id (fragment) == ADFS_ROOT_FRAG)
-		blk = sb->u.adfs_sb.s_map_block + off (fragment) + block;
+		blk = sb->u.adfs_sb.s_map_block + off(fragment) + block;
 	else
-		blk = adfs_map_lookup (sb, frag_id (fragment), off (fragment) + block);
+		blk = adfs_map_lookup (sb, frag_id (fragment), off(fragment) + block);
 	return blk;	
 }
 
-static int adfs_atts2mode (unsigned char mode, unsigned int filetype)
+static int adfs_atts2mode(struct super_block *sb, unsigned char mode, unsigned int filetype)
 {
 	int omode = 0;
 
@@ -120,24 +129,29 @@
 				S_IRGRP|S_IWGRP|S_IXGRP|
 				S_IROTH|S_IWOTH|S_IXOTH;
 	} else {
-		if (mode & ADFS_NDA_DIRECTORY)
-			omode |= S_IFDIR|S_IRUSR|S_IXUSR|S_IXGRP|S_IXOTH;
-		else
+		if (mode & ADFS_NDA_DIRECTORY) {
+			omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask;
+			omode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH;
+		} else
 			omode |= S_IFREG;
+
 		if (mode & ADFS_NDA_OWNER_READ) {
-			omode |= S_IRUSR;
+			omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask;
 			if (filetype == 0xfe6 /* UnixExec */)
-				omode |= S_IXUSR;
+				omode |= S_IXUGO & sb->u.adfs_sb.s_owner_mask;
 		}
+
 		if (mode & ADFS_NDA_OWNER_WRITE)
-			omode |= S_IWUSR;
+			omode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask;
+
 		if (mode & ADFS_NDA_PUBLIC_READ) {
-			omode |= S_IRGRP | S_IROTH;
-			if (filetype == 0xfe6)
-				omode |= S_IXGRP | S_IXOTH;
+			omode |= S_IRUGO & sb->u.adfs_sb.s_other_mask;
+			if (filetype == 0xfe6 /* UnixExec */)
+				omode |= S_IXUGO & sb->u.adfs_sb.s_other_mask;
 		}
+
 		if (mode & ADFS_NDA_PUBLIC_WRITE)
-			omode |= S_IWGRP | S_IWOTH;
+			omode |= S_IWUGO & sb->u.adfs_sb.s_other_mask;
 	}
 	return omode;
 }
@@ -150,8 +164,8 @@
 	int buffers;
 
 	sb = inode->i_sb;
-	inode->i_uid = 0;
-	inode->i_gid = 0;
+	inode->i_uid = sb->u.adfs_sb.s_uid;
+	inode->i_gid = sb->u.adfs_sb.s_gid;
 	inode->i_version = ++event;
 
 	if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
@@ -186,7 +200,7 @@
 			goto bad;
 		}
 		adfs_dir_free (bh, buffers);
-		inode->i_mode	 = adfs_atts2mode (ide.mode, ide.filetype);
+		inode->i_mode	 = adfs_atts2mode(sb, ide.mode, ide.filetype);
 		inode->i_nlink	 = 2;
 		inode->i_size    = ide.size;
 		inode->i_blksize = PAGE_SIZE;
@@ -204,13 +218,5 @@
 	return;
 
 bad:
-	inode->i_mode	 = 0;
-	inode->i_nlink	 = 1;
-	inode->i_size    = 0;
-	inode->i_blksize = 0;
-	inode->i_blocks  = 0;
-	inode->i_mtime   =
-	inode->i_atime   =
-	inode->i_ctime   = 0;
-	inode->i_op	 = NULL;
+	make_bad_inode(inode);
 }
diff -u --recursive --new-file v2.2.4/linux/fs/adfs/super.c linux/fs/adfs/super.c
--- v2.2.4/linux/fs/adfs/super.c	Mon Apr  6 17:41:00 1998
+++ linux/fs/adfs/super.c	Fri Mar 26 13:57:41 1999
@@ -21,25 +21,26 @@
 
 #include <stdarg.h>
 
-static void adfs_put_super (struct super_block *sb);
-static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz);
-void adfs_read_inode (struct inode *inode);
+static void adfs_put_super(struct super_block *sb);
+static int adfs_remount(struct super_block *sb, int *flags, char *data);
+static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+void adfs_read_inode(struct inode *inode);
 
-void adfs_error (struct super_block *sb, const char *function, const char *fmt, ...)
+void adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
 {
 	char error_buf[128];
 	va_list args;
 
-	va_start (args, fmt);
-	vsprintf (error_buf, fmt, args);
-	va_end (args);
+	va_start(args, fmt);
+	vsprintf(error_buf, fmt, args);
+	va_end(args);
 
-	printk (KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
-		kdevname (sb->s_dev), function ? ": " : "",
+	printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
+		kdevname(sb->s_dev), function ? ": " : "",
 		function ? function : "", error_buf);
 }
 
-unsigned char adfs_calccrosscheck (struct super_block *sb, char *map)
+static unsigned char adfs_calczonecheck(struct super_block *sb, char *map)
 {
 	unsigned int v0, v1, v2, v3;
 	int i;
@@ -63,7 +64,7 @@
 	return v0 ^ v1 ^ v2 ^ v3;
 }
 
-static int adfs_checkmap (struct super_block *sb)
+static int adfs_checkmap(struct super_block *sb)
 {
 	unsigned char crosscheck = 0, zonecheck = 1;
 	int i;
@@ -72,14 +73,14 @@
 		char *map;
 
 		map = sb->u.adfs_sb.s_map[i]->b_data;
-		if (adfs_calccrosscheck (sb, map) != map[0]) {
-			adfs_error (sb, "adfs_checkmap", "zone %d fails zonecheck", i);
+		if (adfs_calczonecheck(sb, map) != map[0]) {
+			adfs_error(sb, "adfs_checkmap", "zone %d fails zonecheck", i);
 			zonecheck = 0;
 		}
 		crosscheck ^= map[3];
 	}
 	if (crosscheck != 0xff)
-		adfs_error (sb, "adfs_checkmap", "crosscheck != 0xff");
+		adfs_error(sb, "adfs_checkmap", "crosscheck != 0xff");
 	return crosscheck == 0xff && zonecheck;
 }
 
@@ -92,21 +93,73 @@
 	adfs_put_super,
 	NULL,
 	adfs_statfs,
-	NULL
+	adfs_remount
 };
 
-static void adfs_put_super (struct super_block *sb)
+static void adfs_put_super(struct super_block *sb)
 {
 	int i;
 
 	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-		brelse (sb->u.adfs_sb.s_map[i]);
-	kfree (sb->u.adfs_sb.s_map);
-	brelse (sb->u.adfs_sb.s_sbh);
+		brelse(sb->u.adfs_sb.s_map[i]);
+	kfree(sb->u.adfs_sb.s_map);
+	brelse(sb->u.adfs_sb.s_sbh);
 	MOD_DEC_USE_COUNT;
 }
 
-struct super_block *adfs_read_super (struct super_block *sb, void *data, int silent)
+static int parse_options(struct super_block *sb, char *options)
+{
+	char *value, *opt;
+
+	if (!options)
+		return 0;
+
+	for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) {
+		value = strchr(opt, '=');
+		if (value)
+			*value++ = '\0';
+
+		if (!strcmp(opt, "uid")) {	/* owner of all files */
+			if (!value || !*value)
+				return -EINVAL;
+			sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0);
+			if (*value)
+				return -EINVAL;
+		} else
+		if (!strcmp(opt, "gid")) {	/* group owner of all files */
+			if (!value || !*value)
+				return -EINVAL;
+			sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0);
+			if (*value)
+				return -EINVAL;
+		} else
+		if (!strcmp(opt, "ownmask")) {	/* owner permission mask */
+			if (!value || !*value)
+				return -EINVAL;
+			sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8);
+			if (*value)
+				return -EINVAL;
+		} else
+		if (!strcmp(opt, "othmask")) {	/* others permission mask */
+			if (!value || !*value)
+				return -EINVAL;
+			sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8);
+			if (*value)
+				return -EINVAL;
+		} else {			/* eh? say again. */
+			printk("ADFS-fs: unrecognised mount option %s\n", opt);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int adfs_remount(struct super_block *sb, int *flags, char *data)
+{
+	return parse_options(sb, data);
+}
+
+struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
 {
 	struct adfs_discrecord *dr;
 	struct buffer_head *bh;
@@ -114,28 +167,30 @@
 	kdev_t dev = sb->s_dev;
 	int i, j;
 
+	/* set default options */
+	sb->u.adfs_sb.s_uid = 0;
+	sb->u.adfs_sb.s_gid = 0;
+	sb->u.adfs_sb.s_owner_mask = S_IRWXU;
+	sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO;
+
+	if (parse_options(sb, data))
+		goto error;
+
 	MOD_INC_USE_COUNT;
-	lock_super (sb);
-	set_blocksize (dev, BLOCK_SIZE);
-	if (!(bh = bread (dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
-		unlock_super (sb);
-		adfs_error (sb, NULL, "unable to read superblock");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+	lock_super(sb);
+	set_blocksize(dev, BLOCK_SIZE);
+	if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
+		adfs_error(sb, NULL, "unable to read superblock");
+		goto error_unlock;
 	}
 
 	b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
 
-	if (adfs_checkbblk (b_data)) {
+	if (adfs_checkbblk(b_data)) {
 		if (!silent)
-			printk ("VFS: Can't find an adfs filesystem on dev "
+			printk("VFS: Can't find an adfs filesystem on dev "
 				"%s.\n", kdevname(dev));
-failed_mount:
-		unlock_super (sb);
-		if (bh)
-			brelse (bh);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto error_free_bh;
 	}
 	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
 
@@ -145,26 +200,26 @@
 	    (sb->s_blocksize == 512 || sb->s_blocksize == 1024 ||
 	     sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) {
 
-		brelse (bh);
-		set_blocksize (dev, sb->s_blocksize);
-		bh = bread (dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
+		brelse(bh);
+		set_blocksize(dev, sb->s_blocksize);
+		bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
 		if (!bh) {
-			adfs_error (sb, NULL, "couldn't read superblock on "
+			adfs_error(sb, NULL, "couldn't read superblock on "
 				"2nd try.");
-			goto failed_mount;
+			goto error_unlock;
 		}
 		b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
-		if (adfs_checkbblk (b_data)) {
-			adfs_error (sb, NULL, "disc record mismatch, very weird!");
-			goto failed_mount;
+		if (adfs_checkbblk(b_data)) {
+			adfs_error(sb, NULL, "disc record mismatch, very weird!");
+			goto error_free_bh;
 		}
 		dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
 	}
 	if (sb->s_blocksize != bh->b_size) {
 		if (!silent)
-			printk (KERN_ERR "VFS: Unsupported blocksize on dev "
-				"%s.\n", kdevname (dev));
-		goto failed_mount;
+			printk(KERN_ERR "VFS: Unsupported blocksize on dev "
+				"%s.\n", kdevname(dev));
+		goto error_free_bh;
 	}
 	/* blocksize on this device should now be set to the adfs log2secsize */
 
@@ -202,71 +257,81 @@
 	else
 		sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk;
 
-	printk (KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n",
+	printk(KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n",
 		sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone,
 		sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size);
-	printk (KERN_DEBUG "ADFS: sector size %d, map bit size %d\n",
-		1 << dr->log2secsize, 1 << dr->log2bpmb);
+	printk(KERN_DEBUG "ADFS: sector size %d, map bit size %d, share size %d\n",
+		1 << dr->log2secsize, 1 << dr->log2bpmb,
+		1 << (dr->log2secsize + dr->log2sharesize));
 
 	sb->s_magic = ADFS_SUPER_MAGIC;
-	sb->s_flags |= MS_RDONLY;	/* we don't support writing yet */
 
-	sb->u.adfs_sb.s_map = kmalloc (sb->u.adfs_sb.s_map_size *
-				sizeof (struct buffer_head *), GFP_KERNEL);
+	sb->u.adfs_sb.s_map = kmalloc(sb->u.adfs_sb.s_map_size *
+				sizeof(struct buffer_head *), GFP_KERNEL);
 	if (sb->u.adfs_sb.s_map == NULL) {
-		adfs_error (sb, NULL, "not enough memory");
-		goto failed_mount;
+		adfs_error(sb, NULL, "not enough memory");
+		goto error_free_bh;
 	}
 
 	for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
-		sb->u.adfs_sb.s_map[i] = bread (dev,
+		sb->u.adfs_sb.s_map[i] = bread(dev,
 						sb->u.adfs_sb.s_map_block + i,
 						sb->s_blocksize);
 		if (!sb->u.adfs_sb.s_map[i]) {
 			for (j = 0; j < i; j++)
-				brelse (sb->u.adfs_sb.s_map[j]);
-			kfree (sb->u.adfs_sb.s_map);
-			adfs_error (sb, NULL, "unable to read map");
-			goto failed_mount;
+				brelse(sb->u.adfs_sb.s_map[j]);
+			kfree(sb->u.adfs_sb.s_map);
+			adfs_error(sb, NULL, "unable to read map");
+			goto error_free_bh;
 		}
 	}
-	if (!adfs_checkmap (sb)) {
+	if (!adfs_checkmap(sb)) {
 		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-			brelse (sb->u.adfs_sb.s_map[i]);
-		adfs_error (sb, NULL, "map corrupted");
-		goto failed_mount;
+			brelse(sb->u.adfs_sb.s_map[i]);
+		adfs_error(sb, NULL, "map corrupted");
+		goto error_free_bh;
 	}
 
 	dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4);
-	unlock_super (sb);
+	unlock_super(sb);
 
 	/*
 	 * set up enough so that it can read an inode
 	 */
 	sb->s_op = &adfs_sops;
-	sb->u.adfs_sb.s_root = adfs_inode_generate (dr->root, 0);
+	sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0);
 	sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL);
 
 	if (!sb->s_root) {
-		sb->s_dev = 0;
 		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
-			brelse (sb->u.adfs_sb.s_map[i]);
-		brelse (bh);
-		adfs_error (sb, NULL, "get root inode failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+			brelse(sb->u.adfs_sb.s_map[i]);
+		brelse(bh);
+		adfs_error(sb, NULL, "get root inode failed\n");
+		goto error_dec_use;
 	}
 	return sb;
+
+error_free_bh:
+	if (bh)
+		brelse(bh);
+error_unlock:
+	unlock_super(sb);
+error_dec_use:
+	MOD_DEC_USE_COUNT;
+error:
+	sb->s_dev = 0;
+	return NULL;
 }
 
-static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
+static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
 	struct statfs tmp;
 	const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1;
 
 	tmp.f_type = ADFS_SUPER_MAGIC;
 	tmp.f_bsize = sb->s_blocksize;
-	tmp.f_blocks = (sb->u.adfs_sb.s_dr->disc_size) >> (sb->s_blocksize_bits);
+	tmp.f_blocks = sb->u.adfs_sb.s_dr->disc_size_high << (32 - sb->s_blocksize_bits) |
+		       sb->u.adfs_sb.s_dr->disc_size >> sb->s_blocksize_bits;
 	tmp.f_files = tmp.f_blocks >> nidlen;
 	{
 		unsigned int i, j = 0;
@@ -305,35 +370,35 @@
 				if (freelink <= nidlen) break;
 			} while (mapindex < 8 * sb->s_blocksize);
 			if (mapindex > 8 * sb->s_blocksize)
-				adfs_error (sb, NULL, "oversized free fragment\n");
+				adfs_error(sb, NULL, "oversized free fragment\n");
 			else if (freelink)
-				adfs_error (sb, NULL, "undersized free fragment\n");
+				adfs_error(sb, NULL, "undersized free fragment\n");
 		}
 		tmp.f_bfree = tmp.f_bavail = j <<
 			(sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits);
 	}
 	tmp.f_ffree = tmp.f_bfree >> nidlen;
 	tmp.f_namelen = ADFS_NAME_LEN;
-	return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0;
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
 static struct file_system_type adfs_fs_type = {
 	"adfs", FS_REQUIRES_DEV, adfs_read_super, NULL
 };
 
-__initfunc(int init_adfs_fs (void))
+__initfunc(int init_adfs_fs(void))
 {
-	return register_filesystem (&adfs_fs_type);
+	return register_filesystem(&adfs_fs_type);
 }
 
 #ifdef MODULE
-int init_module (void)
+int init_module(void)
 {
 	return init_adfs_fs();
 }
 
-void cleanup_module (void)
+void cleanup_module(void)
 {
-	unregister_filesystem (&adfs_fs_type);
+	unregister_filesystem(&adfs_fs_type);
 }
 #endif
diff -u --recursive --new-file v2.2.4/linux/fs/buffer.c linux/fs/buffer.c
--- v2.2.4/linux/fs/buffer.c	Tue Mar 23 14:35:48 1999
+++ linux/fs/buffer.c	Wed Mar 24 09:37:18 1999
@@ -853,6 +853,7 @@
 		__brelse(buf);
 		return;
 	}
+	buf->b_count = 0;
 	remove_from_queues(buf);
 	put_last_free(buf);
 }
diff -u --recursive --new-file v2.2.4/linux/fs/ioctl.c linux/fs/ioctl.c
--- v2.2.4/linux/fs/ioctl.c	Thu Nov 19 09:56:28 1998
+++ linux/fs/ioctl.c	Wed Mar 24 08:08:53 1999
@@ -74,14 +74,20 @@
 				filp->f_flags &= ~flag;
 			break;
 
-		case FIOASYNC: /* O_SYNC is not yet implemented,
-				  but it's here for completeness. */
+		case FIOASYNC:
 			if ((error = get_user(on, (int *)arg)) != 0)
 				break;
+			flag = on ? FASYNC : 0;
+
+			/* Did FASYNC state change ? */
+			if ((flag ^ filp->f_flags) & FASYNC) {
+				if (filp->f_op && filp->f_op->fasync)
+					filp->f_op->fasync(fd, filp, on); 
+			}
 			if (on)
-				filp->f_flags |= O_SYNC;
+				filp->f_flags |= FASYNC;
 			else
-				filp->f_flags &= ~O_SYNC;
+				filp->f_flags &= ~FASYNC;
 			break;
 
 		default:
diff -u --recursive --new-file v2.2.4/linux/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c
--- v2.2.4/linux/fs/lockd/svcsubs.c	Wed Mar 10 15:29:49 1999
+++ linux/fs/lockd/svcsubs.c	Thu Mar 25 09:23:34 1999
@@ -127,7 +127,7 @@
 			kfree(file);
 			return;
 		}
-		fp = &file->f_next;
+		fp = &f->f_next;
 	}
 
 	printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
diff -u --recursive --new-file v2.2.4/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- v2.2.4/linux/fs/ncpfs/sock.c	Sun Nov  8 14:03:06 1998
+++ linux/fs/ncpfs/sock.c	Fri Mar 26 13:57:41 1999
@@ -94,7 +94,7 @@
 	poll_table wait_table;
 	struct poll_table_entry entry;
 	int init_timeout, max_timeout;
-	int timeout; long tmp_timeout;
+	int timeout;
 	int retrans;
 	int major_timeout_seen;
 	int acknowledge_seen;
diff -u --recursive --new-file v2.2.4/linux/fs/qnx4/file.c linux/fs/qnx4/file.c
--- v2.2.4/linux/fs/qnx4/file.c	Thu Nov 12 16:21:23 1998
+++ linux/fs/qnx4/file.c	Fri Mar 26 13:57:41 1999
@@ -33,8 +33,6 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
-#include <linux/fs.h>
-#include <linux/qnx4_fs.h>
 
 static int qnx4_readpage(struct file *file, struct page *page);
 
diff -u --recursive --new-file v2.2.4/linux/fs/sysv/CHANGES linux/fs/sysv/CHANGES
--- v2.2.4/linux/fs/sysv/CHANGES	Thu Dec 31 10:29:02 1998
+++ linux/fs/sysv/CHANGES	Wed Mar 24 12:34:35 1999
@@ -1,45 +1,55 @@
-Mon Dec 15 1997	  Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+Mon, 15 Dec 1997	  Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
 	*    namei.c: struct sysv_dir_inode_operations updated to use dentries.
 
-Fri Jan 23 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+Fri, 23 Jan 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
 	*    inode.c: corrected 1 track offset setting (in sb->sv_block_base).
 		      Originally it was overridden (by setting to zero)
 		      in detected_[xenix,sysv4,sysv2,coherent]. Thanks
 		      to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
 		      for identifying the problem.
 
-Tue Jan 27 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+Tue, 27 Jan 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
         *    inode.c: added 2048-byte block support to SystemV FS.
 		      Merged detected_bs[512,1024,2048]() into one function:
 		      void detected_bs (u_char type, struct super_block *sb).
 		      Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
 		      for the patch.
 
-Wed Feb  4 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+Wed, 4 Feb 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
 	*    namei.c: removed static subdir(); is_subdir() from dcache.c
 		      is used instead. Cosmetic changes.
 
-Thu Dec  3 1998   Al Viro (viro@math.psu.edu)
+Thu, 3 Dec 1998   Al Viro (viro@math.psu.edu)
 	*    namei.c (sysv_rmdir):
 		      Bugectomy: old check for victim being busy
 		      (inode->i_count) wasn't replaced (with checking
 		      dentry->d_count) and escaped Linus in the last round
 		      of changes. Shot and buried.
 
-Wed Dec  9 1998   AV
+Wed, 9 Dec 1998   AV
 	*    namei.c (do_sysv_rename):
 		       Fixed incorrect check for other owners + race.
 		       Removed checks that went to VFS.
 	*    namei.c (sysv_unlink):
 		       Removed checks that went to VFS.
 
-Thu Dec 10 1998   AV
+Thu, 10 Dec 1998   AV
 	*    namei.c (do_mknod):
 			Removed dead code - mknod is never asked to
 			create a symlink or directory. Incidentially,
 			it wouldn't do it right if it would be called.
 
-Sat Dec 26 1998   KGB
+Sat, 26 Dec 1998   KGB
 	*    inode.c (detect_sysv4):
 			Added detection of expanded s_type field (0x10,
 			0x20 and 0x30).  Forced read-only access in this case.
+
+Sun, 21 Mar 1999   AV
+	*    namei.c (sysv_link):
+			Fixed i_count usage that resulted in dcache corruption.
+	*    inode.c:
+			Filled ->delete_inode() method with sysv_delete_inode().
+			sysv_put_inode() is gone, as it tried to do ->delete_
+			_inode()'s job.
+	*    ialloc.c: (sysv_free_inode):
+			Fixed race.
diff -u --recursive --new-file v2.2.4/linux/include/asm-alpha/mmu_context.h linux/include/asm-alpha/mmu_context.h
--- v2.2.4/linux/include/asm-alpha/mmu_context.h	Wed Sep  9 14:51:10 1998
+++ linux/include/asm-alpha/mmu_context.h	Wed Mar 24 07:41:55 1999
@@ -14,11 +14,11 @@
 /*
  * The maximum ASN's the processor supports.  On the EV4 this is 63
  * but the PAL-code doesn't actually use this information.  On the
- * EV5 this is 127.
+ * EV5 this is 127, and EV6 has 255.
  *
  * On the EV4, the ASNs are more-or-less useless anyway, as they are
- * only used as an icache tag, not for TB entries.  On the EV5 ASN's
- * also validate the TB entries, and thus make a lot more sense.
+ * only used as an icache tag, not for TB entries.  On the EV5 and EV6,
+ * ASN's also validate the TB entries, and thus make a lot more sense.
  *
  * The EV4 ASN's don't even match the architecture manual, ugh.  And
  * I quote: "If a processor implements address space numbers (ASNs),
@@ -73,7 +73,7 @@
 extern unsigned long asn_cache;
 #endif /* __SMP__ */
 
-#define WIDTH_HARDWARE_ASN	7
+#define WIDTH_HARDWARE_ASN	8
 #define ASN_FIRST_VERSION (1UL << (WIDTH_THIS_PROCESSOR + WIDTH_HARDWARE_ASN))
 #define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1)
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-alpha/termios.h linux/include/asm-alpha/termios.h
--- v2.2.4/linux/include/asm-alpha/termios.h	Tue Dec 22 14:16:57 1998
+++ linux/include/asm-alpha/termios.h	Wed Mar 24 11:46:00 1999
@@ -79,6 +79,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 /*	eof=^D		eol=\0		eol2=\0		erase=del
diff -u --recursive --new-file v2.2.4/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h
--- v2.2.4/linux/include/asm-i386/termios.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-i386/termios.h	Wed Mar 24 11:45:59 1999
@@ -52,6 +52,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IR - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-i386/timex.h linux/include/asm-i386/timex.h
--- v2.2.4/linux/include/asm-i386/timex.h	Fri Jan  1 12:58:21 1999
+++ linux/include/asm-i386/timex.h	Sun Mar 28 10:03:26 1999
@@ -39,7 +39,7 @@
 #else
 	unsigned long eax, edx;
 
-	__asm__("rdtsc":"=a" (eax), "=d" (edx));
+	__asm__ __volatile__("rdtsc":"=a" (eax), "=d" (edx));
 	return eax;
 #endif
 }
diff -u --recursive --new-file v2.2.4/linux/include/asm-m68k/termios.h linux/include/asm-m68k/termios.h
--- v2.2.4/linux/include/asm-m68k/termios.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-m68k/termios.h	Wed Mar 24 11:46:00 1999
@@ -60,6 +60,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-mips/termios.h linux/include/asm-mips/termios.h
--- v2.2.4/linux/include/asm-mips/termios.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-mips/termios.h	Wed Mar 24 11:45:59 1999
@@ -98,6 +98,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-ppc/termios.h linux/include/asm-ppc/termios.h
--- v2.2.4/linux/include/asm-ppc/termios.h	Thu Dec 31 10:29:02 1998
+++ linux/include/asm-ppc/termios.h	Wed Mar 24 11:46:00 1999
@@ -184,6 +184,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/mmu_context.h linux/include/asm-sparc/mmu_context.h
--- v2.2.4/linux/include/asm-sparc/mmu_context.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc/mmu_context.h	Thu Mar 25 09:23:34 1999
@@ -22,9 +22,9 @@
 
 #define destroy_context(mm) BTFIXUP_CALL(destroy_context)(mm)
 
-/* After we have set current->mm to a new value, this activates
- * the context for the new mm so we see the new mappings.
+/* This need not do anything on Sparc32.  The switch happens
+ * properly later as a side effect of calling flush_thread.
  */
-#define activate_context(tsk)	switch_to_context(tsk)
+#define activate_context(tsk)	do { } while(0)
 
 #endif /* !(__SPARC_MMU_CONTEXT_H) */
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.2.4/linux/include/asm-sparc/processor.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc/processor.h	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.69 1999/01/19 07:57:44 davem Exp $
+/* $Id: processor.h,v 1.70 1999/03/24 11:42:44 davem Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -148,7 +148,10 @@
 extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 
-#define copy_segments(nr, tsk, mm)	do { } while (0)
+#define copy_segments(__nr, __tsk, __mm)	\
+	if((__tsk) == current &&		\
+	   (__mm) != NULL)			\
+		flush_user_windows()
 #define release_segments(mm)		do { } while (0)
 #define forget_segments()		do { } while (0)
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/termios.h linux/include/asm-sparc/termios.h
--- v2.2.4/linux/include/asm-sparc/termios.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc/termios.h	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: termios.h,v 1.28 1999/01/02 16:50:22 davem Exp $ */
+/* $Id: termios.h,v 1.29 1999/03/25 09:11:18 davem Exp $ */
 #ifndef _SPARC_TERMIOS_H
 #define _SPARC_TERMIOS_H
 
@@ -68,6 +68,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.2.4/linux/include/asm-sparc/unistd.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc/unistd.h	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.53 1999/02/21 02:34:54 anton Exp $ */
+/* $Id: unistd.h,v 1.54 1999/03/25 00:40:12 davem Exp $ */
 #ifndef _SPARC_UNISTD_H
 #define _SPARC_UNISTD_H
 
@@ -428,6 +428,8 @@
 static __inline__ _syscall0(int,sync)
 static __inline__ _syscall0(pid_t,setsid)
 static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
+static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 static __inline__ _syscall1(int,dup,int,fd)
 static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp)
 static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode)
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.2.4/linux/include/asm-sparc64/pgtable.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc64/pgtable.h	Sun Mar 28 09:07:47 1999
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.102 1999/01/22 16:19:29 jj Exp $
+/* $Id: pgtable.h,v 1.103 1999/03/28 08:40:04 davem Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -333,8 +333,8 @@
 extern struct pgtable_cache_struct {
 	unsigned long *pgd_cache;
 	unsigned long *pte_cache;
-	unsigned long pgcache_size;
-	unsigned long pgdcache_size;
+	unsigned int pgcache_size;
+	unsigned int pgdcache_size;
 } pgt_quicklists;
 #endif
 #define pgd_quicklist		(pgt_quicklists.pgd_cache)
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc64/smp.h linux/include/asm-sparc64/smp.h
--- v2.2.4/linux/include/asm-sparc64/smp.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc64/smp.h	Sun Mar 28 09:07:47 1999
@@ -98,8 +98,17 @@
 /* This needn't do anything as we do not sleep the cpu
  * inside of the idler task, so an interrupt is not needed
  * to get a clean fast response.
+ *
+ * Addendum: We do want it to do something for the signal
+ *           delivery case, we detect that by just seeing
+ *           if we are trying to send this to an idler or not.
  */
-extern __inline__ void smp_send_reschedule(int cpu) { }
+extern __inline__ void smp_send_reschedule(int cpu)
+{
+	extern void smp_receive_signal(int);
+	if(cpu_data[cpu].idle_volume == 0)
+		smp_receive_signal(cpu);
+}
 
 /* This is a nop as well because we capture all other cpus
  * anyways when making the PROM active.
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc64/termios.h linux/include/asm-sparc64/termios.h
--- v2.2.4/linux/include/asm-sparc64/termios.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc64/termios.h	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: termios.h,v 1.7 1999/01/02 16:50:29 davem Exp $ */
+/* $Id: termios.h,v 1.8 1999/03/25 09:11:26 davem Exp $ */
 #ifndef _SPARC64_TERMIOS_H
 #define _SPARC64_TERMIOS_H
 
@@ -68,6 +68,7 @@
 #define N_PROFIBUS_FDL	10	/* Reserved for Profibus <Dave@mvhi.com> */
 #define N_IRDA		11	/* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */
 #define N_SMSBLOCK	12	/* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC		13	/* synchronous HDLC */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h
--- v2.2.4/linux/include/asm-sparc64/unistd.h	Tue Mar 23 14:35:48 1999
+++ linux/include/asm-sparc64/unistd.h	Thu Mar 25 09:23:34 1999
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.26 1999/02/10 22:24:35 jj Exp $ */
+/* $Id: unistd.h,v 1.27 1999/03/25 00:40:14 davem Exp $ */
 #ifndef _SPARC64_UNISTD_H
 #define _SPARC64_UNISTD_H
 
@@ -417,6 +417,7 @@
 static __inline__ _syscall0(pid_t,setsid)
 static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
 static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 static __inline__ _syscall1(int,dup,int,fd)
 static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp)
 static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode)
diff -u --recursive --new-file v2.2.4/linux/include/linux/adfs_fs_sb.h linux/include/linux/adfs_fs_sb.h
--- v2.2.4/linux/include/linux/adfs_fs_sb.h	Fri Jul 31 17:05:52 1998
+++ linux/include/linux/adfs_fs_sb.h	Fri Mar 26 13:57:41 1999
@@ -15,6 +15,10 @@
 struct adfs_sb_info {
 	struct buffer_head *s_sbh;	/* buffer head containing disc record	 */
 	struct adfs_discrecord *s_dr;	/* pointer to disc record in s_sbh	 */
+	uid_t	s_uid;			/* owner uid				 */
+	gid_t	s_gid;			/* owner gid				 */
+	int	s_owner_mask;		/* ADFS Owner perm -> unix perm		 */
+	int	s_other_mask;		/* ADFS Other perm -> unix perm		 */
 	__u16	s_zone_size;		/* size of a map zone in bits		 */
 	__u16	s_ids_per_zone;		/* max. no ids in one zone		 */
 	__u32	s_idlen;		/* length of ID in map			 */
diff -u --recursive --new-file v2.2.4/linux/include/linux/hippidevice.h linux/include/linux/hippidevice.h
--- v2.2.4/linux/include/linux/hippidevice.h	Fri Oct  9 13:27:16 1998
+++ linux/include/linux/hippidevice.h	Sun Mar 28 10:03:28 1999
@@ -52,6 +52,7 @@
 void hippi_setup(struct device *dev);
 
 extern struct device *init_hippi_dev(struct device *, int);
+extern void unregister_hipdev(struct device *dev);
 #endif
 
 #endif	/* _LINUX_HIPPIDEVICE_H */
diff -u --recursive --new-file v2.2.4/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v2.2.4/linux/include/linux/if_arp.h	Tue Dec 22 14:16:58 1998
+++ linux/include/linux/if_arp.h	Sun Mar 28 10:04:25 1999
@@ -66,6 +66,13 @@
 #define ARPHRD_ASH	781		/* Nexus 64Mbps Ash		*/
 #define ARPHRD_ECONET	782		/* Acorn Econet			*/
 #define ARPHRD_IRDA 	783		/* Linux/IR			*/
+/* ARP works differently on different FC media .. so  */
+#define ARPHRD_FCPP	784		/* Point to point fibrechanel	*/
+#define ARPHRD_FCAL	785		/* Fibrechannel arbitrated loop */
+#define ARPHRD_FCPL	786		/* Fibrechannel public loop	*/
+#define ARPHRD_FCFABRIC	787		/* Fibrechannel fabric		*/
+	/* 787->799 reserved for fibrechannel media types */
+
 
 /* ARP protocol opcodes. */
 #define	ARPOP_REQUEST	1		/* ARP request			*/
diff -u --recursive --new-file v2.2.4/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- v2.2.4/linux/include/linux/nfsd/nfsfh.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/nfsd/nfsfh.h	Sun Mar 28 10:03:54 1999
@@ -107,6 +107,9 @@
 void	nfsd_fh_init(void);
 void	nfsd_fh_free(void);
 
+void	expire_all(void);
+void	expire_by_dentry(struct dentry *);
+
 static __inline__ struct svc_fh *
 fh_copy(struct svc_fh *dst, struct svc_fh *src)
 {
diff -u --recursive --new-file v2.2.4/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.2.4/linux/include/linux/sched.h	Wed Mar 10 15:29:50 1999
+++ linux/include/linux/sched.h	Sun Mar 28 10:03:29 1999
@@ -113,6 +113,7 @@
  */
 extern rwlock_t tasklist_lock;
 extern spinlock_t scheduler_lock;
+extern spinlock_t runqueue_lock;
 
 extern void sched_init(void);
 extern void show_state(void);
diff -u --recursive --new-file v2.2.4/linux/include/linux/synclink.h linux/include/linux/synclink.h
--- v2.2.4/linux/include/linux/synclink.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/synclink.h	Fri Mar 12 08:38:44 1999
@@ -0,0 +1,243 @@
+/*
+ * SyncLink Multiprotocol Serial Adapter Driver
+ *
+ * Copyright (C) 1998 by Microgate Corporation
+ * 
+ * Redistribution of this file is permitted under 
+ * the terms of the GNU Public License (GPL)
+ */
+
+#ifndef _SYNCLINK_H_
+#define _SYNCLINK_H_
+
+#define BOOLEAN int
+#define TRUE 1
+#define FALSE 0
+
+#define BIT0	0x0001
+#define BIT1	0x0002
+#define BIT2	0x0004
+#define BIT3	0x0008
+#define BIT4	0x0010
+#define BIT5	0x0020
+#define BIT6	0x0040
+#define BIT7	0x0080
+#define BIT8	0x0100
+#define BIT9	0x0200
+#define BIT10	0x0400
+#define BIT11	0x0800
+#define BIT12	0x1000
+#define BIT13	0x2000
+#define BIT14	0x4000
+#define BIT15	0x8000
+#define BIT16	0x00010000
+#define BIT17	0x00020000
+#define BIT18	0x00040000
+#define BIT19	0x00080000
+#define BIT20	0x00100000
+#define BIT21	0x00200000
+#define BIT22	0x00400000
+#define BIT23	0x00800000
+#define BIT24	0x01000000
+#define BIT25	0x02000000
+#define BIT26	0x04000000
+#define BIT27	0x08000000
+#define BIT28	0x10000000
+#define BIT29	0x20000000
+#define BIT30	0x40000000
+#define BIT31	0x80000000
+
+
+#define HDLC_MAX_FRAME_SIZE	4096
+#define MAX_ASYNC_TRANSMIT	4096
+
+#define ASYNC_PARITY_NONE		0
+#define ASYNC_PARITY_EVEN		1
+#define ASYNC_PARITY_ODD		2
+#define ASYNC_PARITY_SPACE		3
+
+#define HDLC_FLAG_UNDERRUN_ABORT7	0x0000
+#define HDLC_FLAG_UNDERRUN_ABORT15	0x0001
+#define HDLC_FLAG_UNDERRUN_FLAG		0x0002
+#define HDLC_FLAG_UNDERRUN_CRC		0x0004
+#define HDLC_FLAG_SHARE_ZERO		0x0010
+#define HDLC_FLAG_AUTO_CTS		0x0020
+#define HDLC_FLAG_AUTO_DCD		0x0040
+#define HDLC_FLAG_AUTO_RTS		0x0080
+#define HDLC_FLAG_RXC_DPLL		0x0100
+#define HDLC_FLAG_RXC_BRG		0x0200
+#define HDLC_FLAG_TXC_DPLL		0x0400
+#define HDLC_FLAG_TXC_BRG		0x0800
+#define HDLC_FLAG_DPLL_DIV8		0x1000
+#define HDLC_FLAG_DPLL_DIV16		0x2000
+#define HDLC_FLAG_DPLL_DIV32		0x0000
+
+#define HDLC_CRC_NONE			0
+#define HDLC_CRC_16_CCITT		1
+
+#define HDLC_TXIDLE_FLAGS		0
+#define HDLC_TXIDLE_ALT_ZEROS_ONES	1
+#define HDLC_TXIDLE_ZEROS		2
+#define HDLC_TXIDLE_ONES		3
+#define HDLC_TXIDLE_ALT_MARK_SPACE	4
+#define HDLC_TXIDLE_SPACE		5
+#define HDLC_TXIDLE_MARK		6
+
+#define HDLC_ENCODING_NRZ			0
+#define HDLC_ENCODING_NRZB			1
+#define HDLC_ENCODING_NRZI_MARK			2
+#define HDLC_ENCODING_NRZI_SPACE		3
+#define HDLC_ENCODING_BIPHASE_MARK		4
+#define HDLC_ENCODING_BIPHASE_SPACE		5
+#define HDLC_ENCODING_BIPHASE_LEVEL		6
+#define HDLC_ENCODING_DIFF_BIPHASE_LEVEL	7
+
+#define HDLC_PREAMBLE_LENGTH_8BITS	0
+#define HDLC_PREAMBLE_LENGTH_16BITS	1
+#define HDLC_PREAMBLE_LENGTH_32BITS	2
+#define HDLC_PREAMBLE_LENGTH_64BITS	3
+
+#define HDLC_PREAMBLE_PATTERN_NONE	0
+#define HDLC_PREAMBLE_PATTERN_ZEROS	1
+#define HDLC_PREAMBLE_PATTERN_FLAGS	2
+#define HDLC_PREAMBLE_PATTERN_10	3
+#define HDLC_PREAMBLE_PATTERN_01	4
+#define HDLC_PREAMBLE_PATTERN_ONES	5
+
+#define MGSL_MODE_ASYNC		1
+#define MGSL_MODE_HDLC		2
+
+#define MGSL_BUS_TYPE_ISA	1
+#define MGSL_BUS_TYPE_EISA	2
+#define MGSL_BUS_TYPE_PCI	5
+
+typedef struct _MGSL_PARAMS
+{
+	/* Common */
+
+	unsigned long	mode;		/* Asynchronous or HDLC */
+	unsigned char	loopback;	/* internal loopback mode */
+	
+	/* HDLC Only */
+
+	unsigned short	flags;
+	unsigned char	encoding;	/* NRZ, NRZI, etc. */
+	unsigned long	clock_speed;	/* external clock speed in bits per second */
+	unsigned char	addr_filter;	/* receive HDLC address filter, 0xFF = disable */
+	unsigned short	crc_type;	/* None, CRC16 or CRC16-CCITT */
+	unsigned char	preamble_length;
+	unsigned char	preamble;
+
+	/* Async Only */
+
+	unsigned long	data_rate;	/* bits per second */
+	unsigned char	data_bits;	/* 7 or 8 data bits */
+	unsigned char	stop_bits;	/* 1 or 2 stop bits */
+	unsigned char	parity;		/* none, even, or odd */
+
+} MGSL_PARAMS, *PMGSL_PARAMS;
+
+#define MICROGATE_VENDOR_ID 0x13c0
+#define SYNCLINK_DEVICE_ID 0x0010
+#define MGSL_MAX_SERIAL_NUMBER 30
+
+/*
+** device diagnostics status
+*/
+
+#define DiagStatus_OK				0
+#define DiagStatus_AddressFailure		1
+#define DiagStatus_AddressConflict		2
+#define DiagStatus_IrqFailure			3
+#define DiagStatus_IrqConflict			4
+#define DiagStatus_DmaFailure			5
+#define DiagStatus_DmaConflict			6
+#define DiagStatus_PciAdapterNotFound		7
+#define DiagStatus_CantAssignPciResources	8
+#define DiagStatus_CantAssignPciMemAddr		9
+#define DiagStatus_CantAssignPciIoAddr		10
+#define DiagStatus_CantAssignPciIrq		11
+#define DiagStatus_MemoryError			12
+
+#define SerialSignal_DCD            0x01     /* Data Carrier Detect */
+#define SerialSignal_TXD            0x02     /* Transmit Data */
+#define SerialSignal_RI             0x04     /* Ring Indicator */
+#define SerialSignal_RXD            0x08     /* Receive Data */
+#define SerialSignal_CTS            0x10     /* Clear to Send */
+#define SerialSignal_RTS            0x20     /* Request to Send */
+#define SerialSignal_DSR            0x40     /* Data Set Ready */
+#define SerialSignal_DTR            0x80     /* Data Terminal Ready */
+
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct mgsl_icount {
+	__u32	cts, dsr, rng, dcd, tx, rx;
+	__u32	frame, parity, overrun, brk;
+	__u32	buf_overrun;
+	__u32	txok;
+	__u32	txunder;
+	__u32	txabort;
+	__u32	txtimeout;
+	__u32	rxshort;
+	__u32	rxlong;
+	__u32	rxabort;
+	__u32	rxover;
+	__u32	rxcrc;
+	__u32	rxok;
+	__u32	exithunt;
+	__u32	rxidle;
+};
+
+
+#define DEBUG_LEVEL_DATA	1
+#define DEBUG_LEVEL_ERROR 	2
+#define DEBUG_LEVEL_INFO  	3
+#define DEBUG_LEVEL_BH    	4
+#define DEBUG_LEVEL_ISR		5
+
+/*
+** Event bit flags for use with MgslWaitEvent
+*/
+
+#define MgslEvent_DsrActive	0x0001
+#define MgslEvent_DsrInactive	0x0002
+#define MgslEvent_Dsr		0x0003
+#define MgslEvent_CtsActive	0x0004
+#define MgslEvent_CtsInactive	0x0008
+#define MgslEvent_Cts		0x000c
+#define MgslEvent_DcdActive	0x0010
+#define MgslEvent_DcdInactive	0x0020
+#define MgslEvent_Dcd		0x0030
+#define MgslEvent_RiActive	0x0040
+#define MgslEvent_RiInactive	0x0080
+#define MgslEvent_Ri		0x00c0
+#define MgslEvent_ExitHuntMode	0x0100
+#define MgslEvent_IdleReceived	0x0200
+
+/* Private IOCTL codes:
+ *
+ * MGSL_IOCSPARAMS	set MGSL_PARAMS structure values
+ * MGSL_IOCGPARAMS	get current MGSL_PARAMS structure values
+ * MGSL_IOCSTXIDLE	set current transmit idle mode
+ * MGSL_IOCGTXIDLE	get current transmit idle mode
+ * MGSL_IOCTXENABLE	enable or disable transmitter
+ * MGSL_IOCRXENABLE	enable or disable receiver
+ * MGSL_IOCTXABORT	abort transmitting frame (HDLC)
+ * MGSL_IOCGSTATS	return current statistics
+ * MGSL_IOCWAITEVENT	wait for specified event to occur
+ */
+#define MGSL_MAGIC_IOC	'm'
+#define MGSL_IOCSPARAMS		_IOW(MGSL_MAGIC_IOC,0,sizeof(MGSL_PARAMS))
+#define MGSL_IOCGPARAMS		_IOR(MGSL_MAGIC_IOC,1,sizeof(MGSL_PARAMS))
+#define MGSL_IOCSTXIDLE		_IO(MGSL_MAGIC_IOC,2)
+#define MGSL_IOCGTXIDLE		_IO(MGSL_MAGIC_IOC,3)
+#define MGSL_IOCTXENABLE	_IO(MGSL_MAGIC_IOC,4)
+#define MGSL_IOCRXENABLE	_IO(MGSL_MAGIC_IOC,5)
+#define MGSL_IOCTXABORT		_IO(MGSL_MAGIC_IOC,6)
+#define MGSL_IOCGSTATS		_IO(MGSL_MAGIC_IOC,7)
+#define MGSL_IOCWAITEVENT	_IO(MGSL_MAGIC_IOC,8)
+#define MGSL_IOCCLRMODCOUNT	_IO(MGSL_MAGIC_IOC,15)
+
+#endif /* _SYNCLINK_H_ */
diff -u --recursive --new-file v2.2.4/linux/include/net/pkt_cls.h linux/include/net/pkt_cls.h
--- v2.2.4/linux/include/net/pkt_cls.h	Tue Mar 23 14:35:48 1999
+++ linux/include/net/pkt_cls.h	Thu Mar 25 09:23:34 1999
@@ -79,9 +79,8 @@
 
 extern __inline__ unsigned long cls_set_class(unsigned long *clp, unsigned long cl)
 {
-	net_serialize_enter();
 	cl = xchg(clp, cl);
-	net_serialize_leave();
+	synchronize_bh();
 	return cl;
 }
 
diff -u --recursive --new-file v2.2.4/linux/include/net/sock.h linux/include/net/sock.h
--- v2.2.4/linux/include/net/sock.h	Tue Mar 23 14:35:48 1999
+++ linux/include/net/sock.h	Sun Mar 28 10:04:27 1999
@@ -845,6 +845,9 @@
 
 extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_FILTER
+	struct sk_filter *filter;
+#endif
 	/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
 	   number of warnings when compiling with -W --ANK
 	 */
@@ -852,7 +855,7 @@
                 return -ENOMEM;
 
 #ifdef CONFIG_FILTER
-	if (sk->filter && sk_filter(skb, sk->filter))
+	if ((filter = sk->filter) != NULL && sk_filter(skb, filter))
 		return -EPERM;	/* Toss packet */
 #endif /* CONFIG_FILTER */
 
@@ -922,14 +925,6 @@
 {
 	return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
 }
-
-#ifdef __SMP__
-#define net_serialize_enter()	start_bh_atomic()
-#define net_serialize_leave()	end_bh_atomic()
-#else
-#define net_serialize_enter()	barrier();
-#define net_serialize_leave()	barrier();
-#endif
 
 /* 
  *	Enable debug/info messages 
diff -u --recursive --new-file v2.2.4/linux/kernel/acct.c linux/kernel/acct.c
--- v2.2.4/linux/kernel/acct.c	Tue Mar 23 14:35:48 1999
+++ linux/kernel/acct.c	Tue Mar 23 16:57:38 1999
@@ -194,13 +194,13 @@
 	}
 	if (old_acct) {
 		do_acct_process(0,old_acct);
-		filp_close(old_acct);
+		filp_close(old_acct, NULL);
 	}
 out:
 	unlock_kernel();
 	return error;
 out_err:
-	filp_close(file);
+	filp_close(file, NULL);
 	goto out;
 }
 
diff -u --recursive --new-file v2.2.4/linux/kernel/fork.c linux/kernel/fork.c
--- v2.2.4/linux/kernel/fork.c	Tue Mar 23 14:35:48 1999
+++ linux/kernel/fork.c	Fri Mar 26 09:45:07 1999
@@ -57,33 +57,39 @@
 
 #define uidhashfn(uid)	(((uid >> 8) ^ uid) & (UIDHASH_SZ - 1))
 
+/*
+ * These routines must be called with the uidhash spinlock held!
+ */
 static inline void uid_hash_insert(struct user_struct *up, unsigned int hashent)
 {
-	spin_lock(&uidhash_lock);
 	if((up->next = uidhash[hashent]) != NULL)
 		uidhash[hashent]->pprev = &up->next;
 	up->pprev = &uidhash[hashent];
 	uidhash[hashent] = up;
-	spin_unlock(&uidhash_lock);
 }
 
 static inline void uid_hash_remove(struct user_struct *up)
 {
-	spin_lock(&uidhash_lock);
 	if(up->next)
 		up->next->pprev = up->pprev;
 	*up->pprev = up->next;
-	spin_unlock(&uidhash_lock);
 }
 
-static inline struct user_struct *uid_find(unsigned short uid, unsigned int hashent)
+static inline struct user_struct *uid_hash_find(unsigned short uid, unsigned int hashent)
 {
-	struct user_struct *up;
+	struct user_struct *up, *next;
 
-	spin_lock(&uidhash_lock);
-	for(up = uidhash[hashent]; (up && up->uid != uid); up = up->next)
-		;
-	spin_unlock(&uidhash_lock);
+	next = uidhash[hashent];
+	for (;;) {
+		up = next;
+		if (next) {
+			next = up->next;
+			if (up->uid != uid)
+				continue;
+			atomic_inc(&up->count);
+		}
+		break;
+	}
 	return up;
 }
 
@@ -94,7 +100,9 @@
 	if (up) {
 		p->user = NULL;
 		if (atomic_dec_and_test(&up->count)) {
+			spin_lock(&uidhash_lock);
 			uid_hash_remove(up);
+			spin_unlock(&uidhash_lock);
 			kmem_cache_free(uid_cachep, up);
 		}
 	}
@@ -103,20 +111,37 @@
 int alloc_uid(struct task_struct *p)
 {
 	unsigned int hashent = uidhashfn(p->uid);
-	struct user_struct *up = uid_find(p->uid, hashent);
+	struct user_struct *up;
+
+	spin_lock(&uidhash_lock);
+	up = uid_hash_find(p->uid, hashent);
+	spin_unlock(&uidhash_lock);
 
-	p->user = up;
 	if (!up) {
-		up = kmem_cache_alloc(uid_cachep, SLAB_KERNEL);
-		if (!up)
+		struct user_struct *new;
+
+		new = kmem_cache_alloc(uid_cachep, SLAB_KERNEL);
+		if (!new)
 			return -EAGAIN;
-		p->user = up;
-		up->uid = p->uid;
-		atomic_set(&up->count, 0);
-		uid_hash_insert(up, hashent);
-	}
+		new->uid = p->uid;
+		atomic_set(&new->count, 1);
+
+		/*
+		 * Before adding this, check whether we raced
+		 * on adding the same user already..
+		 */
+		spin_lock(&uidhash_lock);
+		up = uid_hash_find(p->uid, hashent);
+		if (up) {
+			kmem_cache_free(uid_cachep, new);
+		} else {
+			uid_hash_insert(new, hashent);
+			up = new;
+		}
+		spin_unlock(&uidhash_lock);
 
-	atomic_inc(&up->count);
+	}
+	p->user = up;
 	return 0;
 }
 
diff -u --recursive --new-file v2.2.4/linux/kernel/signal.c linux/kernel/signal.c
--- v2.2.4/linux/kernel/signal.c	Tue Dec 22 14:16:59 1998
+++ linux/kernel/signal.c	Fri Mar 26 14:01:26 1999
@@ -363,8 +363,27 @@
 	}
 
 	sigaddset(&t->signal, sig);
-	if (!sigismember(&t->blocked, sig))
+	if (!sigismember(&t->blocked, sig)) {
 		t->sigpending = 1;
+#ifdef __SMP__
+		/*
+		 * If the task is running on a different CPU 
+		 * force a reschedule on the other CPU - note that
+		 * the code below is a tad loose and might occasionally
+		 * kick the wrong CPU if we catch the process in the
+		 * process of changing - but no harm is done by that
+		 * other than doing an extra (lightweight) IPI interrupt.
+		 *
+		 * note that we rely on the previous spin_lock to
+		 * lock interrupts for us! No need to set need_resched
+		 * since signal event passing goes through ->blocked.
+		 */
+		spin_lock(&runqueue_lock);
+		if (t->has_cpu && t->processor != smp_processor_id())
+			smp_send_reschedule(t->processor);
+		spin_unlock(&runqueue_lock);
+#endif /* __SMP__ */
+	}
 
 out:
 	spin_unlock_irqrestore(&t->sigmask_lock, flags);
diff -u --recursive --new-file v2.2.4/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.2.4/linux/net/appletalk/ddp.c	Thu Jan  7 15:11:41 1999
+++ linux/net/appletalk/ddp.c	Fri Mar 26 14:01:40 1999
@@ -356,7 +356,7 @@
 	/*
 	 * Scan the networks.
 	 */
-
+	atif->status |= ATIF_PROBE;
 	for(netct = 0; netct <= netrange; netct++)
 	{
 		/*
@@ -374,8 +374,10 @@
 				 */
 				aarp_probe_network(atif);
 
-				if(!(atif->status & ATIF_PROBE_FAIL))
+				if(!(atif->status & ATIF_PROBE_FAIL)) {
+					atif->status &= ~ATIF_PROBE;
 					return (0);
+				}
 			}
 			atif->status &= ~ATIF_PROBE_FAIL;
 		}
@@ -383,7 +385,7 @@
 		if(probe_net > ntohs(atif->nets.nr_lastnet))
 			probe_net = ntohs(atif->nets.nr_firstnet);
 	}
-
+	atif->status &= ~ATIF_PROBE;
 	return (-EADDRINUSE);	/* Network is full... */
 }
 
diff -u --recursive --new-file v2.2.4/linux/net/core/dev.c linux/net/core/dev.c
--- v2.2.4/linux/net/core/dev.c	Tue Mar 23 14:35:48 1999
+++ linux/net/core/dev.c	Thu Mar 25 09:23:34 1999
@@ -232,9 +232,8 @@
 	{
 		if(pt==(*pt1))
 		{
-			net_serialize_enter();
 			*pt1=pt->next;
-			net_serialize_leave();
+			synchronize_bh();
 #ifdef CONFIG_NET_FASTROUTE
 			if (pt->data)
 				netdev_fastroute_obstacles--;
@@ -1823,9 +1822,8 @@
 	/* And unlink it from device chain. */
 	for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
 		if (d == dev) {
-			net_serialize_enter();
 			*dp = d->next;
-			net_serialize_leave();
+			synchronize_bh();
 			d->next = NULL;
 
 			if (dev->destructor)
@@ -1986,9 +1984,8 @@
 			/*
 			 *	It failed to come up. Unhook it.
 			 */
-			net_serialize_enter();
 			*dp = dev->next;
-			net_serialize_leave();
+			synchronize_bh();
 		} 
 		else
 		{
diff -u --recursive --new-file v2.2.4/linux/net/core/filter.c linux/net/core/filter.c
--- v2.2.4/linux/net/core/filter.c	Tue Mar 23 14:35:48 1999
+++ linux/net/core/filter.c	Thu Mar 25 09:23:34 1999
@@ -441,9 +441,8 @@
 
 	if ((err = sk_chk_filter(fp->insns, fp->len))==0) {
 		struct sk_filter *old_fp = sk->filter;
-		net_serialize_enter();
 		sk->filter = fp;
-		net_serialize_leave();
+		synchronize_bh();
 		fp = old_fp;
 	}
 
diff -u --recursive --new-file v2.2.4/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.2.4/linux/net/core/neighbour.c	Tue Mar 23 14:35:48 1999
+++ linux/net/core/neighbour.c	Thu Mar 25 09:23:34 1999
@@ -337,9 +337,8 @@
 
 	for (np = &tbl->phash_buckets[hash_val]; (n=*np) != NULL; np = &n->next) {
 		if (memcmp(n->key, pkey, key_len) == 0 && n->dev == dev) {
-			net_serialize_enter();
 			*np = n->next;
-			net_serialize_leave();
+			synchronize_bh();
 			if (tbl->pdestructor)
 				tbl->pdestructor(n);
 			kfree(n);
@@ -358,9 +357,8 @@
 		np = &tbl->phash_buckets[h]; 
 		for (np = &tbl->phash_buckets[h]; (n=*np) != NULL; np = &n->next) {
 			if (n->dev == dev || dev == NULL) {
-				net_serialize_enter();
 				*np = n->next;
-				net_serialize_leave();
+				synchronize_bh();
 				if (tbl->pdestructor)
 					tbl->pdestructor(n);
 				kfree(n);
@@ -963,9 +961,8 @@
 		return;
 	for (p = &tbl->parms.next; *p; p = &(*p)->next) {
 		if (*p == parms) {
-			net_serialize_enter();
 			*p = parms->next;
-			net_serialize_leave();
+			synchronize_bh();
 #ifdef CONFIG_SYSCTL
 			neigh_sysctl_unregister(parms);
 #endif
@@ -1014,9 +1011,8 @@
 		printk(KERN_CRIT "neighbour leakage\n");
 	for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
 		if (*tp == tbl) {
-			net_serialize_enter();
 			*tp = tbl->next;
-			net_serialize_leave();
+			synchronize_bh();
 			break;
 		}
 	}
diff -u --recursive --new-file v2.2.4/linux/net/core/sock.c linux/net/core/sock.c
--- v2.2.4/linux/net/core/sock.c	Tue Mar 23 14:35:48 1999
+++ linux/net/core/sock.c	Sun Mar 28 09:07:47 1999
@@ -7,7 +7,7 @@
  *		handler for protocols to use and generic option handler.
  *
  *
- * Version:	$Id: sock.c,v 1.77 1999/03/21 05:22:26 davem Exp $
+ * Version:	$Id: sock.c,v 1.79 1999/03/28 10:18:25 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -150,6 +150,9 @@
 		    char *optval, int optlen)
 {
 	struct sock *sk=sock->sk;
+#ifdef CONFIG_FILTER
+	struct sk_filter *filter;
+#endif
 	int val;
 	int valbool;
 	int err;
@@ -341,17 +344,11 @@
 			break;
 
 		case SO_DETACH_FILTER:
-                        if(sk->filter) {
-				struct sk_filter *filter;
-
-				filter = sk->filter;
-
-				net_serialize_enter();
+			filter = sk->filter;
+                        if(filter) {
 				sk->filter = NULL;
-				net_serialize_leave();
-				
-				if (filter)
-					sk_filter_release(sk, filter);
+				synchronize_bh();
+				sk_filter_release(sk, filter);
 				return 0;
 			}
 			return -ENOENT;
@@ -501,22 +498,16 @@
 
 void sk_free(struct sock *sk)
 {
-	if (sk->destruct)
-		sk->destruct(sk);
-
 #ifdef CONFIG_FILTER
-	if (sk->filter) {
-		sk_filter_release(sk, sk->filter);
-		sk->filter = NULL;
-	}
+	struct sk_filter *filter;
 #endif
-
-	if (atomic_read(&sk->omem_alloc))
-		printk(KERN_DEBUG "sk_free: optmem leakage (%d bytes) detected.\n", atomic_read(&sk->omem_alloc));
+	if (sk->destruct)
+		sk->destruct(sk);
 
 #ifdef CONFIG_FILTER
-	if (sk->filter) {
-		sk_filter_release(sk, sk->filter);
+	filter = sk->filter;
+	if (filter) {
+		sk_filter_release(sk, filter);
 		sk->filter = NULL;
 	}
 #endif
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.2.4/linux/net/ipv4/af_inet.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/af_inet.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.85 1999/03/21 05:22:28 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.86 1999/03/25 00:38:15 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -661,6 +661,7 @@
 		lock_sock(sk);  
 		tcp_set_state(sk, TCP_CLOSE);
 		release_sock(sk); 
+		sk->zapped = 0;
 	}
 	sock->state = SS_UNCONNECTED;
 	return sock_error(sk);
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.2.4/linux/net/ipv4/devinet.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/devinet.c	Thu Mar 25 09:23:34 1999
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.26 1999/03/21 05:22:31 davem Exp $
+ *	Version: $Id: devinet.c,v 1.27 1999/03/25 10:04:06 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -138,9 +138,8 @@
 #ifdef CONFIG_SYSCTL
 	devinet_sysctl_unregister(&in_dev->cnf);
 #endif
-	net_serialize_enter();
 	in_dev->dev->ip_ptr = NULL;
-	net_serialize_leave();
+	synchronize_bh();
 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
 	kfree(in_dev);
 }
@@ -174,9 +173,8 @@
 				ifap1 = &ifa->ifa_next;
 				continue;
 			}
-			net_serialize_enter();
 			*ifap1 = ifa->ifa_next;
-			net_serialize_leave();
+			synchronize_bh();
 
 			rtmsg_ifa(RTM_DELADDR, ifa);
 			notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa);
@@ -186,9 +184,8 @@
 
 	/* 2. Unlink it */
 
-	net_serialize_enter();
 	*ifap = ifa1->ifa_next;
-	net_serialize_leave();
+	synchronize_bh();
 
 	/* 3. Announce address deletion */
 
@@ -244,9 +241,8 @@
 	}
 
 	ifa->ifa_next = *ifap;
-	net_serialize_enter();
+	wmb();
 	*ifap = ifa;
-	net_serialize_leave();
 
 	/* Send message first, then call notifier.
 	   Notifier will trigger FIB update, so that
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.2.4/linux/net/ipv4/fib_hash.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/fib_hash.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:	$Id: fib_hash.c,v 1.7 1999/03/21 05:22:32 davem Exp $
+ * Version:	$Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -566,9 +566,8 @@
 	if (del_fp) {
 		f = *del_fp;
 		/* Unlink replaced node */
-		net_serialize_enter();
 		*del_fp = f->fn_next;
-		net_serialize_leave();
+		synchronize_bh();
 
 		if (!(f->fn_state&FN_S_ZOMBIE))
 			rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
@@ -656,9 +655,8 @@
 		rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
 
 		if (matched != 1) {
-			net_serialize_enter();
 			*del_fp = f->fn_next;
-			net_serialize_leave();
+			synchronize_bh();
 
 			if (f->fn_state&FN_S_ACCESSED)
 				rt_cache_flush(-1);
@@ -689,9 +687,8 @@
 		struct fib_info *fi = FIB_INFO(f);
 
 		if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) {
-			net_serialize_enter();
 			*fp = f->fn_next;
-			net_serialize_leave();
+			synchronize_bh();
 
 			fn_free_node(f);
 			found++;
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.2.4/linux/net/ipv4/fib_rules.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/fib_rules.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.8 1999/03/21 05:22:33 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.9 1999/03/25 10:04:23 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -101,9 +101,10 @@
 		    (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
 			if (r == &local_rule)
 				return -EPERM;
-			net_serialize_enter();
+
 			*rp = r->r_next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			if (r != &default_rule && r != &main_rule)
 				kfree(r);
 			return 0;
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.2.4/linux/net/ipv4/igmp.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/igmp.c	Thu Mar 25 09:23:34 1999
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.29 1999/03/21 05:22:36 davem Exp $
+ *	Version: $Id: igmp.c,v 1.30 1999/03/25 10:04:10 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -463,9 +463,9 @@
 	for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
 		if (i->multiaddr==addr) {
 			if (--i->users == 0) {
-				net_serialize_enter();
 				*ip = i->next;
-				net_serialize_leave();
+				synchronize_bh();
+
 				igmp_group_dropped(i);
 				if (in_dev->dev->flags & IFF_UP)
 					ip_rt_multicast_event(in_dev);
@@ -613,9 +613,10 @@
 			struct in_device *in_dev;
 			if (--iml->count)
 				return 0;
-			net_serialize_enter();
+
 			*imlp = iml->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			in_dev = inetdev_by_index(iml->multi.imr_ifindex);
 			if (in_dev)
 				ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- v2.2.4/linux/net/ipv4/ip_gre.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/ip_gre.c	Thu Mar 25 09:23:34 1999
@@ -211,10 +211,9 @@
 {
 	struct ip_tunnel **tp = ipgre_bucket(t);
 
-	net_serialize_enter();
 	t->next = *tp;
+	wmb();
 	*tp = t;
-	net_serialize_leave();
 }
 
 static void ipgre_tunnel_unlink(struct ip_tunnel *t)
@@ -223,9 +222,8 @@
 
 	for (tp = ipgre_bucket(t); *tp; tp = &(*tp)->next) {
 		if (t == *tp) {
-			net_serialize_enter();
 			*tp = t->next;
-			net_serialize_leave();
+			synchronize_bh();
 			break;
 		}
 	}
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.2.4/linux/net/ipv4/ip_output.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/ip_output.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.66 1999/03/21 05:22:41 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.67 1999/03/25 00:43:00 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -350,7 +350,7 @@
 		   into account). Actually, tcp should make it. --ANK (980801)
 		 */
 		iph->frag_off |= __constant_htons(IP_DF);
-		printk(KERN_DEBUG "sending pkt_too_big to self\n");
+		NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));
 
 		/* icmp_send is not reenterable, so that bh_atomic... --ANK */
 		start_bh_atomic();
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.2.4/linux/net/ipv4/ip_sockglue.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/ip_sockglue.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *
  *		The IP to API glue.
  *		
- * Version:	$Id: ip_sockglue.c,v 1.40 1999/03/21 05:22:42 davem Exp $
+ * Version:	$Id: ip_sockglue.c,v 1.41 1999/03/25 10:04:29 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -209,9 +209,9 @@
 					kfree(new_ra);
 				return -EADDRINUSE;
 			}
-			net_serialize_enter();
 			*rap = ra->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			if (ra->destructor)
 				ra->destructor(sk);
 			kfree(ra);
@@ -222,10 +222,11 @@
 		return -ENOBUFS;
 	new_ra->sk = sk;
 	new_ra->destructor = destructor;
+
 	new_ra->next = ra;
-	net_serialize_enter();
+	wmb();
 	*rap = new_ra;
-	net_serialize_leave();
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.2.4/linux/net/ipv4/ipconfig.c	Tue Jan 19 11:32:53 1999
+++ linux/net/ipv4/ipconfig.c	Sun Mar 28 09:07:47 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.19 1999/01/15 06:54:00 davem Exp $
+ *  $Id: ipconfig.c,v 1.20 1999/03/28 10:18:28 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
@@ -825,9 +825,9 @@
 	 */
 	if (ic_myaddr == INADDR_NONE ||
 #ifdef CONFIG_ROOT_NFS
-	     root_server_addr == INADDR_NONE ||
+	    (root_server_addr == INADDR_NONE && ic_servaddr == INADDR_NONE) ||
 #endif
-	    (ic_first_dev && ic_first_dev->next)) {
+	    ic_first_dev->next) {
 #ifdef CONFIG_IP_PNP_DYNAMIC
 		if (ic_dynamic() < 0) {
 			printk(KERN_ERR "IP-Config: Auto-configuration of network failed.\n");
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.2.4/linux/net/ipv4/ipip.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/ipip.c	Thu Mar 25 09:23:34 1999
@@ -1,7 +1,7 @@
 /*
  *	Linux NET3:	IP/IP protocol decoder. 
  *
- *	Version: $Id: ipip.c,v 1.25 1999/03/21 05:22:43 davem Exp $
+ *	Version: $Id: ipip.c,v 1.26 1999/03/25 10:04:32 davem Exp $
  *
  *	Authors:
  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
@@ -182,9 +182,8 @@
 
 	for (tp = ipip_bucket(t); *tp; tp = &(*tp)->next) {
 		if (t == *tp) {
-			net_serialize_enter();
 			*tp = t->next;
-			net_serialize_leave();
+			synchronize_bh();
 			break;
 		}
 	}
@@ -194,10 +193,9 @@
 {
 	struct ip_tunnel **tp = ipip_bucket(t);
 
-	net_serialize_enter();
 	t->next = *tp;
+	wmb();
 	*tp = t;
-	net_serialize_leave();
 }
 
 struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int create)
@@ -265,9 +263,8 @@
 static void ipip_tunnel_destroy(struct device *dev)
 {
 	if (dev == &ipip_fb_tunnel_dev) {
-		net_serialize_enter();
 		tunnels_wc[0] = NULL;
-		net_serialize_leave();
+		synchronize_bh();
 	} else {
 		ipip_tunnel_unlink((struct ip_tunnel*)dev->priv);
 		kfree(dev);
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.2.4/linux/net/ipv4/ipmr.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/ipmr.c	Thu Mar 25 09:23:34 1999
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.39 1999/03/21 05:22:44 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.40 1999/03/25 10:04:25 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -661,9 +661,10 @@
 {
 	if (sk == mroute_socket) {
 		ipv4_devconf.mc_forwarding = 0;
-		net_serialize_enter();
+
 		mroute_socket=NULL;
-		net_serialize_leave();
+		synchronize_bh();
+
 		mroute_close(sk);
 	}
 }
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.2.4/linux/net/ipv4/route.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/route.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.64 1999/03/23 21:21:13 davem Exp $
+ * Version:	$Id: route.c,v 1.65 1999/03/25 10:04:35 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -338,11 +338,11 @@
 
 	rt_deadline = 0;
 
-	net_serialize_enter();
+	start_bh_atomic();
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
 		if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
 			continue;
-		net_serialize_leave();
+		end_bh_atomic();
 
 		for (; rth; rth=next) {
 			next = rth->u.rt_next;
@@ -350,9 +350,9 @@
 			rt_free(rth);
 		}
 
-		net_serialize_enter();
+		start_bh_atomic();
 	}
-	net_serialize_leave();
+	end_bh_atomic();
 }
   
 void rt_cache_flush(int delay)
diff -u --recursive --new-file v2.2.4/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.2.4/linux/net/ipv4/tcp_ipv4.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv4/tcp_ipv4.c	Sun Mar 28 09:07:47 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.170 1999/03/21 05:22:47 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.171 1999/03/28 10:18:26 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -1305,6 +1305,9 @@
 
 	if(newsk != NULL) {
 		struct tcp_opt *newtp;
+#ifdef CONFIG_FILTER
+		struct sk_filter *filter;
+#endif
 
 		memcpy(newsk, sk, sizeof(*newsk));
 		newsk->sklist_next = NULL;
@@ -1326,8 +1329,8 @@
 		skb_queue_head_init(&newsk->back_log);
 		skb_queue_head_init(&newsk->error_queue);
 #ifdef CONFIG_FILTER
-		if (newsk->filter)
-			sk_filter_charge(newsk, newsk->filter);
+		if ((filter = newsk->filter) != NULL)
+			sk_filter_charge(newsk, filter);
 #endif
 
 		/* Now setup tcp_opt */
@@ -1559,9 +1562,9 @@
 
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
-
 #ifdef CONFIG_FILTER
-	if (sk->filter && sk_filter(skb, sk->filter))
+	struct sk_filter *filter = sk->filter;
+	if (filter && sk_filter(skb, filter))
 		goto discard;
 #endif /* CONFIG_FILTER */
 
diff -u --recursive --new-file v2.2.4/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.2.4/linux/net/ipv6/addrconf.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv6/addrconf.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.47 1999/03/21 05:22:50 davem Exp $
+ *	$Id: addrconf.c,v 1.48 1999/03/25 10:04:43 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -340,9 +340,9 @@
 
 	for (; iter; iter = iter->lst_next) {
 		if (iter == ifp) {
-			net_serialize_enter();
 			*back = ifp->lst_next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			ifp->lst_next = NULL;
 			break;
 		}
@@ -354,9 +354,9 @@
 
 	for (; iter; iter = iter->if_next) {
 		if (iter == ifp) {
-			net_serialize_enter();
 			*back = ifp->if_next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			ifp->if_next = NULL;
 			break;
 		}
diff -u --recursive --new-file v2.2.4/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.2.4/linux/net/ipv6/ipv6_sockglue.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv6/ipv6_sockglue.c	Thu Mar 25 09:23:34 1999
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.25 1999/03/21 05:22:54 davem Exp $
+ *	$Id: ipv6_sockglue.c,v 1.26 1999/03/25 10:04:53 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -86,9 +86,10 @@
 					kfree(new_ra);
 				return -EADDRINUSE;
 			}
-			net_serialize_enter();
+
 			*rap = ra->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			if (ra->destructor)
 				ra->destructor(sk);
 			kfree(ra);
diff -u --recursive --new-file v2.2.4/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.2.4/linux/net/ipv6/mcast.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv6/mcast.c	Thu Mar 25 09:23:34 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.18 1999/03/21 05:22:55 davem Exp $
+ *	$Id: mcast.c,v 1.19 1999/03/25 10:04:50 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -132,9 +132,10 @@
 		if (mc_lst->ifindex == ifindex &&
 		    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
 			struct device *dev;
-			net_serialize_enter();
+
 			*lnk = mc_lst->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			if ((dev = dev_get_by_index(ifindex)) != NULL)
 				ipv6_dev_mc_dec(dev, &mc_lst->addr);
 			sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
@@ -254,9 +255,8 @@
 		
 		for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
 			if (iter == ma) {
-				net_serialize_enter();
 				*lnk = iter->if_next;
-				net_serialize_leave();
+				synchronize_bh();
 				return;
 			}
 		}
@@ -277,9 +277,10 @@
 		if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
 			if (atomic_dec_and_test(&ma->mca_users)) {
 				igmp6_group_dropped(ma);
-				net_serialize_enter();
+
 				*lnk = ma->next;
-				net_serialize_leave();
+				synchronize_bh();
+
 				ipv6_mca_remove(dev, ma);
 				kfree(ma);
 			}
@@ -595,9 +596,8 @@
 
 		for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) {
 			if (*lnk == i) {
-				net_serialize_enter();
 				*lnk = i->next;
-				net_serialize_leave();
+				synchronize_bh();
 				break;
 			}
 		}
diff -u --recursive --new-file v2.2.4/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.2.4/linux/net/ipv6/sit.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv6/sit.c	Thu Mar 25 09:23:34 1999
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.30 1999/03/21 05:22:58 davem Exp $
+ *	$Id: sit.c,v 1.31 1999/03/25 10:04:55 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -118,9 +118,8 @@
 
 	for (tp = ipip6_bucket(t); *tp; tp = &(*tp)->next) {
 		if (t == *tp) {
-			net_serialize_enter();
 			*tp = t->next;
-			net_serialize_leave();
+			synchronize_bh();
 			break;
 		}
 	}
@@ -130,10 +129,9 @@
 {
 	struct ip_tunnel **tp = ipip6_bucket(t);
 
-	net_serialize_enter();
 	t->next = *tp;
+	wmb();
 	*tp = t;
-	net_serialize_leave();
 }
 
 struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int create)
@@ -200,9 +198,8 @@
 static void ipip6_tunnel_destroy(struct device *dev)
 {
 	if (dev == &ipip6_fb_tunnel_dev) {
-		net_serialize_enter();
 		tunnels_wc[0] = NULL;
-		net_serialize_leave();
+		synchronize_bh();
 		return;
 	} else {
 		ipip6_tunnel_unlink((struct ip_tunnel*)dev->priv);
diff -u --recursive --new-file v2.2.4/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.2.4/linux/net/ipv6/tcp_ipv6.c	Tue Mar 23 14:35:48 1999
+++ linux/net/ipv6/tcp_ipv6.c	Sun Mar 28 09:07:47 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.100 1999/03/21 05:22:59 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.101 1999/03/28 10:18:30 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -1181,6 +1181,9 @@
 
 static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_FILTER
+	struct sk_filter *filter;
+#endif
 	int users = 0;
 
 	/* Imagine: socket is IPv6. IPv4 packet arrives,
@@ -1195,7 +1198,8 @@
 		return tcp_v4_do_rcv(sk, skb);
 
 #ifdef CONFIG_FILTER
-	if (sk->filter && sk_filter(skb, sk->filter))
+	filter = sk->filter;
+	if (filter && sk_filter(skb, filter))
 		goto discard;
 #endif /* CONFIG_FILTER */
 
diff -u --recursive --new-file v2.2.4/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- v2.2.4/linux/net/netlink/af_netlink.c	Tue Mar 23 14:35:48 1999
+++ linux/net/netlink/af_netlink.c	Thu Mar 25 09:23:34 1999
@@ -747,9 +747,9 @@
 {
 	struct socket *sock = netlink_kernel[unit];
 
-	net_serialize_enter();
 	netlink_kernel[unit] = NULL;
-	net_serialize_leave();
+	synchronize_bh();
+
 	sock_release(sock);
 }
 
diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_api.c linux/net/sched/cls_api.c
--- v2.2.4/linux/net/sched/cls_api.c	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/cls_api.c	Thu Mar 25 09:23:34 1999
@@ -226,9 +226,9 @@
 
 	if (fh == 0) {
 		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
-			net_serialize_enter();
 			*back = tp->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			tp->ops->destroy(tp);
 			kfree(tp);
 			err = 0;
diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_fw.c linux/net/sched/cls_fw.c
--- v2.2.4/linux/net/sched/cls_fw.c	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/cls_fw.c	Thu Mar 25 09:23:34 1999
@@ -157,9 +157,8 @@
 		if (*fp == f) {
 			unsigned long cl;
 
-			net_serialize_enter();
 			*fp = f->next;
-			net_serialize_leave();
+			synchronize_bh();
 
 			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
@@ -207,9 +206,10 @@
 #ifdef CONFIG_NET_CLS_POLICE
 		if (tb[TCA_FW_POLICE-1]) {
 			struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
-			net_serialize_enter();
+
 			police = xchg(&f->police, police);
-			net_serialize_leave();
+			synchronize_bh();
+
 			tcf_police_release(police);
 		}
 #endif
@@ -224,9 +224,9 @@
 		if (head == NULL)
 			return -ENOBUFS;
 		memset(head, 0, sizeof(*head));
-		net_serialize_enter();
+
 		tp->root = head;
-		net_serialize_leave();
+		synchronize_bh();
 	}
 
 	f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
@@ -250,9 +250,9 @@
 #endif
 
 	f->next = head->ht[fw_hash(handle)];
-	net_serialize_enter();
+	wmb();
 	head->ht[fw_hash(handle)] = f;
-	net_serialize_leave();
+
 	*arg = (unsigned long)f;
 	return 0;
 
@@ -303,7 +303,11 @@
 
 	t->tcm_handle = f->id;
 
-	if (!f->res.classid && !f->police)
+       if (!f->res.classid
+#ifdef CONFIG_NET_CLS_POLICE
+           && !f->police
+#endif
+           )
 		return skb->len;
 
 	rta = (struct rtattr*)b;
diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_route.c linux/net/sched/cls_route.c
--- v2.2.4/linux/net/sched/cls_route.c	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/cls_route.c	Thu Mar 25 09:23:34 1999
@@ -329,9 +329,9 @@
 		if (*fp == f) {
 			unsigned long cl;
 
-			net_serialize_enter();
 			*fp = f->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			route4_reset_fastmap(head, f->id);
 
 			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
@@ -349,9 +349,9 @@
 					return 0;
 
 			/* OK, session has no flows */
-			net_serialize_enter();
 			head->table[to_hash(h)] = NULL;
-			net_serialize_leave();
+			synchronize_bh();
+
 			kfree(b);
 			return 0;
 		}
@@ -394,9 +394,10 @@
 #ifdef CONFIG_NET_CLS_POLICE
 		if (tb[TCA_ROUTE4_POLICE-1]) {
 			struct tcf_police *police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
-			net_serialize_enter();
+
 			police = xchg(&f->police, police);
-			net_serialize_leave();
+			synchronize_bh();
+
 			tcf_police_release(police);
 		}
 #endif
@@ -410,9 +411,9 @@
 		if (head == NULL)
 			return -ENOBUFS;
 		memset(head, 0, sizeof(struct route4_head));
-		net_serialize_enter();
+
 		tp->root = head;
-		net_serialize_leave();
+		synchronize_bh();
 	}
 
 	f = kmalloc(sizeof(struct route4_filter), GFP_KERNEL);
@@ -473,9 +474,9 @@
 		if (b == NULL)
 			goto errout;
 		memset(b, 0, sizeof(*b));
-		net_serialize_enter();
+
 		head->table[h1] = b;
-		net_serialize_leave();
+		synchronize_bh();
 	}
 	f->bkt = b;
 
@@ -495,9 +496,9 @@
 #endif
 
 	f->next = f1;
-	net_serialize_enter();
+	wmb();
 	*ins_f = f;
-	net_serialize_leave();
+
 	route4_reset_fastmap(head, f->id);
 	*arg = (unsigned long)f;
 	return 0;
diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_rsvp.h linux/net/sched/cls_rsvp.h
--- v2.2.4/linux/net/sched/cls_rsvp.h	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/cls_rsvp.h	Thu Mar 25 09:23:34 1999
@@ -149,7 +149,7 @@
 	struct iphdr *nhptr = skb->nh.iph;
 #endif
 
-#if !defined( __i386__) && !defined(__m68k__)
+#if !defined( __i386__) && !defined(__mc68000__)
 	if ((unsigned long)nhptr & 3)
 		return -1;
 #endif
@@ -309,9 +309,9 @@
 		if (*fp == f) {
 			unsigned long cl;
 
-			net_serialize_enter();
+
 			*fp = f->next;
-			net_serialize_leave();
+			synchronize_bh();
 
 			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
@@ -332,9 +332,9 @@
 			for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF];
 			     *sp; sp = &(*sp)->next) {
 				if (*sp == s) {
-					net_serialize_enter();
 					*sp = s->next;
-					net_serialize_leave();
+					synchronize_bh();
+
 					kfree(s);
 					return 0;
 				}
@@ -453,9 +453,10 @@
 #ifdef CONFIG_NET_CLS_POLICE
 		if (tb[TCA_RSVP_POLICE-1]) {
 			struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
-			net_serialize_enter();
+
 			police = xchg(&f->police, police);
-			net_serialize_leave();
+			synchronize_bh();
+
 			tcf_police_release(police);
 		}
 #endif
@@ -545,9 +546,9 @@
 				if (((*fp)->spi.mask&f->spi.mask) != f->spi.mask)
 					break;
 			f->next = *fp;
-			net_serialize_enter();
+			wmb();
 			*fp = f;
-			net_serialize_leave();
+
 			*arg = (unsigned long)f;
 			return 0;
 		}
@@ -569,9 +570,8 @@
 			break;
 	}
 	s->next = *sp;
-	net_serialize_enter();
+	wmb();
 	*sp = s;
-	net_serialize_leave();
 	
 	goto insert;
 
diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_u32.c linux/net/sched/cls_u32.c
--- v2.2.4/linux/net/sched/cls_u32.c	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/cls_u32.c	Thu Mar 25 09:23:34 1999
@@ -114,7 +114,7 @@
 	int sel = 0;
 	int i;
 
-#if !defined(__i386__) && !defined(__m68k__)
+#if !defined(__i386__) && !defined(__mc68000__)
 	if ((unsigned long)ptr & 3)
 		return -1;
 #endif
@@ -326,9 +326,8 @@
 	if (ht) {
 		for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) {
 			if (*kp == key) {
-				net_serialize_enter();
 				*kp = key->next;
-				net_serialize_leave();
+				synchronize_bh();
 
 				u32_destroy_key(tp, key);
 				return 0;
@@ -346,9 +345,9 @@
 
 	for (h=0; h<=ht->divisor; h++) {
 		while ((n = ht->ht[h]) != NULL) {
-			net_serialize_enter();
 			ht->ht[h] = n->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			u32_destroy_key(tp, n);
 		}
 	}
@@ -466,9 +465,8 @@
 			ht_down->refcnt++;
 		}
 
-		net_serialize_enter();
 		ht_down = xchg(&n->ht_down, ht_down);
-		net_serialize_leave();
+		synchronize_bh();
 
 		if (ht_down)
 			ht_down->refcnt--;
@@ -484,9 +482,10 @@
 #ifdef CONFIG_NET_CLS_POLICE
 	if (tb[TCA_U32_POLICE-1]) {
 		struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1], est);
-		net_serialize_enter();
+
 		police = xchg(&n->police, police);
-		net_serialize_leave();
+		synchronize_bh();
+
 		tcf_police_release(police);
 	}
 #endif
@@ -588,10 +587,11 @@
 		for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next)
 			if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle))
 				break;
-		net_serialize_enter();
+
 		n->next = *ins;
+		wmb();
 		*ins = n;
-		net_serialize_leave();
+
 		*arg = (unsigned long)n;
 		return 0;
 	}
diff -u --recursive --new-file v2.2.4/linux/net/sched/estimator.c linux/net/sched/estimator.c
--- v2.2.4/linux/net/sched/estimator.c	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/estimator.c	Thu Mar 25 09:23:34 1999
@@ -171,9 +171,10 @@
 				pest = &est->next;
 				continue;
 			}
-			net_serialize_enter();
+
 			*pest = est->next;
-			net_serialize_leave();
+			synchronize_bh();
+
 			kfree(est);
 			killed++;
 		}
diff -u --recursive --new-file v2.2.4/linux/net/sched/sch_prio.c linux/net/sched/sch_prio.c
--- v2.2.4/linux/net/sched/sch_prio.c	Tue Mar 23 14:35:48 1999
+++ linux/net/sched/sch_prio.c	Thu Mar 25 09:23:34 1999
@@ -195,9 +195,9 @@
 			struct Qdisc *child;
 			child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
 			if (child) {
-				net_serialize_enter();
 				child = xchg(&q->queues[band], child);
-				net_serialize_leave();
+				synchronize_bh();
+
 				if (child != &noop_qdisc)
 					qdisc_destroy(child);
 			}
diff -u --recursive --new-file v2.2.4/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.2.4/linux/net/unix/af_unix.c	Tue Mar 23 14:35:48 1999
+++ linux/net/unix/af_unix.c	Thu Mar 25 09:23:34 1999
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.74 1999/03/21 05:23:16 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.75 1999/03/22 05:02:45 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
Binary files v2.2.4/linux/scripts/ksymoops/ksymoops and linux/scripts/ksymoops/ksymoops differ
diff -u --recursive --new-file v2.2.4/linux/scripts/ksymoops/oops.c linux/scripts/ksymoops/oops.c
--- v2.2.4/linux/scripts/ksymoops/oops.c	Tue Mar 23 14:35:48 1999
+++ linux/scripts/ksymoops/oops.c	Wed Mar 24 16:07:59 1999
@@ -288,9 +288,11 @@
 		errno = 0;
 		eip_relative = strtoul(string[5], NULL, 16);
 		if (errno) {
+#if 0
 			/* Try strtoull also, e.g. sparc binutils print <_PC+0xfffffffffffffd58> */
 			errno = 0;
 			eip_relative = strtoull(string[5], NULL, 16);
+#endif
 			if (errno) {
 				fprintf(stderr,
 					"%s Invalid hex value in objdump line, "
