diff -u --recursive --new-file v2.1.6/linux/CREDITS linux/CREDITS
--- v2.1.6/linux/CREDITS	Tue Oct 29 19:58:01 1996
+++ linux/CREDITS	Fri Nov  1 13:04:40 1996
@@ -668,7 +668,7 @@
 S: USA
 
 N: Bernhard Kaindl
-E: bartelt@computerhaus.at
+E: edv@bartelt.via.at
 D: Author of a menu based configuration tool, kmenu, which 
 D: is the predecessor of 'make menuconfig' and 'make xconfig'.
 S: Tallak 95
@@ -1161,7 +1161,8 @@
 
 N: Thomas Sailer
 E: sailer@ife.ee.ethz.ch
-D: Baycom radio modem driver
+E: HB9JNX@HB9W.CHE.EU (packet radio)
+D: Baycom and Soundcard radio modem driver
 S: Weinbergstrasse 76
 S: 8408 Winterthur
 S: Switzerland
diff -u --recursive --new-file v2.1.6/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.6/linux/Documentation/Configure.help	Tue Oct 29 19:58:01 1996
+++ linux/Documentation/Configure.help	Wed Oct 30 14:15:11 1996
@@ -1875,17 +1875,29 @@
   running kernel whenever you want), say M here and read
   Documentation/modules.txt.
 
-BAYCOM ser12 and par96 kiss emulation driver for AX.25
+BAYCOM ser12 and par96 driver for AX.25
 CONFIG_BAYCOM
   This is an experimental driver for Baycom style simple amateur radio
   modems that connect to either a serial interface or a parallel
   interface. The driver supports the ser12 and par96 designs. To
-  configure the driver, use the setbaycom utility available from
-  http://www.ife.ee.ethz.ch/~sailer/ham/ham.html#lnxbay. For
-  information on the modems, see http://www.baycom.de and
-  drivers/char/README.baycom.  If you want to compile this as a module
-  ( = code which can be inserted in and removed from the running
-  kernel whenever you want), say M here and read
+  configure the driver, use the sethdlc utility available
+  in the standard ax25 utilities package. For information on the modems,
+  see http://www.baycom.de and drivers/net/README.baycom.
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. This is recommended.
+
+Soundcard modem driver for AX.25
+CONFIG_SOUNDMODEM
+  This experimental driver allows a standard SoundBlaster or 
+  WindowsSoundSystem compatible soundcard to be used as a packet radio
+  modem. To configure the driver, use the sethdlc, smdiag and smmixer
+  utilities available in the standard ax25 utilities package. For
+  informations on how to key the transmitter, see
+  http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html and
+  drivers/net/README.soundmodem. If you want to compile this as a
+  module ( = code which can be inserted in and removed from the
+  running kernel whenever you want), say M here and read
   Documentation/modules.txt. This is recommended.
 
 PLIP (parallel port) support
diff -u --recursive --new-file v2.1.6/linux/Makefile linux/Makefile
--- v2.1.6/linux/Makefile	Tue Oct 29 19:58:01 1996
+++ linux/Makefile	Tue Oct 29 19:27:13 1996
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 6
+SUBLEVEL = 7
 
 ARCH = i386
 
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.6/linux/arch/alpha/kernel/entry.S	Wed Oct 16 10:48:06 1996
+++ linux/arch/alpha/kernel/entry.S	Wed Oct 30 18:41:43 1996
@@ -14,7 +14,7 @@
 #define osf_vfork sys_fork
 
 /*
- * These offsets must match with "struct hae" in io.h:	
+ * These offsets must match with "struct hae" in io.h:
  */
 #define HAE_CACHE	0
 #define HAE_REG		8
@@ -142,9 +142,29 @@
 .ent	entMM
 entMM:
 	SAVE_ALL
-	lda	$27,do_page_fault
-	lda	$26,ret_from_sys_call
-	jsr	$31,($27),do_page_fault
+/* save $9 - $15 so the inline exception code can manipulate them.  */
+	subq	$30,56,$30
+	stq	$9,0($30)
+	stq	$10,8($30)
+	stq	$11,16($30)
+	stq	$12,24($30)
+	stq	$13,32($30)
+	stq	$14,40($30)
+	stq	$15,48($30)
+	addq	$30,56,$19
+/* handle the fault */
+	jsr	$26,do_page_fault
+/* reload the registers after the exception code played.  */
+	ldq	$9,0($30)
+	ldq	$10,8($30)
+	ldq	$11,16($30)
+	ldq	$12,24($30)
+	ldq	$13,32($30)
+	ldq	$14,40($30)
+	ldq	$15,48($30)
+	addq	$30,56,$30
+/* finish up the syscall as normal.  */
+	br	ret_from_sys_call
 .end entMM
 
 .align 3
@@ -190,7 +210,7 @@
 	SAVE_ALL
 	lda $27,sys_clone
 	jsr $26,($27),sys_clone
-	stq $0,0($30)	
+	stq $0,0($30)
 	br $31,ret_from_sys_call
 .end	kernel_clone
 
@@ -424,7 +444,7 @@
 
 /*
  * A fork is the same as clone(SIGCHLD, 0);
-*/
+ */
 .align 3
 .globl	sys_fork
 .ent	sys_fork
@@ -508,7 +528,7 @@
 	ldq	$4,0($2)
 	addq	$1,1,$1
 	and	$3,$4,$2
-	bne	$2,handle_bottom_half	
+	bne	$2,handle_bottom_half
 ret_from_handle_bh:
 	ldq	$0,SP_OFF($30)
 	and	$0,8,$0
@@ -529,9 +549,9 @@
 restore_all:
 	RESTORE_ALL
 	rti
-	
-	
-/* PTRACE syscall handler */	
+
+
+/* PTRACE syscall handler */
 .align 3
 strace:
 	/* set up signal stack, call syscall_trace */
@@ -588,7 +608,7 @@
 	lda     $27,syscall_trace
 	jsr     $26,($27),syscall_trace
 	bis	$9,$9,$19
-	bis	$10,$10,$20	
+	bis	$10,$10,$20
 	bsr	$1,undo_switch_stack
 
 	bis	$31,$31,$26	/* tell "ret_from_sys_call" that we can restart */
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
--- v2.1.6/linux/arch/alpha/kernel/traps.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/alpha/kernel/traps.c	Thu Oct 31 13:59:58 1996
@@ -178,63 +178,217 @@
 	unsigned long count, va, pc;
 } unaligned[2];
 
+
+/* Macro for exception fixup code to access integer registers.  */
+#define una_reg(r)  (regs.regs[(r) >= 16 && (r) <= 18 ? (r)+19 : (r)])
+
+
 asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg,
 	unsigned long a3, unsigned long a4, unsigned long a5,
 	struct allregs regs)
 {
 	static int cnt = 0;
 	static long last_time = 0;
+	long error, tmp1, tmp2, tmp3, tmp4;
+	unsigned long pc = regs.pc - 4;
+	unsigned fixup;
 
 	if (cnt >= 5 && jiffies - last_time > 5*HZ) {
 		cnt = 0;
 	}
 	if (++cnt < 5) {
 		printk("kernel: unaligned trap at %016lx: %p %lx %ld\n",
-		       regs.pc - 4, va, opcode, reg);
+		       pc, va, opcode, reg);
 	}
 	last_time = jiffies;
 
-	++unaligned[0].count;
-	unaligned[0].va = (unsigned long) va - 4;
-	unaligned[0].pc = regs.pc;
-
-	/* $16-$18 are PAL-saved, and are offset by 19 entries */
-	if (reg >= 16 && reg <= 18)
-		reg += 19;
-
-	{
-		/* Set up an exception handler address just in case we are
-		   handling an unaligned fixup within get_user().  Notice
-		   that we do *not* change the exception count because we
-		   only want to bounce possible exceptions on through.  */
-
-		__label__ handle_ex;
-		register void *ex_vector __asm__("$28");
-		__asm__ __volatile__ ("" : "=r"(ex_vector) : "0"(&&handle_ex));
-
-		switch (opcode) {
-		case 0x28: /* ldl */
-			*(reg+regs.regs) = get_unaligned((int *)va);
-			return;
-		case 0x29: /* ldq */
-			*(reg+regs.regs) = get_unaligned((long *)va);
-			return;
-		case 0x2c: /* stl */
-			put_unaligned(*(reg+regs.regs), (int *)va);
-			return;
-		case 0x2d: /* stq */
-			put_unaligned(*(reg+regs.regs), (long *)va);
-			return;
-
-		/* We'll only get back here if we are handling a
-		   valid exception.  */
-		handle_ex:
-			(&regs)->pc = *(28+regs.regs);
-			return;
-		}
+	unaligned[0].count++;
+	unaligned[0].va = (unsigned long) va;
+	unaligned[0].pc = pc;
+
+	/* We don't want to use the generic get/put unaligned macros as
+	   we want to trap exceptions.  Only if we actually get an
+	   exception will we decide whether we should have caught it.  */
+
+	switch (opcode) {
+#ifdef __HAVE_CPU_BWX
+	case 0x0c: /* ldwu */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,1(%3)\n"
+		"	extwl %1,%3,%1\n"
+		"	extwh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".text"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto got_exception;
+		una_reg(reg) = tmp1|tmp2;
+		return;
+#endif
+
+	case 0x28: /* ldl */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,3(%3)\n"
+		"	extll %1,%3,%1\n"
+		"	extlh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".text"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto got_exception;
+		una_reg(reg) = (int)(tmp1|tmp2);
+		return;
+
+	case 0x29: /* ldq */
+		__asm__ __volatile__(
+		"1:	ldq_u %1,0(%3)\n"
+		"2:	ldq_u %2,7(%3)\n"
+		"	extql %1,%3,%1\n"
+		"	extqh %2,%3,%2\n"
+		"3:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %1,3b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %2,3b-2b(%0)\n"
+		".text"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2)
+			: "r"(va), "0"(0));
+		if (error)
+			goto got_exception;
+		una_reg(reg) = tmp1|tmp2;
+		return;
+
+	/* Note that the store sequences do not indicate that they change
+	   memory because it _should_ be affecting nothing in this context.
+	   (Otherwise we have other, much larger, problems.)  */
+#ifdef __HAVE_CPU_BWX
+	case 0x0d: /* stw */
+		__asm__ __volatile__(
+		"1:	ldq_u %2,1(%5)\n"
+		"2:	ldq_u %1,0(%5)\n"
+		"	inswh %6,%5,%4\n"
+		"	inswl %6,%5,%3\n"
+		"	mskwh %2,%5,%2\n"
+		"	mskwl %1,%5,%1\n"
+		"	or %2,%4,%2\n"
+		"	or %1,%3,%1\n"
+		"3:	stq_u %2,1(%5)\n"
+		"4:	stq_u %1,0(%5)\n"
+		"5:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %2,5b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %1,5b-2b(%0)\n"
+		"	.gprel32 3b\n"
+		"	lda $31,5b-3b(%0)\n"
+		"	.gprel32 4b\n"
+		"	lda $31,5b-4b(%0)\n"
+		".text"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2),
+			  "=&r"(tmp3), "=&r"(tmp4)
+			: "r"(va), "r"(una_reg(reg)), "0"(0));
+		if (error)
+			goto got_exception;
+		return;
+#endif
+
+	case 0x2c: /* stl */
+		__asm__ __volatile__(
+		"1:	ldq_u %2,3(%5)\n"
+		"2:	ldq_u %1,0(%5)\n"
+		"	inslh %6,%5,%4\n"
+		"	insll %6,%5,%3\n"
+		"	msklh %2,%5,%2\n"
+		"	mskll %1,%5,%1\n"
+		"	or %2,%4,%2\n"
+		"	or %1,%3,%1\n"
+		"3:	stq_u %2,3(%5)\n"
+		"4:	stq_u %1,0(%5)\n"
+		"5:\n"
+		".section __ex_table,\"a\"\n"
+		"	.gprel32 1b\n"
+		"	lda %2,5b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %1,5b-2b(%0)\n"
+		"	.gprel32 3b\n"
+		"	lda $31,5b-3b(%0)\n"
+		"	.gprel32 4b\n"
+		"	lda $31,5b-4b(%0)\n"
+		".text"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2),
+			  "=&r"(tmp3), "=&r"(tmp4)
+			: "r"(va), "r"(una_reg(reg)), "0"(0));
+		if (error)
+			goto got_exception;
+		return;
+
+	case 0x2d: /* stq */
+		__asm__ __volatile__(
+		"1:	ldq_u %2,7(%5)\n"
+		"2:	ldq_u %1,0(%5)\n"
+		"	insqh %6,%5,%4\n"
+		"	insql %6,%5,%3\n"
+		"	mskqh %2,%5,%2\n"
+		"	mskql %1,%5,%1\n"
+		"	or %2,%4,%2\n"
+		"	or %1,%3,%1\n"
+		"3:	stq_u %2,7(%5)\n"
+		"4:	stq_u %1,0(%5)\n"
+		"5:\n"
+		".section __ex_table,\"a\"\n\t"
+		"	.gprel32 1b\n"
+		"	lda %2,5b-1b(%0)\n"
+		"	.gprel32 2b\n"
+		"	lda %1,5b-2b(%0)\n"
+		"	.gprel32 3b\n"
+		"	lda $31,5b-3b(%0)\n"
+		"	.gprel32 4b\n"
+		"	lda $31,5b-4b(%0)\n"
+		".text"
+			: "=r"(error), "=&r"(tmp1), "=&r"(tmp2),
+			  "=&r"(tmp3), "=&r"(tmp4)
+			: "r"(va), "r"(una_reg(reg)), "0"(0));
+		if (error)
+			goto got_exception;
+		return;
 	}
 	printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n",
-		regs.pc, va, opcode, reg);
+		pc, va, opcode, reg);
+	do_exit(SIGSEGV);
+	return;
+
+got_exception:
+	/* Ok, we caught the exception, but we don't want it.  Is there
+	   someone to pass it along to?  */
+	if ((fixup = search_exception_table(pc)) != 0) {
+		unsigned long newpc;
+		newpc = fixup_exception(una_reg, fixup, pc);
+		printk("Forwarding unaligned exception at %lx (%lx)\n",
+		       pc, newpc);
+		(&regs)->pc = newpc;
+		return;
+	}
+
+	/* Yikes!  No one to forward the exception to.  */
+	printk("%s: unhandled unaligned exception at pc=%lx ra=%lx"
+	       " (bad address = %p)\n", current->comm,
+	       pc, una_reg(26), va);
 	do_exit(SIGSEGV);
 }
 
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/lib/clear_user.S linux/arch/alpha/lib/clear_user.S
--- v2.1.6/linux/arch/alpha/lib/clear_user.S	Sat Oct 19 10:07:28 1996
+++ linux/arch/alpha/lib/clear_user.S	Thu Oct 31 13:59:59 1996
@@ -25,6 +25,14 @@
  *	$1,$2,$3,$4,$5,$6
  */
 
+/* Allow an exception for an insn; exit if we get one.  */
+#define EX(x,y...)			\
+	99: x,##y;			\
+	.section __ex_table,"a";	\
+	.gprel32 99b;			\
+	lda $31, $exception-99b($31); 	\
+	.text
+
 	.set noat
 	.set noreorder
 	.align 4
@@ -38,7 +46,7 @@
 	and	$1, 3, $4	# e0    :
 	beq	$4, 1f		# .. e1 :
 
-0:	stq_u	$31, 0($6)	# e0    : zero one word
+0:	EX( stq_u $31, 0($6) )	# e0    : zero one word
 	subq	$0, 8, $0	# .. e1 :
 	subq	$4, 1, $4	# e0    :
 	addq	$6, 8, $6	# .. e1 :
@@ -48,13 +56,13 @@
 1:	bic	$1, 3, $1	# e0    :
 	beq	$1, $tail	# .. e1 :
 
-2:	stq_u	$31, 0($6)	# e0    : zero four words
+2:	EX( stq_u $31, 0($6) )	# e0    : zero four words
 	subq	$0, 8, $0	# .. e1 :
-	stq_u	$31, 8($6)	# e0    :
+	EX( stq_u $31, 8($6) )	# e0    :
 	subq	$0, 8, $0	# .. e1 :
-	stq_u	$31, 16($6)	# e0    :
+	EX( stq_u $31, 16($6) )	# e0    :
 	subq	$0, 8, $0	# .. e1 :
-	stq_u	$31, 24($6)	# e0    :
+	EX( stq_u $31, 24($6) )	# e0    :
 	subq	$0, 8, $0	# .. e1 :
 	subq	$1, 4, $1	# e0    :
 	addq	$6, 32, $6	# .. e1 :
@@ -62,35 +70,29 @@
 
 $tail:
 	bne	$2, 1f		# e1    : is there a tail to do?
-
-	stq	$3, 0($7)	# e0    : decrement exception count
 	ret	$31, ($28), 1	# .. e1 :
 
-1:	ldq_u	$5, 0($6)	# e1    :
-	mskqh	$5, $0, $5	# e0    :
-	stq_u	$5, 0($6)	# e0    :
+1:	EX( ldq_u $5, 0($6) )	# e0    :
 	clr	$0		# .. e1 :
-	stq	$3, 0($7)	# e0    : decrement exception count
+	nop			# e1    :
+	mskqh	$5, $0, $5	# e0    :
+	EX( stq_u $5, 0($6) )	# e0    :
 	ret	$31, ($28), 1	# .. e1 :
 
 __clear_user:
-	ldq	$3, 0($7)	# e0    : load exception count for increment
-	beq	$0, $zerolength # .. e1 :
 	and	$6, 7, $4	# e0    : find dest misalignment
-	addq	$0, $4, $1	# e1    : bias counter
-	addq	$3, 1, $5	# e0    :
-	and	$1, 7, $2	# .. e1 : number of bytes in tail
+	beq	$0, $zerolength # .. e1 :
+	addq	$0, $4, $1	# e0    : bias counter
+	and	$1, 7, $2	# e1    : number of bytes in tail
 	srl	$1, 3, $1	# e0    :
-	unop			#       :
-	stq	$5, 0($7)	# e0    : increment exception count
 	beq	$4, $loop	# .. e1 :
 
-	ldq_u	$5, 0($6)	# e0    : load dst word to mask back in
+	EX( ldq_u $5, 0($6) )	# e0    : load dst word to mask back in
 	beq	$1, $oneword	# .. e1 : sub-word store?
 
 	mskql	$5, $6, $5	# e0    : take care of misaligned head
 	addq	$6, 8, $6	# .. e1 :
-	stq_u	$5, -8($6)	# e0    :
+	EX( stq_u $5, -8($6) )	# e0    :
 	addq	$0, $4, $0	# .. e1 : bytes left -= 8 - misalignment
 	subq	$1, 1, $1	# e0    :
 	subq	$0, 8, $0	# .. e1 :
@@ -101,11 +103,11 @@
 	mskql	$5, $6, $4	# e0    :
 	mskqh	$5, $2, $5	# e0    :
 	or	$5, $4, $5	# e1    :
-	stq_u	$5, 0($6)	# e0    :
+	EX( stq_u $5, 0($6) )	# e0    :
 	clr	$0		# .. e1 :
-	stq	$3, 0($7)	# e0    : decrement exception count
 
 $zerolength:
+$exception:
 	ret	$31, ($28), 1	# .. e1 :
 
 	.end __clear_user
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/lib/copy_user.S linux/arch/alpha/lib/copy_user.S
--- v2.1.6/linux/arch/alpha/lib/copy_user.S	Wed Oct 16 10:48:06 1996
+++ linux/arch/alpha/lib/copy_user.S	Thu Oct 31 13:59:59 1996
@@ -18,7 +18,7 @@
  *	destination address in $6
  *	source address in $7
  *	exception pointer in $8
- *	return address in $28 (exceptions expect it there)
+ *	return address in $28
  *
  * Outputs:
  *	bytes left to copy in $0
@@ -27,28 +27,33 @@
  *	$1,$2,$3,$4,$5,$6,$7
  */
 
+/* Allow an exception for an insn; exit if we get one.  */
+#define EX(x,y...)			\
+	99: x,##y;			\
+	.section __ex_table,"a";	\
+	.gprel32 99b;			\
+	lda $31, $exit-99b($31);	\
+	.text
+
 	.set noat
 	.align 3
 	.globl __copy_user
 	.ent __copy_user
 __copy_user:
-	ldq $5,0($8)
-	beq $0,$35
 	and $6,7,$3
-	addq $5,1,$1
-	stq $1,0($8)
+	beq $0,$35
 	beq $3,$36
 	subq $3,8,$3
 	.align 5
 $37:
-	ldq_u $1,0($7)
-	ldq_u $2,0($6)
+	EX( ldq_u $1,0($7) )
+	EX( ldq_u $2,0($6) )
 	extbl $1,$7,$1
 	mskbl $2,$6,$2
 	insbl $1,$6,$1
 	addq $3,1,$3
 	bis $1,$2,$1
-	stq_u $1,0($6)
+	EX( stq_u $1,0($6) )
 	subq $0,1,$0
 	addq $6,1,$6
 	addq $7,1,$7
@@ -59,10 +64,10 @@
 	bic $0,7,$4
 	beq $1,$43
 	beq $4,$48
-	ldq_u $3,0($7)
+	EX( ldq_u $3,0($7) )
 	.align 5
 $50:
-	ldq_u $2,8($7)
+	EX( ldq_u $2,8($7) )
 	subq $4,8,$4
 	extql $3,$7,$3
 	extqh $2,$7,$1
@@ -77,13 +82,13 @@
 	beq $0,$41
 	.align 5
 $57:
-	ldq_u $1,0($7)
-	ldq_u $2,0($6)
+	EX( ldq_u $1,0($7) )
+	EX( ldq_u $2,0($6) )
 	extbl $1,$7,$1
 	mskbl $2,$6,$2
 	insbl $1,$6,$1
 	bis $1,$2,$1
-	stq_u $1,0($6)
+	EX( stq_u $1,0($6) )
 	subq $0,1,$0
 	addq $6,1,$6
 	addq $7,1,$7
@@ -93,9 +98,8 @@
 $43:
 	beq $4,$65
 	.align 5
-	.align 5
 $66:
-	ldq $1,0($7)
+	EX( ldq $1,0($7) )
 	subq $4,8,$4
 	stq $1,0($6)
 	addq $7,8,$7
@@ -104,15 +108,15 @@
 	bne $4,$66
 $65:
 	beq $0,$41
-	ldq $2,0($7)
-	ldq $1,0($6)
+	EX( ldq $2,0($7) )
+	EX( ldq $1,0($6) )
 	mskql $2,$0,$2
 	mskqh $1,$0,$1
 	bis $2,$1,$2
-	stq $2,0($6)
+	EX( stq $2,0($6) )
 	bis $31,$31,$0
 $41:
-	stq $5,0($8)
 $35:
+$exit:
 	ret $31,($28),1
 	.end __copy_user
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/lib/strncpy_from_user.S linux/arch/alpha/lib/strncpy_from_user.S
--- v2.1.6/linux/arch/alpha/lib/strncpy_from_user.S	Sat Oct 19 10:07:28 1996
+++ linux/arch/alpha/lib/strncpy_from_user.S	Thu Oct 31 13:59:59 1996
@@ -6,59 +6,334 @@
  *
  * -EFAULT       if an exception occurs before the terminator is copied.
  * N             if the buffer filled.
- * 
- * Otherwise the length of the string is returned.
  *
- * Additionally, the fourth argument should be `&current->tss.ex'.
+ * Otherwise the length of the string is returned.
  */
 
+
 #include <asm/errno.h>
+#include <alpha/regdef.h>
+
+
+/* Allow an exception for an insn; exit if we get one.  */
+#define EX(x,y...)			\
+	99: x,##y;			\
+	.section __ex_table,"a";	\
+	.gprel32 99b;			\
+	lda $31, $exception-99b($0); 	\
+	.text
+
 
 	.set noat
 	.set noreorder
-
 	.text
 
-	.align 3
 	.globl __strncpy_from_user
 	.ent __strncpy_from_user
+	.frame $30, 0, $26
+
+	.align 3
+$aligned:
+	/* On entry to this basic block:
+	   t0 == the first destination word for masking back in
+	   t1 == the first source word.  */
+
+	/* Create the 1st output word and detect 0's in the 1st input word.  */
+	lda	t2, -1		# e1    : build a mask against false zero
+	mskqh	t2, a1, t2	# e0    :   detection in the src word
+	mskqh	t1, a1, t3	# e0    :
+	ornot	t1, t2, t2	# .. e1 :
+	mskql	t0, a1, t0	# e0    : assemble the first output word
+	cmpbge	zero, t2, t7	# .. e1 : bits set iff null found
+	or	t0, t3, t0	# e0    :
+	beq	a2, $a_eoc	# .. e1 :
+	bne	t7, $a_eos	# .. e1 :
+
+	/* On entry to this basic block:
+	   t0 == a source word not containing a null.  */
+
+$a_loop:
+	stq_u	t0, 0(a0)	# e0    :
+	addq	a0, 8, a0	# .. e1 :
+	EX( ldq_u t0, 0(a1) )	# e0    :
+	addq	a1, 8, a1	# .. e1 :
+	subq	a2, 1, a2	# e0    :
+	cmpbge	zero, t0, t7	# .. e1 (stall)
+	beq	a2, $a_eoc      # e1    :
+	beq	t7, $a_loop	# e1    :
+
+	/* Take care of the final (partial) word store.  At this point
+	   the end-of-count bit is set in t7 iff it applies.
+
+	   On entry to this basic block we have:
+	   t0 == the source word containing the null
+	   t7 == the cmpbge mask that found it.  */
+
+$a_eos:
+	negq	t7, t8		# e0    : find low bit set
+	and	t7, t8, t8	# e1 (stall)
+
+	/* For the sake of the cache, don't read a destination word
+	   if we're not going to need it.  */
+	and	t8, 0x80, t6	# e0    :
+	bne	t6, 1f		# .. e1 (zdb)
+
+	/* We're doing a partial word store and so need to combine
+	   our source and original destination words.  */
+	ldq_u	t1, 0(a0)	# e0    :
+	subq	t8, 1, t6	# .. e1 :
+	or	t8, t6, t7	# e0    :
+	unop			#
+	zapnot	t0, t7, t0	# e0    : clear src bytes > null
+	zap	t1, t7, t1	# .. e1 : clear dst bytes <= null
+	or	t0, t1, t0	# e1    :
+
+1:	stq_u	t0, 0(a0)
+	br	$finish_up
+
+	/* Add the end-of-count bit to the eos detection bitmask.  */
+$a_eoc:
+	or	t10, t7, t7
+	br	$a_eos
 
+	/*** The Function Entry Point ***/
+	.align 3
 __strncpy_from_user:
-	.frame $30, 0, $26
-	.prologue 0
 
-	ldq	$20, 0($19)
-	beq	$18, 9f
-	br	$28, 1f		# set up exception return address
+	mov	a0, v0		# save the string start
+	beq	a2, $zerolength
 
-	lda	$0, -EFAULT
-	ret
+	/* Are source and destination co-aligned?  */
+	xor	a0, a1, t1	# e0    :
+	and	a0, 7, t0	# .. e1 : find dest misalignment
+	and	t1, 7, t1	# e0    :
+	addq	a2, t0, a2	# .. e1 : bias count by dest misalignment
+	subq	a2, 1, a2	# e0    :
+	and	a2, 7, t2	# e1    :
+	srl	a2, 3, a2	# e0    : a2 = loop counter = (count - 1)/8
+	addq	zero, 1, t10	# .. e1 :
+	sll	t10, t2, t10	# e0    : t10 = bitmask of last count byte
+	bne	t1, $unaligned	# .. e1 :
+
+	/* We are co-aligned; take care of a partial first word.  */
+
+	EX( ldq_u t1, 0(a1) )	# e0    : load first src word
+	addq	a1, 8, a1	# .. e1 :
+
+	beq	t0, $aligned	# avoid loading dest word if not needed
+	ldq_u	t0, 0(a0)	# e0    :
+	br	$aligned	# .. e1 :
+
+
+/* The source and destination are not co-aligned.  Align the destination
+   and cope.  We have to be very careful about not reading too much and
+   causing a SEGV.  */
+
+	.align 3
+$u_head:
+	/* We know just enough now to be able to assemble the first
+	   full source word.  We can still find a zero at the end of it
+	   that prevents us from outputting the whole thing.
+
+	   On entry to this basic block:
+	   t0 == the first dest word, unmasked
+	   t1 == the shifted low bits of the first source word
+	   t6 == bytemask that is -1 in dest word bytes */
+
+	EX( ldq_u t2, 8(a1) )	# e0    : load second src word
+	addq	a1, 8, a1	# .. e1 :
+	mskql	t0, a0, t0	# e0    : mask trailing garbage in dst
+	extqh	t2, a1, t4	# e0    :
+	or	t1, t4, t1	# e1    : first aligned src word complete
+	mskqh	t1, a0, t1	# e0    : mask leading garbage in src
+	or	t0, t1, t0	# e0    : first output word complete
+	or	t0, t6, t6	# e1    : mask original data for zero test
+	cmpbge	zero, t6, t7	# e0    :
+	beq	a2, $u_eocfin	# .. e1 :
+	bne	t7, $u_final	# e1    :
+
+	lda	t6, -1			# e1    : mask out the bits we have
+	mskql	t6, a1, t6		# e0    :   already seen
+	stq_u	t0, 0(a0)		# e0    : store first output word
+	or      t6, t2, t2		# .. e1 :
+	cmpbge	zero, t2, t7		# e0    : find nulls in second partial
+	addq	a0, 8, a0		# .. e1 :
+	subq	a2, 1, a2		# e0    :
+	bne	t7, $u_late_head_exit	# .. e1 :
+
+	/* Finally, we've got all the stupid leading edge cases taken care
+	   of and we can set up to enter the main loop.  */
+
+	extql	t2, a1, t1	# e0    : position hi-bits of lo word
+	EX( ldq_u t2, 8(a1) )	# .. e1 : read next high-order source word
+	addq	a1, 8, a1	# e0    :
+	cmpbge	zero, t2, t7	# e1 (stall)
+	beq	a2, $u_eoc	# e1    :
+	bne	t7, $u_eos	# e1    :
+
+	/* Unaligned copy main loop.  In order to avoid reading too much,
+	   the loop is structured to detect zeros in aligned source words.
+	   This has, unfortunately, effectively pulled half of a loop
+	   iteration out into the head and half into the tail, but it does
+	   prevent nastiness from accumulating in the very thing we want
+	   to run as fast as possible.
+
+	   On entry to this basic block:
+	   t1 == the shifted high-order bits from the previous source word
+	   t2 == the unshifted current source word
+
+	   We further know that t2 does not contain a null terminator.  */
+
+	.align 3
+$u_loop:
+	extqh	t2, a1, t0	# e0    : extract high bits for current word
+	addq	a1, 8, a1	# .. e1 :
+	extql	t2, a1, t3	# e0    : extract low bits for next time
+	addq	a0, 8, a0	# .. e1 :
+	or	t0, t1, t0	# e0    : current dst word now complete
+	EX( ldq_u t2, 0(a1) )	# .. e1 : load high word for next time
+	stq_u	t0, -8(a0)	# e0    : save the current word
+	mov	t3, t1		# .. e1 :
+	subq	a2, 1, a2	# e0    :
+	cmpbge	zero, t2, t7	# .. e1 : test new word for eos
+	beq	a2, $u_eoc	# e1    :
+	beq	t7, $u_loop	# e1    :
+
+	/* We've found a zero somewhere in the source word we just read.
+	   If it resides in the lower half, we have one (probably partial)
+	   word to write out, and if it resides in the upper half, we
+	   have one full and one partial word left to write out.
+
+	   On entry to this basic block:
+	   t1 == the shifted high-order bits from the previous source word
+	   t2 == the unshifted current source word.  */
+$u_eos:
+	extqh	t2, a1, t0	# e0    :
+	or	t0, t1, t0	# e1    : first (partial) source word complete
+
+	cmpbge	zero, t0, t7	# e0    : is the null in this first bit?
+	bne	t7, $u_final	# .. e1 (zdb)
+
+	stq_u	t0, 0(a0)	# e0    : the null was in the high-order bits
+	addq	a0, 8, a0	# .. e1 :
+	subq	a2, 1, a2	# e1    :
+
+$u_late_head_exit:
+	extql	t2, a1, t0	# .. e0 :
+	cmpbge	zero, t0, t7	# e0    :
+	or	t7, t10, t6	# e1    :
+	cmoveq	a2, t6, t7	# e0    :
+	nop			# .. e1 :
+
+	/* Take care of a final (probably partial) result word.
+	   On entry to this basic block:
+	   t0 == assembled source word
+	   t7 == cmpbge mask that found the null.  */
+$u_final:
+	negq	t7, t6		# e0    : isolate low bit set
+	and	t6, t7, t8	# e1    :
+
+	and	t8, 0x80, t6	# e0    : avoid dest word load if we can
+	bne	t6, 1f		# .. e1 (zdb)
+
+	ldq_u	t1, 0(a0)	# e0    :
+	subq	t8, 1, t6	# .. e1 :
+	or	t6, t8, t7	# e0    :
+	zapnot	t0, t7, t0	# .. e1 : kill source bytes > null
+	zap	t1, t7, t1	# e0    : kill dest bytes <= null
+	or	t0, t1, t0	# e1    :
+
+1:	stq_u	t0, 0(a0)	# e0    :
+	br	$finish_up
+
+$u_eoc:				# end-of-count
+	extqh	t2, a1, t0
+	or	t0, t1, t0
+	cmpbge	zero, t0, t7
+
+$u_eocfin:			# end-of-count, final word
+	or	t10, t7, t7
+	br	$u_final
+
+	/* Unaligned copy entry point.  */
+	.align 3
+$unaligned:
+
+	EX( ldq_u t1, 0(a1) )	# e0    : load first source word
+
+	and	a0, 7, t4	# .. e1 : find dest misalignment
+	and	a1, 7, t5	# e0    : find src misalignment
 
-1:	addq	$20, 1, $21
-	mov	$16, $0		# save the string start
-	stq	$21, 0($19)	# increment exception count
-	bsr	$23, __stxncpy	# do the work of the copy
-
-	zapnot	$1, $22, $5	# was last byte written null?
-	stq	$20, 0($19)	# decrement exception count
-	cmovne	$5, 1, $5
-
-	and	$22, 0xf0, $4	# binary search for the address of the
-	and	$22, 0xcc, $3	# last byte written
-	and	$22, 0xaa, $2
-	bic	$16, 7, $1
-	cmovne	$4, 4, $4
-	cmovne	$3, 2, $3
-	cmovne	$2, 1, $2
-	addq	$1, $4, $1
-	addq	$2, $3, $2
-	addq	$1, $2, $1
-	addq	$1, $5, $1	# add one if we filled the buffer
+	/* Conditionally load the first destination word and a bytemask
+	   with 0xff indicating that the destination byte is sacrosanct.  */
+
+	mov	zero, t0	# .. e1 :
+	mov	zero, t6	# e0    :
+	beq	t4, 1f		# .. e1 :
+	ldq_u	t0, 0(a0)	# e0    :
+	lda	t6, -1		# .. e1 :
+	mskql	t6, a0, t6	# e0    :
+1:
+	subq	a1, t4, a1	# .. e1 : sub dest misalignment from src addr
+
+	/* If source misalignment is larger than dest misalignment, we need
+	   extra startup checks to avoid SEGV.  */
+
+	cmplt	t4, t5, t8	# e1    :
+	extql	t1, a1, t1	# .. e0 : shift src into place
+	lda	t2, -1		# e0    : for creating masks later
+	beq	t8, $u_head	# e1    :
+
+	mskqh	t2, t5, t2	# e0    : begin src byte validity mask
+	cmpbge	zero, t1, t7	# .. e1 : is there a zero?
+	extql	t2, a1, t2	# e0    :
+	or	t7, t10, t6	# .. e1 : test for end-of-count too
+	cmpbge	zero, t2, t3	# e0    :
+	cmoveq	a2, t6, t7	# .. e1 :
+	andnot	t7, t3, t7	# e0    :
+	beq	t7, $u_head	# .. e1 (zdb)
+
+	/* At this point we've found a zero in the first partial word of
+	   the source.  We need to isolate the valid source data and mask
+	   it into the original destination data.  (Incidentally, we know
+	   that we'll need at least one byte of that original dest word.) */
+
+	ldq_u	t0, 0(a0)	# e0    :
+	negq	t7, t6		# .. e1 : build bitmask of bytes <= zero
+	mskqh	t1, t4, t1	# e0    :
+	and	t6, t7, t8	# .. e1 :
+	subq	t8, 1, t6	# e0    :
+	or	t6, t8, t7	# e1    :
+
+	zapnot	t2, t7, t2	# e0    : prepare source word; mirror changes
+	zapnot	t1, t7, t1	# .. e1 : to source validity mask
+
+	andnot	t0, t2, t0	# e0    : zero place for source to reside
+	or	t0, t1, t0	# e1    : and put it there
+	stq_u	t0, 0(a0)	# e0    :
+
+$finish_up:
+	zapnot	t0, t10, t4	# was last byte written null?
+	cmovne	t4, 1, t4
+
+	and	t10, 0xf0, t3	# binary search for the address of the
+	and	t10, 0xcc, t2	# last byte written
+	and	t10, 0xaa, t1
+	bic	a0, 7, t0
+	cmovne	t3, 4, t3
+	cmovne	t2, 2, t2
+	cmovne	t1, 1, t1
+	addq	t0, t3, t0
+	addq	t1, t2, t1
+	addq	t0, t1, t0
+	addq	t0, t4, t0	# add one if we filled the buffer
 
-	subq	$1, $0, $0	# find string length
+	subq	t0, v0, v0	# find string length
 	ret
 
-9:	clr	$0
+$zerolength:
+	clr	v0
+$exception:
 	ret
 
 	.end __strncpy_from_user
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/mm/Makefile linux/arch/alpha/mm/Makefile
--- v2.1.6/linux/arch/alpha/mm/Makefile	Tue Aug 15 15:07:02 1995
+++ linux/arch/alpha/mm/Makefile	Wed Oct 30 18:41:44 1996
@@ -7,7 +7,7 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
-OBJS	= init.o fault.o
+OBJS	= init.o fault.o extable.o
 
 mm.o: $(OBJS)
 	$(LD) -r -o mm.o $(OBJS)
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/mm/extable.c linux/arch/alpha/mm/extable.c
--- v2.1.6/linux/arch/alpha/mm/extable.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/alpha/mm/extable.c	Thu Oct 31 13:59:59 1996
@@ -0,0 +1,50 @@
+/*
+ * linux/arch/alpha/mm/extable.c
+ */
+
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __start___ex_table[];
+extern const struct exception_table_entry __stop___ex_table[];
+
+static inline unsigned
+search_one_table(const struct exception_table_entry *first,
+		 const struct exception_table_entry *last,
+		 signed int value)
+{
+        while (first <= last) {
+		const struct exception_table_entry *mid;
+		long diff;
+
+		mid = (last - first) / 2 + first;
+		diff = mid->insn - value;
+                if (diff == 0)
+                        return mid->fixup.unit;
+                else if (diff < 0)
+                        first = mid+1;
+                else
+                        last = mid-1;
+        }
+        return 0;
+}
+
+unsigned
+search_exception_table(unsigned long addr)
+{
+	unsigned ret;
+	signed int reladdr;
+
+	/* Search the kernel's table first.  */
+	{
+		register unsigned long gp __asm__("$29");
+		reladdr = addr - gp;
+	}
+	ret = search_one_table(__start___ex_table,
+			       __stop___ex_table-1, reladdr);
+	if (ret)
+		return ret;
+
+	/* FIXME -- search the module's tables here */
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.6/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c
--- v2.1.6/linux/arch/alpha/mm/fault.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/alpha/mm/fault.c	Thu Oct 31 13:59:59 1996
@@ -52,14 +52,24 @@
  *	-1 = instruction fetch
  *	0 = load
  *	1 = store
+ *
+ * Registers $9 through $15 are saved in a block just prior to `regs' and
+ * are saved and restored around the call to allow exception code to
+ * modify them.
  */
-asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause,
-	unsigned long a3, unsigned long a4, unsigned long a5,
-	struct pt_regs regs)
+
+/* Macro for exception fixup code to access integer registers.  */
+#define dpf_reg(r) \
+	(((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-16 : \
+				 (r) <= 18 ? (r)+8 : (r)-10])
+
+asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr,
+			      long cause, struct pt_regs *regs)
 {
 	struct vm_area_struct * vma;
 	struct task_struct *tsk = current;
 	struct mm_struct *mm = tsk->mm;
+	unsigned fixup;
 
 	down(&mm->mmap_sem);
 	vma = find_vma(mm, address);
@@ -97,18 +107,21 @@
  */
 bad_area:
 	up(&mm->mmap_sem);
-	/* Did we have an exception handler installed? */
-	if (current->tss.ex.count == 1) {
-		printk("Taking exception at %lx (%lx)\n", regs.pc, regs.r28);
-		current->tss.ex.count = 0;
-		/* return to the address in r28 */
-		(&regs)->pc = regs.r28;
+
+	/* Are we prepared to handle this fault as an exception?  */
+	if ((fixup = search_exception_table(regs->pc)) != 0) {
+		unsigned long newpc;
+		newpc = fixup_exception(dpf_reg, fixup, regs->pc);
+		printk("Taking exception at %lx (%lx)\n", regs->pc, newpc);
+		regs->pc = newpc;
 		return;
 	}
-	if (user_mode(&regs)) {
-		printk("%s: memory violation at pc=%08lx rp=%08lx (bad address = %08lx)\n",
-			tsk->comm, regs.pc, regs.r26, address);
-		die_if_kernel("oops", &regs, cause);
+
+	if (user_mode(regs)) {
+		printk("%s: memory violation at pc=%08lx ra=%08lx "
+		       "(bad address = %08lx)\n",
+			tsk->comm, regs->pc, regs->r26, address);
+		die_if_kernel("oops", regs, cause);
 		force_sig(SIGSEGV, tsk);
 		return;
 	}
@@ -116,8 +129,8 @@
  * Oops. The kernel tried to access some bad page. We'll have to
  * terminate things with extreme prejudice.
  */
-	printk(KERN_ALERT 
-	       "Unable to handle kernel paging request at virtual address %016lx\n", address);
-	die_if_kernel("Oops", &regs, cause);
+	printk(KERN_ALERT "Unable to handle kernel paging request at "
+	       "virtual address %016lx\n", address);
+	die_if_kernel("Oops", regs, cause);
 	do_exit(SIGKILL);
 }
diff -u --recursive --new-file v2.1.6/linux/arch/i386/mm/Makefile linux/arch/i386/mm/Makefile
--- v2.1.6/linux/arch/i386/mm/Makefile	Wed Sep 25 11:46:53 1996
+++ linux/arch/i386/mm/Makefile	Fri Nov  1 11:56:43 1996
@@ -8,6 +8,6 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-O_OBJS	 := init.o fault.o ioremap.o
+O_OBJS	 := init.o fault.o ioremap.o extable.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.6/linux/arch/i386/mm/extable.c linux/arch/i386/mm/extable.c
--- v2.1.6/linux/arch/i386/mm/extable.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/i386/mm/extable.c	Fri Nov  1 11:56:43 1996
@@ -0,0 +1,50 @@
+/*
+ * linux/arch/i386/mm/extable.c
+ */
+
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __start___ex_table[];
+extern const struct exception_table_entry __stop___ex_table[];
+
+static inline unsigned long
+search_one_table(const struct exception_table_entry *first,
+		 const struct exception_table_entry *last,
+		 unsigned long value)
+{
+	/* Some versions of the linker are buggy and do not align the
+	   __start pointer along with the section, thus we may be low.  */
+	if ((long)first & 3)
+		(long)first = ((long)first | 3) + 1;
+
+        while (first <= last) {
+		const struct exception_table_entry *mid;
+		long diff;
+
+		mid = (last - first) / 2 + first;
+		diff = mid->insn - value;
+                if (diff == 0)
+                        return mid->fixup;
+                else if (diff < 0)
+                        first = mid+1;
+                else
+                        last = mid-1;
+        }
+        return 0;
+}
+
+unsigned long
+search_exception_table(unsigned long addr)
+{
+	unsigned long ret;
+
+	/* Search the kernel's table first.  */
+	ret = search_one_table(__start___ex_table,
+			       __stop___ex_table-1, addr);
+	if (ret)
+		return ret;
+
+	/* FIXME -- search the module's tables here */
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.6/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.6/linux/arch/i386/mm/fault.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/mm/fault.c	Fri Nov  1 11:56:43 1996
@@ -93,6 +93,7 @@
 	struct vm_area_struct * vma;
 	unsigned long address;
 	unsigned long page;
+	unsigned long fixup;
 	int write;
 
 	/* get the address */
@@ -161,14 +162,14 @@
  */
 bad_area:
 	up(&mm->mmap_sem);
-	/* is there valid exception data? Return to indicated handler if so */
-	if (tsk->tss.ex.count == 0) {
-		printk("Exception at %lx (%lx)\n", regs->eip, regs->edx);
-		tsk->tss.ex.count--;
-		regs->eip = regs->edx;
-		regs->edx = -EFAULT;
+
+	/* Are we prepared to handle this fault?  */
+	if ((fixup = search_exception_table(regs->eip)) != 0) {
+		printk("Exception at %lx (%lx)\n", regs->eip, fixup);
+		regs->eip = fixup;
 		return;
 	}
+
 	if (error_code & 4) {
 		tsk->tss.cr2 = address;
 		tsk->tss.error_code = error_code;
diff -u --recursive --new-file v2.1.6/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.1.6/linux/drivers/block/md.c	Thu Oct 10 19:10:54 1996
+++ linux/drivers/block/md.c	Wed Oct 30 18:25:37 1996
@@ -40,6 +40,7 @@
 #define MD_DRIVER
 
 #include <linux/blk.h>
+#include <asm/uaccess.h>
 
 static struct hd_struct md_hd_struct[MAX_MD_DEV];
 static int md_blocksizes[MAX_MD_DEV];
@@ -296,10 +297,9 @@
       
     case BLKGETSIZE:   /* Return device size */
     if  (!arg)  return -EINVAL;
-    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    err = put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
     if (err)
       return err;
-    put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
     break;
 
     case BLKFLSBUF:
@@ -315,10 +315,9 @@
     
     case BLKRAGET:
     if  (!arg)  return -EINVAL;
-    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    err = put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
     if (err)
       return err;
-    put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
     break;
 
     /* We have a problem here : there is no easy way to give a CHS
@@ -328,14 +327,19 @@
     
     case HDIO_GETGEO:
     if (!loc)  return -EINVAL;
-    err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+    err = put_user (2, (char *) &loc->heads);
     if (err)
       return err;
-    put_user (2, (char *) &loc->heads);
-    put_user (4, (char *) &loc->sectors);
-    put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders);
-    put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect,
+    err = put_user (4, (char *) &loc->sectors);
+    if (err)
+      return err;
+    err = put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders);
+    if (err)
+      return err;
+    err = put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect,
 		(long *) &loc->start);
+    if (err)
+      return err;
     break;
     
     RO_IOCTLS(inode->i_rdev,arg);
@@ -367,7 +371,7 @@
 }
 
 
-static int md_read (struct inode *inode, struct file *file,
+static long md_read (struct inode *inode, struct file *file,
 		    char *buf, int count)
 {
   int minor=MINOR(inode->i_rdev);
@@ -378,7 +382,7 @@
   return block_read (inode, file, buf, count);
 }
 
-static int md_write (struct inode *inode, struct file *file,
+static long md_write (struct inode *inode, struct file *file,
 		     const char *buf, int count)
 {
   int minor=MINOR(inode->i_rdev);
diff -u --recursive --new-file v2.1.6/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.6/linux/drivers/cdrom/cdrom.c	Wed Oct 16 10:48:08 1996
+++ linux/drivers/cdrom/cdrom.c	Wed Oct 30 01:56:01 1996
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <asm/fcntl.h>
+#include <asm/uaccess.h>
 
 #include <linux/cdrom.h>
 #include <linux/ucdrom.h>
diff -u --recursive --new-file v2.1.6/linux/drivers/char/README.baycom linux/drivers/char/README.baycom
--- v2.1.6/linux/drivers/char/README.baycom	Sat May 18 11:15:09 1996
+++ linux/drivers/char/README.baycom	Thu Jan  1 02:00:00 1970
@@ -1,164 +0,0 @@
-		    LINUX DRIVER FOR BAYCOM MODEMS
-
-	     Thomas M. Sailer <hb9jnx@radio.amiv.ethz.ch>
-
-This document describes the Linux Kernel Driver for simple Baycom style
-amateur radio modems. The driver supports the following modems:
-
-ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
-       of a modulator/demodulator chip, usually a TI TCM3105. The computer
-       is responsible for regenerating the receiver bit clock, as well as
-       for handling the HDLC protocol. The modem connects to a serial port,
-       hence the name. Since the serial port is not used as an async serial
-       port, the kernel driver for serial ports cannot be used, and this
-       driver only supports standard serial hardware (8250, 16450, 16550)
-
-par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
-       The modem does all the filtering and regenerates the receiver clock.
-       Data is transferred from and to the PC via a shift register.
-       The shift register is filled with 16 bits and an interrupt is signalled.
-       The PC then empties the shift register in a burst. This modem connects
-       to the parallel port, hence the name. The modem leaves the 
-       implementation of the HDLC protocol and the scrambler polynomial to
-       the PC.
-
-par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
-       is protocol compatible to par96, but uses only three low power ICs
-       and can therefore be fed from the parallel port and does not require
-       an additional power supply.
-
-All of the above modems only support half duplex communications. However,
-the driver supports the KISS (see below) fullduplex command. It then simply
-starts to send as soon as there's a packet to transmit and does not care
-about DCD, i.e. it starts to send even if there's someone else on the channel.
-This command is required by some implementations of the DAMA channel 
-access protocol.
-
-
-The Interface of the driver
-
-The driver interfaces to the AX25 stack via a KISS interface. The driver
-can be accessed as a character device with major 60. Major 60 is the first
-number of the local/experimental range. I did no steps to coordinate a
-major number for this driver, but I plan to do so in the near future.
-The driver supports multiple modems (currently four, as defined with
-NR_PORTS). It therefore responds to minor numbers 0 to 3. I recommend
-to access the driver via the special device files /dev/bc[0-3], which
-can be created with 'make bc'.
-
-
-Compiling and installing the driver
-
-First unpack the source files into a directory. Then enter the following: (you
-must be root to do it)
-
-  make dep
-  make
-
-This will create the files baycom.o and setbaycom. baycom.o is as well copied
-to /lib/modules/`uname -n`/misc. If you plan to use kerneld, do the following:
-
-  depmod -a
-
-Do not forget to create the device special files if you install the driver the
-first time. This can be done with:
-
-  make bc
-
-You are now ready to use the driver. You can now activate the driver manually
-by entering
-
-  insmod baycom
-
-or leave this task to kerneld (if installed). Add the following line to
-/etc/conf.modules
-
-  alias char-major-60 baycom
-
-
-
-Configuring the driver
-
-Every time the driver is inserted into the kernel, it has to know which
-modems it should access at which ports. This can be done with the setbaycom
-utility. If you are only using one modem, you can also configure the
-driver from the insmod command line (or by means of an option line in
-/etc/conf.modules).
-
-Examples:
-  insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
-  setbaycom -i /dev/bc0 -p ser12 0x3f8 4 1
-
-Both lines configure the first port to drive a ser12 modem at the first
-serial port (COM1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-
-  insmod baycom modem=2 iobase=0x378 irq=7 options=1
-  setbaycom -i /dev/bc0 -p par96 0x378 7 1
-
-Both lines configure the first port to drive a par96 or par97 modem at the
-first parallel port (LPT1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-  
-The channel access parameters must be set through KISS parameter frames. The
-AX25 stack may be used to generate such frames. KA9Q NET derivatives such
-as WAMPES or TNOS offer the 'param' command for this purpose.
-
-
-
-Hardware DCD versus Software DCD
-
-To avoid collisions on the air, the driver must know when the channel is
-busy. This is the task of the DCD circuitry/software. The driver may either
-utilise a software DCD algorithm (options=1) or use a DCD signal from
-the hardware (options=0).
-
-ser12: if software DCD is utilised, the radio's squelch should always be
-       open. It is highly recommended to use the software DCD algorithm,
-       as it is much faster than most hardware squelch circuitry. The
-       disadvantage is a slightly higher load on the system.
-
-par96: the software DCD algorithm for this type of modem is rather poor.
-       The modem simply does not provide enough information to implement
-       a reasonable DCD algorithm in software. Therefore, if your radio
-       feeds the DCD input of the PAR96 modem, the use of the hardware
-       DCD circuitry is recommended.
-
-par97: as far as I know it is in this respect equivalent to par96.
-
-
-
-Compatibility with the rest of the Linux kernel
-
-The tty interface gave me some headaches. I did not find a reasonable
-documentation of its interfaces, so I'm not particularly sure if I implemented
-it the way I should. Perhaps someone with a more profound knowledge about
-tty drivers could check the interface routines.
-The driver produces a rather high interrupt load. par96/par97 generates 600
-interrupts per second, ser12 1200 while transmitting and 2400 if hardware
-DCD is used, 3600 otherwise. If other device drivers disable interrupts
-too long, the performance of the driver drops (the packet loss rate increases),
-especially with the ser12 modem.
-There were also reports that under rather high load situations the driver
-drops frames. This might be either an interrupt problem, or an AX25 stack
-running in user mode might not get enough CPU time to process the packets
-before the drivers internal buffers overflow. There is no way to throttle
-the other radio stations from this layer, throttling must be done in the
-AX25 layer.
-
-The serial driver, the line printer (lp) driver and the baycom driver compete
-for the same hardware resources. Of course only one driver can access a given
-interface at a time. The serial driver grabs all interfaces it can find at
-startup time. Therefore the baycom driver subsequently won't be able to
-access a serial port. You might therefore find it necessary to release
-a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
-# is the number of the interface. The baycom driver does not reserve any
-port at startup, unless one is specified on the 'insmod' command line. Another
-method to solve the problem is to compile all three drivers as modules and
-leave it to kerneld to load the correct driver depending on the application.
-
-
-
-vy 73s de
-Tom Sailer, hb9jnx@radio.amiv.ethz.ch
-hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.6/linux/drivers/char/baycom.c linux/drivers/char/baycom.c
--- v2.1.6/linux/drivers/char/baycom.c	Tue Oct 29 19:58:05 1996
+++ linux/drivers/char/baycom.c	Thu Jan  1 02:00:00 1970
@@ -1,2327 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	baycom.c  -- baycom ser12 and par96 radio modem driver.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	This program is free software; you can redistribute it and/or modify
- *	it under the terms of the GNU General Public License as published by
- *	the Free Software Foundation; either version 2 of the License, or
- *	(at your option) any later version.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Supported modems
- *
- *  ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
- *         of a modulator/demodulator chip, usually a TI TCM3105. The computer
- *         is responsible for regenerating the receiver bit clock, as well as
- *         for handling the HDLC protocol. The modem connects to a serial port,
- *         hence the name. Since the serial port is not used as an async serial
- *         port, the kernel driver for serial ports cannot be used, and this
- *         driver only supports standard serial hardware (8250, 16450, 16550)
- *  
- *  par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
- *         The modem does all the filtering and regenerates the receiver clock.
- *         Data is transferred from and to the PC via a shift register.
- *         The shift register is filled with 16 bits and an interrupt is
- *         signalled. The PC then empties the shift register in a burst. This
- *         modem connects to the parallel port, hence the name. The modem
- *         leaves the implementation of the HDLC protocol and the scrambler
- *         polynomial to the PC.
- *  
- *  par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
- *         modem is protocol compatible to par96, but uses only three low
- *         power ICs and can therefore be fed from the parallel port and
- *         does not require an additional power supply.
- *
- *
- *  Command line options (insmod command line)
- * 
- *  major    major number the driver should use; default 60 
- *  modem    modem type of the first channel (minor 0); 1=ser12,
- *           2=par96/par97, any other value invalid
- *  iobase   base address of the port; common values are for ser12 0x3f8,
- *           0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
- *  irq      interrupt line of the port; common values are for ser12 3,4
- *           and for par96/par97 7
- *  options  0=use hardware DCD, 1=use software DCD
- * 
- *
- *  History:
- *   0.1  03.05.96  Renamed from ser12 0.5 and added support for par96
- *                  Various resource allocation cleanups
- *   0.2  12.05.96  Changed major to allocated 51. Integrated into kernel
- *                  source tree
- *   0.3  04.06.96  Major bug fixed (forgot to wake up after write) which
- *                  interestingly manifested only with kernel ax25
- *                  (the slip line discipline)
- *                  introduced bottom half and tq_baycom
- *                  HDLC processing now done with interrupts on
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/version.h>
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <asm/uaccess.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/interrupt.h>
-#include <linux/tqueue.h>
-#include <linux/baycom.h>
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_TYPE_NORMAL 0		/* not used */
-#define TTY_DRIVER_TYPE_BAYCOM 6
-
-/*
- * ser12 options:
- * BAYCOM_OPTIONS_SOFTDCD: if undefined, you must use the transmitters
- * hardware carrier detect circuitry, the driver will report DCD as soon as
- * there are transitions on the input line. Advantage: lower interrupt load
- * on the system. Disadvantage: slower, since hardware carrier detect
- * circuitry is usually slow.
- */
-
-#define BUFLEN_RX 8192
-#define BUFLEN_TX 8192
-
-#define NR_PORTS 4
-
-#define KISS_VERBOSE
-
-#define BAYCOM_MAGIC 0x3105bac0
-
-/* --------------------------------------------------------------------- */
-
-/*
- * user settable parameters (from the command line)
- */
-#ifndef MODULE
-static
-#endif /* MODULE */
-int major = BAYCOM_MAJOR;
-
-/* --------------------------------------------------------------------- */
-
-static struct tty_struct *baycom_table[NR_PORTS];
-static struct termios *baycom_termios[NR_PORTS];
-static struct termios *baycom_termios_locked[NR_PORTS];
-
-static int baycom_refcount;
-
-static struct tty_driver baycom_driver;
-
-static struct {
-	int modem, iobase, irq, options;
-} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, };
-
-/* --------------------------------------------------------------------- */
-
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define SER12_EXTENT 8
-
-#define LPT_DATA(iobase)    (iobase+0)
-#define LPT_STATUS(iobase)  (iobase+1)
-#define LPT_CONTROL(iobase) (iobase+2)
-#define LPT_IRQ_ENABLE      0x10
-#define PAR96_BURSTBITS 16
-#define PAR96_BURST     4
-#define PAR96_PTT       2
-#define PAR96_TXBIT     1
-#define PAR96_ACK       0x40
-#define PAR96_RXBIT     0x20
-#define PAR96_DCD       0x10
-#define PAR97_POWER     0xf8
-
-#define PAR96_EXTENT 3
-
-/* ---------------------------------------------------------------------- */
-
-struct access_params {
-	int tx_delay;
-	int tx_tail;
-	int slottime;
-	int ppersist;
-	int fulldup;
-};
-
-struct hdlc_state_rx {
-	int rx_state;	/* 0 = sync hunt, != 0 receiving */
-	unsigned int bitstream;
-	unsigned int bitbuf;
-	int numbits;
-	unsigned int shreg1, shreg2;
-
-	int len;
-	unsigned char *bp;
-	unsigned char buffer[BAYCOM_MAXFLEN+2];	   /* make room for CRC */
-};
-
-struct hdlc_state_tx {
-	/*
-	 * 0 = send flags
-	 * 1 = send txtail (flags)
-	 * 2 = send packet
-	 */
-	int tx_state;	
-	int numflags;
-	unsigned int bitstream;
-	unsigned int current_byte;
-	unsigned char ptt;
-
-	unsigned int bitbuf;
-	int numbits;
-	unsigned int shreg1, shreg2;
-
-	int len;
-	unsigned char *bp;
-	unsigned char buffer[BAYCOM_MAXFLEN+2];		/* make room for CRC */
-};
-
-struct modem_state_ser12 {
-	unsigned char last_sample;
-	unsigned char interm_sample;
-	unsigned int bit_pll;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	unsigned char last_rxbit;
-	unsigned char tx_bit;
-};
-
-struct modem_state_par96 {
-	int dcd_count;
-	unsigned int dcd_shreg;
-	unsigned long descram;
-	unsigned long scram;
-};
-
-struct modem_state {
-	unsigned char dcd;
-	short arb_divider;
-	unsigned char flags;
-	struct modem_state_ser12 ser12;
-	struct modem_state_par96 par96;
-};
-
-struct packet_buffer {
-	unsigned int rd;
-	unsigned int wr;
-	
-	unsigned int buflen;
-	unsigned char *buffer;
-};
-
-struct packet_hdr {
-	unsigned int next;
-	unsigned int len;
-	/* packet following */
-};
-
-#ifdef BAYCOM_DEBUG
-struct bit_buffer {
-	unsigned int rd;
-	unsigned int wr;
-	unsigned int shreg;
-	unsigned char buffer[64];
-};
-
-struct debug_vals {
-	unsigned long last_jiffies;
-	unsigned cur_intcnt;
-	unsigned last_intcnt;
-	int cur_pllcorr;
-	int last_pllcorr;
-};
-#endif /* BAYCOM_DEBUG */
-
-struct kiss_decode {
-	unsigned char dec_state; /* 0 = hunt FEND */
-	unsigned char escaped;
-	unsigned char pkt_buf[BAYCOM_MAXFLEN+1];
-	unsigned int wr;
-};
-
-/* ---------------------------------------------------------------------- */
-
-struct baycom_state {
-	int magic;
-
-	unsigned char modem_type;
-
-	unsigned int iobase;
-	unsigned int irq;
-	unsigned int options;
-
-	int opened;
-	struct tty_struct *tty;
-
-#ifdef BAYCOM_USE_BH
-	struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate;
-#endif /* BAYCOM_USE_BH */
-
-	struct packet_buffer rx_buf;
-	struct packet_buffer tx_buf;
-
-	struct access_params ch_params;
-
-	struct hdlc_state_rx hdlc_rx;
-	struct hdlc_state_tx hdlc_tx;
-
-	int calibrate;
-
-	struct modem_state modem;
-
-#ifdef BAYCOM_DEBUG
-	struct bit_buffer bitbuf_channel;
-	struct bit_buffer bitbuf_hdlc;
-	
-	struct debug_vals debug_vals;
-#endif /* BAYCOM_DEBUG */
-
-	struct kiss_decode kiss_decode;
-
-	struct baycom_statistics stat;
-};
-
-/* --------------------------------------------------------------------- */
-
-struct baycom_state baycom_state[NR_PORTS];
-
-#ifdef BAYCOM_USE_BH
-DECLARE_TASK_QUEUE(tq_baycom);
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-
-/*
- * the CRC routines are stolen from WAMPES
- * by Dieter Deyke
- */
-
-static const unsigned short crc_ccitt_table[] = {
-	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
-	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
-	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
-	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
-	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
-	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
-	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
-	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
-	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
-	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
-	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
-	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
-	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
-	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
-	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
-	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
-	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
-	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
-	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
-	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
-	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
-	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
-	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
-	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
-	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
-	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
-	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
-	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
-	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
-	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
-	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
-	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-
-/*---------------------------------------------------------------------------*/
-
-static inline void append_crc_ccitt(unsigned char *buffer, int len)
-{
- 	unsigned int crc = 0xffff;
-
-	for (;len>0;len--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
-	crc ^= 0xffff;
-	*buffer++ = crc;
-	*buffer++ = crc >> 8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static inline int check_crc_ccitt(const unsigned char *buf,int cnt)
-{
-	unsigned int crc = 0xffff;
-
-	for (; cnt > 0; cnt--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
-	return (crc & 0xffff) == 0xf0b8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-#if 0
-static int calc_crc_ccitt(const unsigned char *buf,int cnt)
-{
-	unsigned int crc = 0xffff;
-
-	for (; cnt > 0; cnt--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
-	crc ^= 0xffff;
-	return (crc & 0xffff);
-}
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-static int store_packet(struct packet_buffer *buf, unsigned char *data,
-	char from_user, unsigned int len)
-{
-	unsigned int free;
-	struct packet_hdr *hdr;
-	unsigned int needed = sizeof(struct packet_hdr)+len;
-	
-	free = buf->rd-buf->wr;
-	if(buf->rd <= buf->wr) {
-		free = buf->buflen - buf->wr;
-		if((free < needed) && (buf->rd >= needed)) {
-			hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
-			hdr->next = 0;
-			hdr->len = 0;
-			buf->wr = 0;
-			free = buf->rd;
-		}
-	}
-	if(free < needed) return 0;		/* buffer overrun */
-	hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
-	if (from_user) 
-		copy_from_user(hdr+1,data,len);
-	else
-		memcpy(hdr+1,data,len);
-	hdr->len = len;
-	hdr->next = buf->wr+needed;
-	if (hdr->next + sizeof(struct packet_hdr) >= buf->buflen)
-		hdr->next = 0;
-	buf->wr = hdr->next;
-	return 1;
-}
-	
-/* ---------------------------------------------------------------------- */
-
-static void get_packet(struct packet_buffer *buf, unsigned char **data,
-	unsigned int *len)
-{
-	struct packet_hdr *hdr;
-	
-	*data = NULL;
-	*len = 0;
-	if (buf->rd == buf->wr)
-		return;
-	hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
-	while (!(hdr->len)) {
-		buf->rd = hdr->next;
-		if (buf->rd == buf->wr)
-			return;
-		hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
-	}
-	*data = (unsigned char *)(hdr+1);
-	*len = hdr->len;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void ack_packet(struct packet_buffer *buf)
-{
-	struct packet_hdr *hdr;
-	
-	if (buf->rd == buf->wr)
-		return;
-	hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
-	buf->rd = hdr->next;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int store_kiss_packet(struct packet_buffer *buf, unsigned char *data,
-	unsigned int len)
-{
-	unsigned char *bp = data;
-	int ln = len;
-	/*
-	 * variables of buf
-	 */
-	unsigned int rd;
-	unsigned int wr;
-	unsigned int buflen;
-	unsigned char *buffer;
-
-	if (!len || !data || !buf)
-		return 0;
-	buflen = buf->buflen;
-	rd = buf->rd;
-	wr = buf->wr;
-	buffer = buf->buffer;
-	
-#define ADD_CHAR(c) {\
-		buffer[wr++] = c;\
-		if (wr >= buflen) wr = 0;\
-		if (wr == rd) return 0;\
-	}
-#define ADD_KISSCHAR(c) {\
-		if (((c) & 0xff) == KISS_FEND) {\
-			ADD_CHAR(KISS_FESC);\
-			ADD_CHAR(KISS_TFEND);\
-		} else if (((c) & 0xff) == KISS_FESC) {\
-			ADD_CHAR(KISS_FESC);\
-			ADD_CHAR(KISS_TFESC);\
-		} else {\
-			ADD_CHAR(c);\
-		}\
-	}
-
-	ADD_CHAR(KISS_FEND);
-	ADD_KISSCHAR(KISS_CMD_DATA);
-	for(; ln > 0; ln--,bp++) {
-		ADD_KISSCHAR(*bp);
-	}
-	ADD_CHAR(KISS_FEND);
-	buf->wr = wr;
-#undef ADD_CHAR
-#undef ADD_KISSCHAR
-	return 1;
-}
-
-/* ---------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit)
-{
-	unsigned char new;
-
-	if (!buf) return;
-	new = buf->shreg & 1;
-	buf->shreg >>= 1;
-	if (bit)
-		buf->shreg |= 0x80;
-	if (new) {
-		buf->buffer[buf->wr] = buf->shreg;
-		buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-		buf->shreg = 0x80;
-	}
-}
-
-static inline void add_bitbuffer_word(struct bit_buffer * buf, 
-				      unsigned int bits)
-{
-	buf->buffer[buf->wr] = bits & 0xff;
-	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-	buf->buffer[buf->wr] = (bits >> 8) & 0xff;
-	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-
-}
-#endif /* BAYCOM_DEBUG */
-
-/* ---------------------------------------------------------------------- */
-
-static inline unsigned int tenms_to_2flags(struct baycom_state *bc, 
-					  unsigned int tenms)
-{
-	switch (bc->modem_type) {
-	case BAYCOM_MODEM_SER12:
-		return tenms * 3 / 4;
-	case BAYCOM_MODEM_PAR96:
-		return tenms * 6;
-	default:
-		return 0;
-	}
-}
-
-/* ---------------------------------------------------------------------- */
-/*
- * The HDLC routines
- */
-
-static inline int hdlc_rx_add_bytes(struct baycom_state *bc, 
-				    unsigned int bits, int num)
-{
-	int added = 0;
-	while (bc->hdlc_rx.rx_state && num >= 8) {
-		if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
-			bc->hdlc_rx.rx_state = 0;
-			return 0;
-		}
-		*bc->hdlc_rx.bp++ = bits >> (32-num);
-		bc->hdlc_rx.len++;
-		num -= 8;
-		added += 8;
-	}
-	return added;
-}
-
-static inline void hdlc_rx_flag(struct baycom_state *bc)
-{
-	if (bc->hdlc_rx.len < 4) 
-		return;
-	if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len)) 
-		return;
-       	bc->stat.rx_packets++;
-	if (!store_kiss_packet(&bc->rx_buf,
-			       bc->hdlc_rx.buffer,
-			       bc->hdlc_rx.len-2))
-		bc->stat.rx_bufferoverrun++;
-}
-
-static void hdlc_rx_word(struct baycom_state *bc, unsigned int word)
-{
-	int i;
-	unsigned int mask1, mask2, mask3, mask4, mask5, mask6;
-	
-	if (!bc) return;
-
-	word &= 0xffff;
-#ifdef BAYCOM_DEBUG
-	add_bitbuffer_word(&bc->bitbuf_hdlc, word);
-#endif /* BAYCOM_DEBUG */
-       	bc->hdlc_rx.bitstream >>= 16;
-	bc->hdlc_rx.bitstream |= word << 16;
-	bc->hdlc_rx.bitbuf >>= 16;
-	bc->hdlc_rx.bitbuf |= word << 16;
-	bc->hdlc_rx.numbits += 16;
-	for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
-	    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
-	    i >= 0; 
-	    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
-	    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
-		if ((bc->hdlc_rx.bitstream & mask1) == mask1)
-			bc->hdlc_rx.rx_state = 0; /* abort received */
-		else if ((bc->hdlc_rx.bitstream & mask2) == mask3) {
-			/* flag received */
-			if (bc->hdlc_rx.rx_state) {
-				hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf << 
-						  (8 + i), bc->hdlc_rx.numbits
-						  - 8 - i);
-				hdlc_rx_flag(bc);
-			}
-			bc->hdlc_rx.len = 0;
-			bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
-			bc->hdlc_rx.rx_state = 1;
-			bc->hdlc_rx.numbits = i;
-		} else if ((bc->hdlc_rx.bitstream & mask4) == mask5) {
-			/* stuffed bit */
-			bc->hdlc_rx.numbits--;
-			bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) |
-				((bc->hdlc_rx.bitbuf & mask6) << 1);
-		}
-	}
-	bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf,
-						 bc->hdlc_rx.numbits);
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned int hdlc_tx_word(struct baycom_state *bc)
-{
-	unsigned int mask1, mask2, mask3;
-	int i;
-
-	if (!bc || !bc->hdlc_tx.ptt)
-		return 0;
-	for (;;) {
-		if (bc->hdlc_tx.numbits >= 16) {
-			unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff;
-			bc->hdlc_tx.bitbuf >>= 16;
-			bc->hdlc_tx.numbits -= 16;
-			return ret;
-		}
-		switch (bc->hdlc_tx.tx_state) {
-		default:
-			bc->hdlc_tx.ptt = 0;
-			bc->hdlc_tx.tx_state = 0;
-			return 0;
-		case 0:
-		case 1:
-			if (bc->hdlc_tx.numflags) {
-				bc->hdlc_tx.numflags--;
-				bc->hdlc_tx.bitbuf |= 
-					0x7e7e << bc->hdlc_tx.numbits;
-				bc->hdlc_tx.numbits += 16;
-				break;
-			}
-			if (bc->hdlc_tx.tx_state == 1) {
-				bc->hdlc_tx.ptt = 0;
-				return 0;
-			}
-			get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
-				   &bc->hdlc_tx.len);
-			if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
-				bc->hdlc_tx.tx_state = 1;
-				bc->hdlc_tx.numflags = tenms_to_2flags
-					(bc, bc->ch_params.tx_tail);
-				break;
-			}
-			if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
-				bc->hdlc_tx.tx_state = 0;
-				bc->hdlc_tx.numflags = 1;
-				ack_packet(&bc->tx_buf);
-				break;
-			}
-			memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp, 
-			       bc->hdlc_tx.len);
-			ack_packet(&bc->tx_buf);
-			bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
-			append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len);
-			/* the appended CRC */
-			bc->hdlc_tx.len += 2; 
-			bc->hdlc_tx.tx_state = 2;
-			bc->hdlc_tx.bitstream = 0;
-			bc->stat.tx_packets++;
-			break;
-		case 2:
-			if (!bc->hdlc_tx.len) {
-				bc->hdlc_tx.tx_state = 0;
-				bc->hdlc_tx.numflags = 1;
-				break;
-			}
-			bc->hdlc_tx.len--;
-			bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp <<
-				bc->hdlc_tx.numbits;
-			bc->hdlc_tx.bitstream >>= 8;
-			bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16;
-			mask1 = 0x1f000;
-			mask2 = 0x10000;
-			mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits);
-			bc->hdlc_tx.numbits += 8;
-			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
-			    mask3 = (mask3 << 1) | 1) {
-				if ((bc->hdlc_tx.bitstream & mask1) != mask1) 
-					continue;
-				bc->hdlc_tx.bitstream &= ~mask2;
-				bc->hdlc_tx.bitbuf = 
-					(bc->hdlc_tx.bitbuf & mask3) |
-						((bc->hdlc_tx.bitbuf & 
-						 (~mask3)) << 1);
-				bc->hdlc_tx.numbits++;
-				mask3 = (mask3 << 1) | 1;
-			}
-			break;
-		}
-	}
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned short random_seed;
-
-static inline unsigned short random_num(void)
-{
-	random_seed = 28629 * random_seed + 157;
-	return random_seed;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static inline void tx_arbitrate(struct baycom_state *bc)
-{
-	unsigned char *bp;
-	unsigned int len;
-	
-	if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd)
-		return;
-	get_packet(&bc->tx_buf, &bp, &len);
-	if (!bp || !len)
-		return;
-	
-	if (!bc->ch_params.fulldup) {
-		if ((random_num() % 256) > bc->ch_params.ppersist)
-			return;
-	}
-	bc->hdlc_tx.tx_state = 0;
-	bc->hdlc_tx.numflags = tenms_to_2flags(bc, bc->ch_params.tx_delay);
-	bc->hdlc_tx.numbits = bc->hdlc_tx.bitbuf = bc->hdlc_tx.bitstream = 0;
-	bc->hdlc_tx.ptt = 1;
-	bc->stat.ptt_keyed++;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static void inline baycom_int_freq(struct baycom_state *bc)
-{
-	unsigned long cur_jiffies = jiffies;
-	/* 
-	 * measure the interrupt frequency
-	 */
-	bc->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
-		bc->debug_vals.last_jiffies = cur_jiffies;
-		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
-		bc->debug_vals.cur_intcnt = 0;
-		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
-		bc->debug_vals.cur_pllcorr = 0;
-	}
-}
-#endif /* BAYCOM_DEBUG */
-
-/* --------------------------------------------------------------------- */
-
-static inline void rx_chars_to_flip(struct baycom_state *bc) 
-{
-	int flip_free;
-	unsigned int cnt;
-	unsigned int new_rd;
-	unsigned long flags;
-
-	if ((!bc) || (!bc->tty) || (bc->tty->flip.count >= TTY_FLIPBUF_SIZE) ||
-	    (bc->rx_buf.rd == bc->rx_buf.wr) || 
-	    (!bc->tty->flip.char_buf_ptr) ||
-	    (!bc->tty->flip.flag_buf_ptr))
-		return;
-	for(;;) {
-		flip_free = TTY_FLIPBUF_SIZE - bc->tty->flip.count;
-		if (bc->rx_buf.rd <= bc->rx_buf.wr)
-			cnt = bc->rx_buf.wr - bc->rx_buf.rd;
-		else
-			cnt = bc->rx_buf.buflen - bc->rx_buf.rd;
-		if ((flip_free <= 0) || (!cnt)) {
-			tty_schedule_flip(bc->tty);
-			return;
-		}
-		if (cnt > flip_free)
-			cnt = flip_free;
-		save_flags(flags); cli();
-		memcpy(bc->tty->flip.char_buf_ptr, bc->rx_buf.buffer+bc->rx_buf.rd, cnt);
-		memset(bc->tty->flip.flag_buf_ptr, TTY_NORMAL, cnt);
-		bc->tty->flip.count += cnt;
-		bc->tty->flip.char_buf_ptr += cnt;
-		bc->tty->flip.flag_buf_ptr += cnt;
-		restore_flags(flags);
-		new_rd = bc->rx_buf.rd+cnt;
-		if (new_rd >= bc->rx_buf.buflen)
-			new_rd -= bc->rx_buf.buflen;
-		bc->rx_buf.rd = new_rd;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== SER12 specific routines =========================
- */
-
-static void inline ser12_set_divisor(struct baycom_state *bc, 
-				     unsigned char divisor)
-{
-	outb(0x81, LCR(bc->iobase));	/* DLAB = 1 */
-	outb(divisor, DLL(bc->iobase));
-	outb(0, DLM(bc->iobase));
-	outb(0x01, LCR(bc->iobase));	/* word length = 6 */
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * must call the TX arbitrator every 10ms
- */
-#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
-			       36 : 24)
-#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
-				240 : 12)
-
-static void baycom_ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev_id;
-	unsigned char cur_s;
-	
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	/*
-	 * make sure the next interrupt is generated;
-	 * 0 must be used to power the modem; the modem draws its
-	 * power from the TxD line
-	 */	
-	outb(0x00, THR(bc->iobase));
-	rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
-	baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
-	/*
-	 * check if transmitter active
-	 */
-	if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
-		ser12_set_divisor(bc, 12); /* one interrupt per channel bit */
-		/*
-		 * first output the last bit (!) then call HDLC transmitter,
-		 * since this may take quite long
-		 */
-		outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase));
-		if (bc->hdlc_tx.shreg1 <= 1) {
-			if (bc->calibrate > 0) {
-				bc->hdlc_tx.shreg1 = 0x10000;
-				bc->calibrate--;
-			} else {
-#ifdef BAYCOM_USE_BH
-				bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
-				bc->hdlc_tx.shreg2 = 0;
-				queue_task_irq_off(&bc->tq_transmitter, 
-						   &tq_baycom);
-				mark_bh(BAYCOM_BH);
-#ifdef HDLC_LOOPBACK
-				bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
-				queue_task_irq_off(&bc->tq_receiver, 
-						   &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#else /* BAYCOM_USE_BH */
-				bc->hdlc_tx.shreg1 = hdlc_tx_word(bc) 
-					| 0x10000;
-#ifdef HDLC_LOOPBACK
-				hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
-#endif /* HDLC_LOOPBACK */
-#endif /* BAYCOM_USE_BH */
-			}	
-		}
-		if (!(bc->hdlc_tx.shreg1 & 1))
-			bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
-		bc->hdlc_tx.shreg1 >>= 1;
-		return;
-	}
-	/*
-	 * do demodulator
-	 */
-	outb(0x0d, MCR(bc->iobase));			/* transmitter off */
-	cur_s = inb(MSR(bc->iobase)) & 0x10;	/* the CTS line */
-#ifdef BAYCOM_DEBUG
-	add_bitbuffer(&bc->bitbuf_channel, cur_s);
-#endif /* BAYCOM_DEBUG */
-	bc->modem.ser12.dcd_shreg <<= 1;
-	if(cur_s != bc->modem.ser12.last_sample) {
-		bc->modem.ser12.dcd_shreg |= 1;
-
-		if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-			unsigned int dcdspos, dcdsneg;
-
-			dcdspos = dcdsneg = 0;
-			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
-			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
-				dcdspos += 2;
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
-
-			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
-		} else
-			bc->modem.ser12.dcd_sum0--;
-	}
-	bc->modem.ser12.last_sample = cur_s;
-	if(!bc->modem.ser12.dcd_time) {
-		bc->modem.dcd = (bc->modem.ser12.dcd_sum0 + 
-				 bc->modem.ser12.dcd_sum1 +
-				 bc->modem.ser12.dcd_sum2) < 0;
-		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
-		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
-		/* offset to ensure DCD off on silent input */
-		bc->modem.ser12.dcd_sum0 = 2;
-		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
-	}
-	bc->modem.ser12.dcd_time--;
-	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-		/*
-		 * PLL code for the improved software DCD algorithm
-		 */
-		if (bc->modem.ser12.interm_sample) {
-			/*
-			 * intermediate sample; set timing correction to normal
-			 */
-			ser12_set_divisor(bc, 4);
-		} else {
-			/*
-			 * do PLL correction and call HDLC receiver
-			 */
-			switch (bc->modem.ser12.dcd_shreg & 7) {
-			case 1: /* transition too late */
-				ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
-				break;
-			case 4:	/* transition too early */
-				ser12_set_divisor(bc, 3);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
-				break;
-			default:
-				ser12_set_divisor(bc, 4);
-				break;
-			}
-			bc->hdlc_rx.shreg1 >>= 1;
-			if (bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit)
-				bc->hdlc_rx.shreg1 |= 0x10000;
-			bc->modem.ser12.last_rxbit = 
-				bc->modem.ser12.last_sample;
-		}
-		if (++bc->modem.ser12.interm_sample >= 3)
-			bc->modem.ser12.interm_sample = 0;		
-	} else {
-		/*
-		 * PLL algorithm for the hardware squelch DCD algorithm
-		 */
-		if (bc->modem.ser12.interm_sample) {
-			/*
-			 * intermediate sample; set timing correction to normal
-			 */
-			ser12_set_divisor(bc, 6);
-		} else {
-			/*
-			 * do PLL correction and call HDLC receiver
-			 */
-			switch (bc->modem.ser12.dcd_shreg & 3) {
-			case 1: /* transition too late */
-				ser12_set_divisor(bc, 7);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
-				break;
-			case 2:	/* transition too early */
-				ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
-				break;
-			default:
-				ser12_set_divisor(bc, 6);
-				break;
-			}
-			bc->hdlc_rx.shreg1 >>= 1;
-			if (bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit)
-				bc->hdlc_rx.shreg1 |= 0x10000;
-			bc->modem.ser12.last_rxbit = 
-				bc->modem.ser12.last_sample;
-		}
-		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
-	}
-	if (bc->hdlc_rx.shreg1 & 1) {
-#ifdef BAYCOM_USE_BH
-		bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000;
-		queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-		mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-		hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1);
-#endif /* BAYCOM_USE_BH */
-		bc->hdlc_rx.shreg1 = 0x10000;
-	}
-	if (--bc->modem.arb_divider <= 0) {
-#ifdef BAYCOM_USE_BH
-		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
-		mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-		tx_arbitrate(bc);
-#endif /* BAYCOM_USE_BH */
-		bc->modem.arb_divider = bc->ch_params.slottime * 
-			SER12_ARB_DIVIDER(bc);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
-	c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
-	{ "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart ser12_check_uart(unsigned int iobase)
-{
-	unsigned char b1,b2,b3;
-	enum uart u;
-	enum uart uart_tab[] =
-		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
-	b1 = inb(MCR(iobase));
-	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
-	b2 = inb(MSR(iobase));
-	outb(0x1a, MCR(iobase));
-	b3 = inb(MSR(iobase)) & 0xf0;
-	outb(b1, MCR(iobase));			/* restore old values */
-	outb(b2, MSR(iobase));
-	if (b3 != 0x90) 
-		return c_uart_unknown;
-	inb(RBR(iobase));
-	inb(RBR(iobase));
-	outb(0x01, FCR(iobase));		/* enable FIFOs */
-	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
-	if (u == c_uart_16450) {
-		outb(0x5a, SCR(iobase));
-		b1 = inb(SCR(iobase));
-		outb(0xa5, SCR(iobase));
-		b2 = inb(SCR(iobase));
-		if ((b1 != 0x5a) || (b2 != 0xa5)) 
-			u = c_uart_8250;
-	}
-	return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_allocate_resources(unsigned int iobase, unsigned int irq,
-				    unsigned int options)
-{
-	enum uart u;
-
-	if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
-		return -ENXIO;
-	if (check_region(iobase, SER12_EXTENT))
-		return -EACCES;
-	if ((u = ser12_check_uart(iobase)) == c_uart_unknown)
-		return -EIO;
-	request_region(iobase, SER12_EXTENT, "baycom_ser12");
-	outb(0, FCR(iobase));		/* disable FIFOs */
-	outb(0x0d, MCR(iobase));
-	printk(KERN_INFO "baycom: ser12 at iobase 0x%x irq %u options 0x%x "
-	       "uart %s\n", iobase, irq, options, uart_str[u]);
-	return 0;
-}
-	
-/* --------------------------------------------------------------------- */
-
-static void ser12_deallocate_resources(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
-		return;
-	/*
-	 * disable interrupts
-	 */
-	outb(0, IER(bc->iobase));
-	outb(1, MCR(bc->iobase));
-	/* 
-	 * this should prevent kernel: Trying to free IRQx
-	 * messages
-	 */
-	if (bc->opened > 0)
-		free_irq(bc->irq, bc);
-	release_region(bc->iobase, SER12_EXTENT);
-	bc->modem_type = BAYCOM_MODEM_INVALID;
-	printk(KERN_INFO "baycom: release ser12 at iobase 0x%x irq %u\n",
-	       bc->iobase, bc->irq);
-	bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_on_open(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
-		return -ENXIO;
-	/*
-	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
-	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
-	 * depending on the usage of the software DCD routine
-	 */
-	ser12_set_divisor(bc, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
-	outb(0x0d, MCR(bc->iobase));
-	outb(0, IER(bc->iobase));
-	if (request_irq(bc->irq, baycom_ser12_interrupt, SA_INTERRUPT, 
-			"baycom_ser12", bc))
-		return -EBUSY;
-	/*
-	 * enable transmitter empty interrupt
-	 */
-	outb(2, IER(bc->iobase));  
-	/* 
-	 * the value here serves to power the modem
-	 */     
-	outb(0x00, THR(bc->iobase));
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_on_close(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
-		return;
-	/*
-	 * disable interrupts
-	 */
-	outb(0, IER(bc->iobase));
-	outb(1, MCR(bc->iobase));
-	free_irq(bc->irq, bc);	
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== PAR96 specific routines =========================
- */
-
-#define PAR96_DESCRAM_TAP1 0x20000
-#define PAR96_DESCRAM_TAP2 0x01000
-#define PAR96_DESCRAM_TAP3 0x00001
-
-#define PAR96_DESCRAM_TAPSH1 17
-#define PAR96_DESCRAM_TAPSH2 12
-#define PAR96_DESCRAM_TAPSH3 0
-
-#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
-#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	register struct baycom_state *bc = (struct baycom_state *)dev_id;
-	int i;
-	unsigned int data, descx, mask, mask2;
-	
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-
-	rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
-	baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
-	/*
-	 * check if transmitter active
-	 */
-	if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
-		/*
-		 * first output the last 16 bits (!) then call HDLC
-		 * transmitter, since this may take quite long
-		 * do the differential encoder and the scrambler on the fly
-		 */
-		data = bc->hdlc_tx.shreg1;
-		for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
-			unsigned char val = PAR97_POWER;
-			bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
-						 (bc->modem.par96.scram & 1));
-			if (!(data & 1))
-				bc->modem.par96.scram ^= 1;
-			if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
-				bc->modem.par96.scram ^= 
-					(PAR96_SCRAM_TAPN << 1);
-			if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
-				val |= PAR96_TXBIT;
-			outb(val, LPT_DATA(bc->iobase));
-			outb(val | PAR96_BURST, LPT_DATA(bc->iobase));
-		}
-		if (bc->calibrate > 0) {
-			bc->hdlc_tx.shreg1 = 0x10000;
-			bc->calibrate--;
-		} else {
-#ifdef BAYCOM_USE_BH
-			bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
-			bc->hdlc_tx.shreg2 = 0;
-			queue_task_irq_off(&bc->tq_transmitter, &tq_baycom);
-			mark_bh(BAYCOM_BH);
-#ifdef HDLC_LOOPBACK
-			bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
-			queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#else /* BAYCOM_USE_BH */
-			bc->hdlc_tx.shreg1 = hdlc_tx_word(bc);
-#ifdef HDLC_LOOPBACK
-			hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
-#endif /* HDLC_LOOPBACK */
-#endif /* BAYCOM_USE_BH */
-		}
-		return;
-	}
-	/*
-	 * do receiver; differential decode and descramble on the fly
-	 */
-	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
-		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
-		if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT)
-			bc->modem.par96.descram |= 1;
-		descx = bc->modem.par96.descram ^ 
-			(bc->modem.par96.descram >> 1);
-		/* now the diff decoded data is inverted in descram */
-		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase));
-		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
-			  (descx >> PAR96_DESCRAM_TAPSH2));
-		data >>= 1;
-		if (!(descx & 1))
-			data |= 0x8000;
-		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, 
-		     LPT_DATA(bc->iobase));
-	}
-#ifdef BAYCOM_USE_BH
-	bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1;
-	bc->hdlc_rx.shreg1 = data | 0x10000;
-	queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-	mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-	hdlc_rx_word(bc, data);
-#endif /* BAYCOM_USE_BH */
-	/*
-	 * do DCD algorithm
-	 */
-	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
-			| (data << 16);
-		/* search for flags and set the dcd counter appropriately */
-		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; 
-		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
-			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
-				bc->modem.par96.dcd_count = BAYCOM_MAXFLEN+4;
-		/* check for abort/noise sequences */
-		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; 
-		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
-			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
-			    (bc->modem.par96.dcd_count >= 0))
-				bc->modem.par96.dcd_count -= BAYCOM_MAXFLEN-10;
-		/* decrement and set the dcd variable */
-		if (bc->modem.par96.dcd_count >= 0)
-			bc->modem.par96.dcd_count -= 2;
-		bc->modem.dcd = bc->modem.par96.dcd_count > 0;
-	} else {
-		bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase))
-				   & PAR96_DCD);
-	}
-	if (--bc->modem.arb_divider <= 0) {
-#ifdef BAYCOM_USE_BH
-		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
-		mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-		tx_arbitrate(bc);
-#endif /* BAYCOM_USE_BH */
-		bc->modem.arb_divider = bc->ch_params.slottime * 6;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_check_lpt(unsigned int iobase)
-{
-	unsigned char b1,b2;
-	int i;
-
-	b1 = inb(LPT_DATA(iobase));
-	b2 = inb(LPT_CONTROL(iobase));
-	outb(0xaa, LPT_DATA(iobase));
-	i = inb(LPT_DATA(iobase)) == 0xaa;
-	outb(0x55, LPT_DATA(iobase));
-	i &= inb(LPT_DATA(iobase)) == 0x55;
-	outb(0x0a, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
-	outb(0x05, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
-	outb(b1, LPT_DATA(iobase));
-	outb(b2, LPT_CONTROL(iobase));
-	return !i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_allocate_resources(unsigned int iobase, unsigned int irq,
-				    unsigned int options)
-{
-	if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
-		return -ENXIO;
-	if (check_region(iobase, PAR96_EXTENT))
-		return -EACCES;
-	if (par96_check_lpt(iobase))
-		return -EIO;
-	request_region(iobase, PAR96_EXTENT, "baycom_par96");
-	outb(0, LPT_CONTROL(iobase));                 /* disable interrupt */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(iobase)); /* switch off PTT */
-	printk(KERN_INFO "baycom: par96 at iobase 0x%x irq %u options 0x%x\n", 
-	       iobase, irq, options);
-	return 0;
-}
-	
-/* --------------------------------------------------------------------- */
-
-static void par96_deallocate_resources(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
-		return;
-	outb(0, LPT_CONTROL(bc->iobase));      /* disable interrupt */
-	outb(PAR96_PTT, LPT_DATA(bc->iobase)); /* switch off PTT */
-	/* 
-	 * this should prevent kernel: Trying to free IRQx
-	 * messages
-	 */
-	if (bc->opened > 0)
-		free_irq(bc->irq, bc);
-	release_region(bc->iobase, PAR96_EXTENT);
-	bc->modem_type = BAYCOM_MODEM_INVALID;
-	printk(KERN_INFO "baycom: release par96 at iobase 0x%x irq %u\n",
-	       bc->iobase, bc->irq);
-	bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_on_open(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
-		return -ENXIO;
-	outb(0, LPT_CONTROL(bc->iobase));      /* disable interrupt */
-	 /* switch off PTT */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
-	if (request_irq(bc->irq, baycom_par96_interrupt, SA_INTERRUPT, 
-			"baycom_par96", bc))
-		return -EBUSY;
-	outb(LPT_IRQ_ENABLE, LPT_CONTROL(bc->iobase));  /* enable interrupt */
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_on_close(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
-		return;
-	outb(0, LPT_CONTROL(bc->iobase));  /* disable interrupt */
-	/* switch off PTT */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
-	free_irq(bc->irq, bc);	
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== Bottom half (soft interrupt) ====================
- */
-
-#ifdef BAYCOM_USE_BH
-static void bh_receiver(void *private)
-{
-	struct baycom_state *bc = (struct baycom_state *)private;
-	unsigned int temp;
-
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	if (!bc->hdlc_rx.shreg2)
-		return;
-	temp = bc->hdlc_rx.shreg2;
-	bc->hdlc_rx.shreg2 = 0;
-	hdlc_rx_word(bc, temp);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void bh_transmitter(void *private)
-{
-	struct baycom_state *bc = (struct baycom_state *)private;
-
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	if (bc->hdlc_tx.shreg2)
-		return;
-	bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void bh_arbitrate(void *private)
-{
-	struct baycom_state *bc = (struct baycom_state *)private;
-
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	tx_arbitrate(bc);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_bottom_half(void)
-{
-	run_task_queue(&tq_baycom);
-}
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== TTY interface routines ==========================
- */
-
-static inline int baycom_paranoia_check(struct baycom_state *bc, 
-					const char *routine)
-{
-	if (!bc || bc->magic != BAYCOM_MAGIC) {
-		printk(KERN_ERR "baycom: bad magic number for baycom struct "
-		       "in routine %s\n", routine);
-		return 1;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * Here the tty driver code starts
- */
-
-static void baycom_put_fend(struct baycom_state *bc)
-{
-	if (bc->kiss_decode.wr <= 0 ||
-	    (bc->kiss_decode.pkt_buf[0] & 0xf0) != 0)
-		return;
-
-	switch (bc->kiss_decode.pkt_buf[0] & 0xf) {
-	case KISS_CMD_DATA:
-		if (bc->kiss_decode.wr <= 8) 
-			break;
-		if (!store_packet(&bc->tx_buf, bc->kiss_decode.pkt_buf+1, 0, 
-				  bc->kiss_decode.wr-1))
-			bc->stat.tx_bufferoverrun++;
-		break;
-
-	case KISS_CMD_TXDELAY:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.tx_delay = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: TX delay = %ums\n", 
-		       bc->ch_params.tx_delay * 10);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_PPERSIST:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: p-persistence = %u\n", 
-		       bc->ch_params.ppersist);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_SLOTTIME:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: slottime = %ums\n", 
-		       bc->ch_params.slottime * 10);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_TXTAIL:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.tx_tail = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: TX tail = %ums\n",
-		       bc->ch_params.tx_tail * 10);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_FULLDUP:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.fulldup = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: %s duplex\n", 
-		       bc->ch_params.fulldup ? "full" : "half");
-#endif /* KISS_VERBOSE */
-		break;
-
-	default:
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: unhandled KISS packet code %u\n",
-		       bc->kiss_decode.pkt_buf[0] & 0xf);
-#endif /* KISS_VERBOSE */
-		break;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_put_char(struct tty_struct *tty, unsigned char ch)
-{
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return;
-	if (baycom_paranoia_check(bc = tty->driver_data, "put_char"))
-		return;
-		
-	if (ch == KISS_FEND) {
-		baycom_put_fend(bc);
-		bc->kiss_decode.wr = 0;
-		bc->kiss_decode.escaped = 0;
-		bc->kiss_decode.dec_state = 1;
-		return;
-	}
-	if (!bc->kiss_decode.dec_state)
-		return;
-	if (ch == KISS_FESC) {
-		bc->kiss_decode.escaped = 1;
-		return;
-	}
-	if (bc->kiss_decode.wr >= sizeof(bc->kiss_decode.pkt_buf)) {
-		bc->kiss_decode.wr = 0;
-		bc->kiss_decode.dec_state = 0;
-		return;
-	}
-	if (bc->kiss_decode.escaped) {
-		if (ch == KISS_TFEND)
-			bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = 
-				KISS_FEND;
-		else if (ch == KISS_TFESC)
-			bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = 
-				KISS_FESC;
-		else {
-			bc->kiss_decode.wr = 0;
-			bc->kiss_decode.dec_state = 0;
-		}
-		bc->kiss_decode.escaped = 0;
-		return;
-	}
-	bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = ch;
-}
-	
-/* --------------------------------------------------------------------- */
-
-static int baycom_write(struct tty_struct * tty, int from_user,
-	const unsigned char *buf, int count)
-{
-	int c;
-	const unsigned char *bp;
-	struct baycom_state *bc;
-		
-	if (!tty || !buf || count <= 0)
-		return count;
-	
-	if (baycom_paranoia_check(bc = tty->driver_data, "write"))
-		return count; 
-		
-	if (from_user) {
-		for(c = count, bp = buf; c > 0; c--,bp++)
-			baycom_put_char(tty, get_user(bp));
-	} else {
-		for(c = count, bp = buf; c > 0; c--,bp++)
-			baycom_put_char(tty, *bp);
-	}
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup)(tty);
-	wake_up_interruptible(&tty->write_wait);
-	return count;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_write_room(struct tty_struct *tty)
-{
-	int free;
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return 0;
-	if (baycom_paranoia_check(bc = tty->driver_data, "write_room"))
-		return 0;
-		
-	free = bc->tx_buf.rd - bc->tx_buf.wr;
-	if (free <= 0) {
-		free = bc->tx_buf.buflen - bc->tx_buf.wr;
-		if (free < bc->tx_buf.rd)
-			free = bc->tx_buf.rd;	/* we may fold */
-	}
-
-	return free / 2; /* a rather pessimistic estimate */
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_chars_in_buffer(struct tty_struct *tty)
-{
-	int cnt;
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return 0;
-	if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer"))
-		return 0;
-
-	cnt = bc->tx_buf.wr - bc->tx_buf.rd;
-	if (cnt < 0)
-		cnt += bc->tx_buf.buflen;
-		
-	return cnt;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_flush_buffer(struct tty_struct *tty)
-{
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return;
-	if (baycom_paranoia_check(bc = tty->driver_data, "flush_buffer"))
-		return;
-
-	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup)(tty);
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void baycom_dealloc_hw(struct baycom_state *bc) 
-{
-	if (!bc || bc->magic != BAYCOM_MAGIC || 
-	    bc->modem_type == BAYCOM_MODEM_INVALID)
-		return;
-	switch(bc->modem_type) {
-	case BAYCOM_MODEM_SER12:
-		ser12_deallocate_resources(bc);
-		break;
-	case BAYCOM_MODEM_PAR96:
-		par96_deallocate_resources(bc);
-		break;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_set_hardware(struct baycom_state *bc,
-			       unsigned int modem_type, unsigned int iobase, 
-			       unsigned int irq, unsigned int options)
-{
-	int i;
-
-	if (!bc)
-		return -EINVAL;
-
-	if (modem_type == BAYCOM_MODEM_SER12) {
-		i = ser12_allocate_resources(iobase, irq, options);
-		if (i < 0)
-			return i;
-	} else if (modem_type == BAYCOM_MODEM_PAR96) {
-		i = par96_allocate_resources(iobase, irq, options);
-		if (i < 0)
-			return i;
-	} else if (modem_type == BAYCOM_MODEM_INVALID) {
-		iobase = irq = options = 0;
-	} else {
-		return -ENXIO;
-	}
-	baycom_dealloc_hw(bc);
-	bc->modem_type = modem_type;
-	bc->iobase = iobase;
-	bc->irq = irq;
-	bc->options = options;
-	i = 0;
-	if (bc->opened > 0) {
-		switch(bc->modem_type) {
-		case BAYCOM_MODEM_SER12:
-			i = ser12_on_open(bc);
-			break;
-		case BAYCOM_MODEM_PAR96:
-			i = par96_on_open(bc);
-			break;
-		}
-	}
-	return i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct tty_struct *tty, struct file * file,
-	unsigned int cmd, unsigned long arg)
-{
-	int i;
-	struct baycom_state *bc;
-	struct baycom_params par;
-		
-	if (!tty)
-		return -EINVAL;
-	if (baycom_paranoia_check(bc = tty->driver_data, "ioctl"))
-		return -EINVAL;
-		
-	switch (cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-	case TIOCMGET:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-		if (i)
-			return i;
-		i = (bc->modem.dcd ? TIOCM_CAR : 0) |
-			(bc->hdlc_tx.ptt ? TIOCM_RTS : 0);
-		put_user(i, (int *) arg);
-		return 0;
-		
-	case BAYCOMCTL_GETDCD:
-		i = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned char));
-		if (!i)
-			put_user(bc->modem.dcd, (unsigned char *) arg);
-		return i;
-		
-	case BAYCOMCTL_GETPTT:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned char));
-		if (!i)
-			put_user(bc->hdlc_tx.ptt, (unsigned char *) arg);
-		return i;
-		
-	case BAYCOMCTL_PARAM_TXDELAY:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.tx_delay = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_PPERSIST:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.ppersist = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_SLOTTIME:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.slottime = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_TXTAIL:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.tx_tail = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_FULLDUP:
-		bc->ch_params.fulldup = arg ? 1 : 0;
-		return 0;
-		
-	case BAYCOMCTL_CALIBRATE:
-		bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ?
-				       600 : 75);
-		return 0;
-
-	case BAYCOMCTL_GETPARAMS:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(par));
-		if (i)
-			return i;
-		par.modem_type = bc->modem_type;
-		par.iobase = bc->iobase;
-		par.irq = bc->irq;
-		par.options = bc->options;
-		par.tx_delay = bc->ch_params.tx_delay;
-		par.tx_tail = bc->ch_params.tx_tail;
-		par.slottime = bc->ch_params.slottime;
-		par.ppersist = bc->ch_params.ppersist;
-		par.fulldup = bc->ch_params.fulldup;
-		copy_to_user((void *)arg, &par, sizeof(par));
-		return 0;
-
-	case BAYCOMCTL_SETPARAMS:
-		if (!suser())
-			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *) arg, 
-				sizeof(par));
-		if (i)
-			return i;
-		copy_from_user(&par, (void *)arg, sizeof(par));
-		printk(KERN_INFO "baycom: changing hardware type: modem %u "
-		       "iobase 0x%x irq %u options 0x%x\n", par.modem_type,
-		       par.iobase, par.irq, par.options);
-		i = baycom_set_hardware(bc, par.modem_type, par.iobase,
-					par.irq, par.options); 
-		if (i)
-			return i;
-		bc->ch_params.tx_delay = par.tx_delay;
-		bc->ch_params.tx_tail = par.tx_tail;
-		bc->ch_params.slottime = par.slottime;
-		bc->ch_params.ppersist = par.ppersist;
-		bc->ch_params.fulldup = par.fulldup;
-		return 0;
-
-	case BAYCOMCTL_GETSTAT:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(struct baycom_statistics));
-		if (i)
-			return i;
-		copy_to_user((void *)arg, &bc->stat, 
-			    sizeof(struct baycom_statistics));
-		return 0;
-		
-
-#ifdef BAYCOM_DEBUG
-	case BAYCOMCTL_GETSAMPLES:
-		if (bc->bitbuf_channel.rd == bc->bitbuf_channel.wr) 
-			return -EAGAIN;
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned char));
-		if (!i) {
-			put_user(bc->bitbuf_channel.buffer
-				 [bc->bitbuf_channel.rd],
-				 (unsigned char *) arg);
-			bc->bitbuf_channel.rd = (bc->bitbuf_channel.rd+1) %
-				sizeof(bc->bitbuf_channel.buffer);
-		}
-		return i;
-		
-	case BAYCOMCTL_GETBITS:
-		if (bc->bitbuf_hdlc.rd == bc->bitbuf_hdlc.wr) 
-			return -EAGAIN;
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned char));
-		if (!i) {
-			put_user(bc->bitbuf_hdlc.buffer[bc->bitbuf_hdlc.rd],
-				 (unsigned char *) arg);
-			bc->bitbuf_hdlc.rd = (bc->bitbuf_hdlc.rd+1) %
-				sizeof(bc->bitbuf_hdlc.buffer);
-		}
-		return i;
-		
-	case BAYCOMCTL_DEBUG1:
-		i = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned long));
-		if (!i)
-			put_user((bc->rx_buf.wr-bc->rx_buf.rd) % 
-				 bc->rx_buf.buflen, (unsigned long *)arg);
-		return i;
-		
-	case BAYCOMCTL_DEBUG2:
-		i = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned long));
-		if (!i)
-			put_user(bc->debug_vals.last_intcnt, 
-				 (unsigned long *)arg);
-		return i;
-		
-	case BAYCOMCTL_DEBUG3:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned long));
-		if (!i)
-			put_user((long)bc->debug_vals.last_pllcorr,
-				 (long *)arg);
-		return i;		
-#endif /* BAYCOM_DEBUG */
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-int baycom_open(struct tty_struct *tty, struct file * filp)
-{
-	int line;
-	struct baycom_state *bc;
-	int i;
-
-	if(!tty)
-		return -ENODEV;
-
-	line = MINOR(tty->device) - tty->driver.minor_start;
-	if (line < 0 || line >= NR_PORTS)
-		return -ENODEV;
-	bc = baycom_state+line;
-
-	if (bc->opened > 0) {
-		bc->opened++;
-		MOD_INC_USE_COUNT;
-		return 0;
-	}
-	/*
-	 * initialise some variables
-	 */
-	bc->calibrate = 0;
-
-	/*
-	 * allocate the buffer space
-	 */
-	if (bc->rx_buf.buffer)
-		kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-	if (bc->tx_buf.buffer)
-		kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-	bc->rx_buf.buflen = BUFLEN_RX;
-	bc->tx_buf.buflen = BUFLEN_TX;
-	bc->rx_buf.rd = bc->rx_buf.wr = 0;
-	bc->tx_buf.rd = bc->tx_buf.wr = 0;
-	bc->rx_buf.buffer = kmalloc(bc->rx_buf.buflen, GFP_KERNEL);
-	bc->tx_buf.buffer = kmalloc(bc->tx_buf.buflen, GFP_KERNEL);
-	if (!bc->rx_buf.buffer || !bc->tx_buf.buffer) {
-		if (bc->rx_buf.buffer)
-			kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-		if (bc->tx_buf.buffer)
-			kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-		bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
-		bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
-		return -ENOMEM;
-	}
-	/*
-	 * check if the modem type has been set
-	 */
-	switch(bc->modem_type) {
-	case BAYCOM_MODEM_SER12:
-		i = ser12_on_open(bc);
-		break;
-	case BAYCOM_MODEM_PAR96:
-		i = par96_on_open(bc);
-		break;
-	case BAYCOM_MODEM_INVALID:
-		/*
-		 * may open even if no hardware specified, in order to
-		 * subsequently allow the BAYCOMCTL_SETPARAMS ioctl
-		 */
-		i = 0;
-		break;
-	default:
-		return -ENODEV;
-	}
-	if (i) 
-		return i;
-
-	bc->opened++;
-	MOD_INC_USE_COUNT;
-
-	tty->driver_data = bc;
-	bc->tty = tty;
-
-	return 0;   
-}
-
-
-/* --------------------------------------------------------------------- */
-	
-static void baycom_close(struct tty_struct *tty, struct file * filp)
-{
-	struct baycom_state *bc;
-		
-	if(!tty) return;
-	if (baycom_paranoia_check(bc = tty->driver_data, "close"))
-		return;
-
-	MOD_DEC_USE_COUNT;
-	bc->opened--;
-	if (bc->opened <= 0) {
-		switch(bc->modem_type) {
-		case BAYCOM_MODEM_SER12:
-			ser12_on_close(bc);
-			break;
-		case BAYCOM_MODEM_PAR96:
-			par96_on_close(bc);
-			break;
-		}
-		tty->driver_data = NULL;
-		bc->tty = NULL;
-		bc->opened = 0;
-		/*
-		 * free the buffers 
-		 */
-		bc->rx_buf.rd = bc->rx_buf.wr = 0;
-		bc->tx_buf.rd = bc->tx_buf.wr = 0;
-		if (bc->rx_buf.buffer)
-			kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-		if (bc->tx_buf.buffer)
-			kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-		bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
-		bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * And now the modules code and kernel interface.
- */
-
-static void init_channel(struct baycom_state *bc)
-{
-	struct access_params dflt_ch_params = { 20, 2, 10, 40, 0 };
-
-	if (!bc)
-		return;
-
-	bc->hdlc_rx.rx_state = 0;
-
-	bc->hdlc_tx.tx_state = bc->hdlc_tx.numflags = 0;
-	bc->hdlc_tx.bitstream = 0;
-	bc->hdlc_tx.current_byte = bc->hdlc_tx.ptt = 0;
-
-	memset(&bc->modem, 0, sizeof(bc->modem));
-
-#ifdef BAYCOM_DEBUG
-	bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0;
-	bc->bitbuf_channel.shreg = 0x80;
-
-	bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0;
-	bc->bitbuf_hdlc.shreg = 0x80;
-#endif /* BAYCOM_DEBUG */
-
-	bc->kiss_decode.dec_state = bc->kiss_decode.escaped = 
-	bc->kiss_decode.wr = 0;
-
-	bc->ch_params = dflt_ch_params;
-
-#ifdef BAYCOM_USE_BH
-	bc->tq_receiver.next = bc->tq_transmitter.next =
-		bc->tq_arbitrate.next = NULL;
-	bc->tq_receiver.sync = bc->tq_transmitter.sync =
-		bc->tq_arbitrate.sync = 0;
-	bc->tq_receiver.data = bc->tq_transmitter.data =
-		bc->tq_arbitrate.data = bc;
-	bc->tq_receiver.routine = bh_receiver;
-	bc->tq_transmitter.routine = bh_transmitter;
-	bc->tq_arbitrate.routine = bh_arbitrate;
-#endif /* BAYCOM_USE_BH */
-}
-
-static void init_datastructs(void)
-{
-	int i;
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct baycom_state *bc = baycom_state+i;
-
-		bc->magic = BAYCOM_MAGIC;
-		bc->modem_type = BAYCOM_MODEM_INVALID;
-		bc->iobase = bc->irq = bc->options = bc->opened = 0;
-		bc->tty = NULL;
-
-		bc->rx_buf.rd = bc->rx_buf.wr = 0;
-		bc->rx_buf.buflen = 0;
-		bc->rx_buf.buffer = NULL;
-
-		bc->tx_buf.rd = bc->tx_buf.wr = 0;
-		bc->tx_buf.buflen = 0;
-		bc->tx_buf.buffer = NULL;
-
-		memset(&bc->stat, 0, sizeof(bc->stat));
-
-		init_channel(bc);
-	}
-}
-
-int baycom_init(void) {
-	int i, j;
-
-	/*
-	 * initialize the data structures
-	 */
-	init_datastructs();
-	/*
-	 * initialize bottom half handler
- 	 */
-#ifdef BAYCOM_USE_BH
-	init_bh(BAYCOM_BH, baycom_bottom_half);
-#endif /* BAYCOM_USE_BH */
-	/*
-	 * register the driver as tty driver
-	 */
-	memset(&baycom_driver, 0, sizeof(struct tty_driver));
-	baycom_driver.magic = TTY_DRIVER_MAGIC;
-	baycom_driver.name = "baycom";
-	baycom_driver.major = major;
-	baycom_driver.minor_start = 0;
-	baycom_driver.num = NR_PORTS;
-	baycom_driver.type = TTY_DRIVER_TYPE_BAYCOM;
-	baycom_driver.subtype = BAYCOM_TYPE_NORMAL;
-	baycom_driver.init_termios.c_iflag = 0;
-	baycom_driver.init_termios.c_oflag = 0;
-	baycom_driver.init_termios.c_cflag = CS8 | B1200 | CREAD | CLOCAL;
-	baycom_driver.init_termios.c_lflag = 0;
-	baycom_driver.flags = TTY_DRIVER_REAL_RAW;
-	baycom_driver.refcount = &baycom_refcount;
-	baycom_driver.table = baycom_table;
-	baycom_driver.termios = baycom_termios;
-	baycom_driver.termios_locked = baycom_termios_locked;
-	/*
-	 * the functions
-	 */
-	baycom_driver.open = baycom_open;
-	baycom_driver.close = baycom_close;
-	baycom_driver.write = baycom_write;
-	baycom_driver.put_char = baycom_put_char;
-	baycom_driver.flush_chars = NULL;
-	baycom_driver.write_room = baycom_write_room;
-	baycom_driver.chars_in_buffer = baycom_chars_in_buffer;
-	baycom_driver.flush_buffer = baycom_flush_buffer;
-	baycom_driver.ioctl = baycom_ioctl;
-	/*
-	 * cannot throttle the transmitter on this layer
-	 */
-	baycom_driver.throttle = NULL;
-	baycom_driver.unthrottle = NULL;
-	/*
-	 * no special actions on termio changes
-	 */
-	baycom_driver.set_termios = NULL;
-	/*
-	 * no XON/XOFF and no hangup on the radio port
-	 */
-	baycom_driver.stop = NULL;
-	baycom_driver.start = NULL;
-	baycom_driver.hangup = NULL;
-	baycom_driver.set_ldisc = NULL;
-
-	if (tty_register_driver(&baycom_driver)) {
-		printk(KERN_WARNING "baycom: tty_register_driver failed\n");
-		return -EIO;
-	}
-
-	for (i = 0; i < NR_PORTS && 
-	     baycom_ports[i].modem != BAYCOM_MODEM_INVALID; i++) {
-		j = baycom_set_hardware(baycom_state+i, 
-					baycom_ports[i].modem,
-					baycom_ports[i].iobase, 
-					baycom_ports[i].irq, 
-					baycom_ports[i].options);
-		if (j < 0) {
-			const char *s;
-			switch (-j) {
-			case ENXIO:
-				s = "invalid iobase and/or irq";
-				break;
-			case EACCES:
-				s = "io region already used";
-				break;
-			case EIO:
-				s = "no uart/lpt port at iobase";
-				break;
-			case EBUSY:
-				s = "interface already in use";
-				break;
-			case EINVAL:
-				s = "internal error";
-				break;
-			default:
-				s = "unknown error";
-				break;
-			}
-			printk(KERN_WARNING "baycom: modem %u iobase 0x%x "
-			       "irq %u: (%i) %s\n", baycom_ports[i].modem, 
-			       baycom_ports[i].iobase, baycom_ports[i].irq, 
-			       j, s);
-		}
-	}
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-int modem = BAYCOM_MODEM_INVALID;
-int iobase = 0x3f8;
-int irq = 4;
-int options = BAYCOM_OPTIONS_SOFTDCD;
-
-int init_module(void)
-{
-	int i;
-
-	printk(KERN_INFO "baycom: init_module called\n");
-
-	baycom_ports[0].modem = modem;
-	baycom_ports[0].iobase = iobase;
-	baycom_ports[0].irq = irq;
-	baycom_ports[0].options = options;
-	baycom_ports[1].modem = BAYCOM_MODEM_INVALID;
-
-	i = baycom_init();
-	if (i)
-		return i;
-
-	printk(KERN_INFO "baycom: version 0.3; "
-	       "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n");
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	printk(KERN_INFO "baycom: cleanup_module called\n");
-
-	disable_bh(BAYCOM_BH);
-	if (tty_unregister_driver(&baycom_driver))
-		printk(KERN_WARNING "baycom: failed to unregister tty "
-		       "driver\n");
-	for(i = 0; i < NR_PORTS; i++) {
-		struct baycom_state *bc = baycom_state+i;
-
-		if (bc->magic != BAYCOM_MAGIC)
-			printk(KERN_ERR "baycom: invalid magic in "
-			       "cleanup_module\n");
-		else {
-			baycom_dealloc_hw(bc);
-			/*
-			 * free the buffers 
-			 */
-			bc->rx_buf.rd = bc->rx_buf.wr = 0;
-			bc->tx_buf.rd = bc->tx_buf.wr = 0;
-			if (bc->rx_buf.buffer)
-				kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-			if (bc->tx_buf.buffer)
-				kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-			bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
-			bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom=modem,io,irq,options[,modem,io,irq,options]
- * modem=1: ser12, modem=2: par96
- * options=0: hardware DCD, options=1: software DCD
- */
-
-void baycom_setup(char *str, int *ints)
-{
-	int i;
-
-	for (i = 0; i < NR_PORTS; i++) 
-		if (ints[0] >= 4*i+4) {
-			baycom_ports[i].modem = ints[4*i+1];
-			baycom_ports[i].iobase = ints[4*i+2];
-			baycom_ports[i].irq = ints[4*i+3];
-			baycom_ports[i].options = ints[4*i+4];
-		} else
-			baycom_ports[i].modem = BAYCOM_MODEM_INVALID;
-
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-ctl.c linux/drivers/char/ftape/ftape-ctl.c
--- v2.1.6/linux/drivers/char/ftape/ftape-ctl.c	Sat Oct 19 10:07:30 1996
+++ linux/drivers/char/ftape/ftape-ctl.c	Wed Oct 30 01:40:36 1996
@@ -23,7 +23,7 @@
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 
 #include "tracing.h"
 #include "ftape-eof.h"
diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-io.c linux/drivers/char/ftape/ftape-io.c
--- v2.1.6/linux/drivers/char/ftape/ftape-io.c	Sat Apr 13 11:23:24 1996
+++ linux/drivers/char/ftape/ftape-io.c	Wed Oct 30 01:40:36 1996
@@ -30,7 +30,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/ioctl.h>
 #include <linux/mtio.h>
diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-read.c linux/drivers/char/ftape/ftape-read.c
--- v2.1.6/linux/drivers/char/ftape/ftape-read.c	Sat Oct 19 10:07:30 1996
+++ linux/drivers/char/ftape/ftape-read.c	Wed Oct 30 01:40:36 1996
@@ -30,7 +30,7 @@
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 
 #include "tracing.h"
 #include "ftape-read.h"
diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-write.c linux/drivers/char/ftape/ftape-write.c
--- v2.1.6/linux/drivers/char/ftape/ftape-write.c	Sat Oct 19 10:07:30 1996
+++ linux/drivers/char/ftape/ftape-write.c	Wed Oct 30 01:40:36 1996
@@ -33,7 +33,7 @@
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 
 #include "tracing.h"
 #include "ftape-write.h"
diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/kernel-interface.c linux/drivers/char/ftape/kernel-interface.c
--- v2.1.6/linux/drivers/char/ftape/kernel-interface.c	Mon Sep 30 10:43:38 1996
+++ linux/drivers/char/ftape/kernel-interface.c	Wed Oct 30 01:40:36 1996
@@ -24,7 +24,7 @@
 #include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/major.h>
diff -u --recursive --new-file v2.1.6/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.6/linux/drivers/char/misc.c	Thu Oct 10 19:10:54 1996
+++ linux/drivers/char/misc.c	Tue Oct 29 20:13:02 1996
@@ -184,12 +184,14 @@
 #include <linux/symtab_end.h>
 };
 
+#if defined(CONFIG_PROC_FS) && !defined(MODULE)
 static struct proc_dir_entry proc_misc = {
 	0, 4, "misc",
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, NULL /* ops -- default to array */,
 	&proc_misc_read /* get_info */,
 };
+#endif
 
 int misc_init(void)
 {
diff -u --recursive --new-file v2.1.6/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.6/linux/drivers/char/tty_io.c	Tue Oct 29 19:58:10 1996
+++ linux/drivers/char/tty_io.c	Thu Oct 31 11:54:25 1996
@@ -1941,9 +1941,6 @@
 #ifdef CONFIG_RISCOM8
 	riscom8_init();
 #endif
-#ifdef CONFIG_BAYCOM
-	baycom_init();
-#endif
 	pty_init();
 	vcs_init();
 	return 0;
diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.1.6/linux/drivers/isdn/isdn_common.c	Tue Oct 29 19:58:10 1996
+++ linux/drivers/isdn/isdn_common.c	Wed Oct 30 14:27:57 1996
@@ -117,6 +117,7 @@
  *
  */
 
+#include <asm/uaccess.h>
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
@@ -915,7 +916,7 @@
 	wake_up_interruptible(&(dev->info_waitq));
 }
 
-static int isdn_read(struct inode *inode, struct file *file, char *buf, int count)
+static long isdn_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
 {
 	uint minor = MINOR(inode->i_rdev);
 	int len = 0;
@@ -988,12 +989,13 @@
 	return -ENODEV;
 }
 
-static int isdn_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
+static long long isdn_lseek(struct inode *inode, struct file *file, long long offset, int orig)
 {
 	return -ESPIPE;
 }
 
-static int isdn_write(struct inode *inode, struct file *file, const char *buf, int count)
+static long isdn_write(struct inode *inode, struct file *file,
+	const char *buf, unsigned long count)
 {
 	uint minor = MINOR(inode->i_rdev);
 	int drvidx;
diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.1.6/linux/drivers/isdn/isdn_net.c	Tue Oct 29 19:58:10 1996
+++ linux/drivers/isdn/isdn_net.c	Wed Oct 30 14:26:48 1996
@@ -106,6 +106,7 @@
  *
  */
 
+#include <asm/uaccess.h>
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.1.6/linux/drivers/isdn/isdn_ppp.c	Tue Oct 29 19:58:10 1996
+++ linux/drivers/isdn/isdn_ppp.c	Wed Oct 30 14:26:48 1996
@@ -73,6 +73,7 @@
 
 /* TODO: right tbusy handling when using MP */
 
+#include <asm/uaccess.h>
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
@@ -391,7 +392,7 @@
 	switch (cmd) {
 	case PPPIOCBUNDLE:
 #ifdef CONFIG_ISDN_MPP
-		if ((r = get_user(val, arg)))
+		if ((r = get_user(val, argp)))
 			return r;
 		printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
                         (int) min, (int) is->unit, (int) val);
@@ -588,7 +589,6 @@
 {
 	struct ippp_struct *is;
 	struct ippp_buf_queue *b;
-	int r;
 	unsigned long flags;
 
 	is = file->private_data;
@@ -1445,7 +1445,6 @@
 {
 	struct ppp_stats *res, t;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	int err;
 
 	res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
 
@@ -1476,7 +1475,7 @@
 
 int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {
-	int error;
+	int error = 0;
 	char *r;
 	int len;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.1.6/linux/drivers/isdn/isdn_tty.c	Tue Oct 29 19:58:11 1996
+++ linux/drivers/isdn/isdn_tty.c	Wed Oct 30 14:26:48 1996
@@ -109,6 +109,7 @@
  *
  */
 
+#include <asm/uaccess.h>
 #define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c
--- v2.1.6/linux/drivers/isdn/teles/callc.c	Tue Oct 29 19:58:11 1996
+++ linux/drivers/isdn/teles/callc.c	Wed Oct 30 14:26:48 1996
@@ -47,6 +47,7 @@
  */
 #define __NO_VERSION__
 #include "teles.h"
+#include <asm/uaccess.h>
 
 extern struct IsdnCard cards[];
 extern int      nrcards;
diff -u --recursive --new-file v2.1.6/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.6/linux/drivers/net/Config.in	Thu Oct 10 19:10:55 1996
+++ linux/drivers/net/Config.in	Wed Oct 30 14:15:12 1996
@@ -25,7 +25,8 @@
 bool 'Radio network interfaces' CONFIG_NET_RADIO
 if [ "$CONFIG_NET_RADIO" != "n" ]; then
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'BAYCOM ser12 and par96 kiss emulation driver for AX.25' CONFIG_BAYCOM
+    tristate 'BAYCOM ser12 and par96 driver for AX.25' CONFIG_BAYCOM
+    tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM
   fi
   if [ "$CONFIG_AX25" = "y" ]; then
     bool 'Gracilis PackeTwin support' CONFIG_PT
diff -u --recursive --new-file v2.1.6/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.6/linux/drivers/net/Makefile	Tue Oct 29 19:58:11 1996
+++ linux/drivers/net/Makefile	Wed Oct 30 14:15:12 1996
@@ -18,6 +18,8 @@
 CONFIG_8390_MODULE  :=
 CONFIG_SLHC_BUILTIN :=
 CONFIG_SLHC_MODULE  :=
+CONFIG_HDLCDRV_BUILTIN :=
+CONFIG_HDLCDRV_MODULE  :=
 
 ifeq ($(CONFIG_ISDN),y)
   ifeq ($(CONFIG_ISDN_PPP),y)
@@ -517,6 +519,37 @@
   M_OBJS += dgrs.o
   endif
 endif
+
+ifeq ($(CONFIG_BAYCOM),y)
+L_OBJS += baycom.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_BAYCOM),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += baycom.o
+  endif
+endif
+
+ifeq ($(CONFIG_SOUNDMODEM),y)
+L_OBJS += soundmodem.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_SOUNDMODEM),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += soundmodem.o
+  endif
+endif
+
+# If anything built-in uses the hdlcdrv, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_HDLCDRV_BUILTIN
+LX_OBJS += hdlcdrv.o
+else
+  ifdef CONFIG_HDLCDRV_MODULE
+  MX_OBJS += hdlcdrv.o
+  endif
+endif
+
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.6/linux/drivers/net/README.baycom linux/drivers/net/README.baycom
--- v2.1.6/linux/drivers/net/README.baycom	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/README.baycom	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,118 @@
+		    LINUX DRIVER FOR BAYCOM MODEMS
+
+       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This document describes the Linux Kernel Driver for simple Baycom style
+amateur radio modems. The driver supports the following modems:
+
+ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+        of a modulator/demodulator chip, usually a TI TCM3105. The computer
+        is responsible for regenerating the receiver bit clock, as well as
+        for handling the HDLC protocol. The modem connects to a serial port,
+        hence the name. Since the serial port is not used as an async serial
+        port, the kernel driver for serial ports cannot be used, and this
+        driver only supports standard serial hardware (8250, 16450, 16550)
+
+par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+        The modem does all the filtering and regenerates the receiver clock.
+        Data is transferred from and to the PC via a shift register.
+        The shift register is filled with 16 bits and an interrupt is signalled.
+        The PC then empties the shift register in a burst. This modem connects
+        to the parallel port, hence the name. The modem leaves the 
+        implementation of the HDLC protocol and the scrambler polynomial to
+        the PC.
+
+picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
+        is protocol compatible to par96, but uses only three low power ICs
+        and can therefore be fed from the parallel port and does not require
+        an additional power supply. Furthermore, it incorporates a carrier
+        detect circuitry.
+
+All of the above modems only support half duplex communications. However,
+the driver supports the KISS (see below) fullduplex command. It then simply
+starts to send as soon as there's a packet to transmit and does not care
+about DCD, i.e. it starts to send even if there's someone else on the channel.
+This command is required by some implementations of the DAMA channel 
+access protocol.
+
+
+The Interface of the driver
+
+Unlike previous drivers, the driver is no longer a character device,
+but it is now a true kernel network interface. Installation is therefore
+simple. Once installed, four interfaces named bc[0-3] are available.
+sethdlc from the ax25 utilities may be used to set driver states etc.
+Users of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples:
+  insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
+  sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1
+
+Both lines configure the first port to drive a ser12 modem at the first
+serial port (COM1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+
+  insmod baycom modem=2 iobase=0x378 irq=7 options=1
+  sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1
+
+Both lines configure the first port to drive a par96 or par97 modem at the
+first parallel port (LPT1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+  
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Hardware DCD versus Software DCD
+
+To avoid collisions on the air, the driver must know when the channel is
+busy. This is the task of the DCD circuitry/software. The driver may either
+utilise a software DCD algorithm (options=1) or use a DCD signal from
+the hardware (options=0).
+
+ser12:  if software DCD is utilised, the radio's squelch should always be
+        open. It is highly recommended to use the software DCD algorithm,
+        as it is much faster than most hardware squelch circuitry. The
+        disadvantage is a slightly higher load on the system.
+
+par96:  the software DCD algorithm for this type of modem is rather poor.
+        The modem simply does not provide enough information to implement
+        a reasonable DCD algorithm in software. Therefore, if your radio
+        feeds the DCD input of the PAR96 modem, the use of the hardware
+        DCD circuitry is recommended.
+
+picpar: the picpar modem features a builtin DCD hardware, which is highly
+        recommended.
+
+
+
+Compatibility with the rest of the Linux kernel
+
+The serial driver, the line printer (lp) driver and the baycom driver compete
+for the same hardware resources. Of course only one driver can access a given
+interface at a time. The serial driver grabs all interfaces it can find at
+startup time. Therefore the baycom driver subsequently won't be able to
+access a serial port. You might therefore find it necessary to release
+a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
+# is the number of the interface. The baycom driver does not reserve any
+port at startup, unless one is specified on the 'insmod' command line. Another
+method to solve the problem is to compile all three drivers as modules and
+leave it to kerneld to load the correct driver depending on the application.
+
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.6/linux/drivers/net/README.soundmodem linux/drivers/net/README.soundmodem
--- v2.1.6/linux/drivers/net/README.soundmodem	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/README.soundmodem	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,88 @@
+	     LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
+
+       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss)
+compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK
+AX.25 packet radio modems. Only half duplex operation is supported; an
+attempt to include full duplex support failed because the hardware did
+not support it (it appeared that the card only provides one DMA channel,
+although the Codec chip would support two channels). The driver needs
+some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise
+interactive performance of the computer may become sluggish.
+
+
+The Interface of the driver
+
+The driver provides a kernel network drivers named sm[0-3]. sethdlc 
+from the ax25 utilities may be used to set driver states etc. Users
+of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Some sound cards need to be initialized before they operate in either
+SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this;
+you may use the standard linux sound driver to initialize the soundcard;
+compile it as a module, and do
+  insmod sound
+  rmmod sound
+The soundcard should then be initialized correctly. If this does not help,
+you'll have to write your own initialization utility.
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples:
+  insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1
+  sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1
+
+Both lines configure the first port to drive a soundblaster card
+in 1200 baud AFSK mode.
+
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Input and output levels
+
+It is important that the input and output levels are adjusted properly.
+There are two utilities, available in the ax25 utilities distribution,
+to facilitate this: smmixer and smdiag. smdiag allows you to display
+the input signal in an oscilloscope like display or an eye diagram.
+smmixer allows you to adjust input/output levels. See the respective
+man pages.
+
+
+Transmitter keying
+
+Since soundcards do not have a DC coupled output; PTT keying options include
+the following:
+* VOX circuitry
+* Serial port pin
+* Parallel port pin
+* MPU401 MIDI output via a retriggerable monoflop.
+Circuit schematics may be found at
+http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html.
+
+
+Compatibility with the rest of the Linux kernel
+
+The sound driver and the soundcard modem driver compete for the same
+hardware resources. Of course only one driver can access a given
+interface at a time. Worse yet, the sound driver grabs the soundcard
+at startup time. Therefore the soundcard modem driver subsequently won't
+be able to access the soundcard. You might therefore find it necessary to
+unload the sound driver before using the soundcard modem driver.
+
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.6/linux/drivers/net/baycom.c linux/drivers/net/baycom.c
--- v2.1.6/linux/drivers/net/baycom.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/baycom.c	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,969 @@
+/*****************************************************************************/
+
+/*
+ *	baycom.c  -- baycom ser12 and par96 radio modem driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+ *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ *          is responsible for regenerating the receiver bit clock, as well as
+ *          for handling the HDLC protocol. The modem connects to a serial port,
+ *          hence the name. Since the serial port is not used as an async serial
+ *          port, the kernel driver for serial ports cannot be used, and this
+ *          driver only supports standard serial hardware (8250, 16450, 16550)
+ *  
+ *  par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ *          The modem does all the filtering and regenerates the receiver clock.
+ *          Data is transferred from and to the PC via a shift register.
+ *          The shift register is filled with 16 bits and an interrupt is
+ *          signalled. The PC then empties the shift register in a burst. This
+ *          modem connects to the parallel port, hence the name. The modem
+ *          leaves the implementation of the HDLC protocol and the scrambler
+ *          polynomial to the PC. This modem is no longer available (at least
+ *          from Baycom) and has been replaced by the PICPAR modem (see below).
+ *          You may however still build one from the schematics published in
+ *          cq-DL :-).
+ *  
+ *  picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
+ *          modem is protocol compatible to par96, but uses only three low
+ *          power ICs and can therefore be fed from the parallel port and
+ *          does not require an additional power supply.
+ *
+ *
+ *  Command line options (insmod command line)
+ * 
+ *  modem    modem type of the first channel; 1=ser12,
+ *           2=par96/par97, any other value invalid
+ *  iobase   base address of the port; common values are for ser12 0x3f8,
+ *           0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
+ *  irq      interrupt line of the port; common values are for ser12 3,4
+ *           and for par96/par97 7
+ *  options  0=use hardware DCD, 1=use software DCD
+ * 
+ *
+ *  History:
+ *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#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/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+	int modem, iobase, irq, options;
+} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+#define PAR96_BURSTBITS 16
+#define PAR96_BURST     4
+#define PAR96_PTT       2
+#define PAR96_TXBIT     1
+#define PAR96_ACK       0x40
+#define PAR96_RXBIT     0x20
+#define PAR96_DCD       0x10
+#define PAR97_POWER     0xf8
+
+#define PAR96_EXTENT 3
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board. 
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	unsigned int options;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_ser12 {
+			unsigned char last_sample;
+			unsigned char interm_sample;
+			unsigned int bit_pll;
+			unsigned int dcd_shreg;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			unsigned int dcd_time;
+			unsigned char last_rxbit;
+			unsigned char tx_bit;
+		} ser12;
+		struct modem_state_par96 {
+			int dcd_count;
+			unsigned int dcd_shreg;
+			unsigned long descram;
+			unsigned long scram;
+		} par96;
+	} modem;
+	
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/* --------------------------------------------------------------------- */
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/* 
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+static void inline ser12_set_divisor(struct device *dev, 
+				     unsigned char divisor)
+{
+	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
+	outb(divisor, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */	
+	outb(0x00, THR(dev->base_addr));
+	/*
+	 * it is important not to set the divider while transmitting;
+	 * this reportedly makes some UARTs generating interrupts
+	 * in the hundredthousands per second region
+	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+	 */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * must call the TX arbitrator every 10ms
+ */
+#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+			       36 : 24)
+#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+				240 : 12)
+
+static inline void ser12_tx(struct device *dev, struct baycom_state *bc)
+{
+	/* one interrupt per channel bit */
+	ser12_set_divisor(dev, 12); 
+	/*
+	 * first output the last bit (!) then call HDLC transmitter,
+	 * since this may take quite long
+	 */
+	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+	if (bc->modem.shreg <= 1)
+		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ 
+				   (bc->modem.shreg & 1));
+	bc->modem.shreg >>= 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_rx(struct device *dev, struct baycom_state *bc)
+{
+	unsigned char cur_s;
+	/*
+	 * do demodulator
+	 */
+	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
+	hdlcdrv_channelbit(&bc->hdrv, cur_s); 
+	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
+		(cur_s != bc->modem.ser12.last_sample);
+	bc->modem.ser12.last_sample = cur_s;
+	if(bc->modem.ser12.dcd_shreg & 1) {
+		if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				dcdspos += 2;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		} else
+			bc->modem.ser12.dcd_sum0--;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		/*
+		 * PLL code for the improved software DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 4);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 7) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 4:	/* transition too early */
+				ser12_set_divisor(dev, 3);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 4);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample == 
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit = 
+				bc->modem.ser12.last_sample;
+		}
+		if (++bc->modem.ser12.interm_sample >= 3)
+			bc->modem.ser12.interm_sample = 0;	
+		/*
+		 * DCD stuff
+		 */
+		if (bc->modem.ser12.dcd_shreg & 1) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				<< 1;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		} 
+	} else {
+		/*
+		 * PLL algorithm for the hardware squelch DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 6);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 3) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 7);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 2:	/* transition too early */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 6);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample == 
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit = 
+				bc->modem.ser12.last_sample;
+		}
+		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
+		/*
+		 * DCD stuff
+		 */
+		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
+	}
+	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
+	if (bc->modem.shreg & 1) {
+		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+		bc->modem.shreg = 0x10000;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		ser12_tx(dev, bc);
+	else {
+		ser12_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+			sti();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	sti();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+	{ "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));			/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90) 
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5)) 
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev) 
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	enum uart u;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SER12_EXTENT))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+		return -EIO;
+	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT, 
+			"baycom_ser12", dev))
+		return -EBUSY;
+	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+	/*
+	 * enable transmitter empty interrupt
+	 */
+	outb(2, IER(dev->base_addr));  
+	/*
+	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
+	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
+	 * depending on the usage of the software DCD routine
+	 */
+	ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
+	printk(KERN_INFO "baycom: ser12 at iobase 0x%lx irq %u options "
+	       "0x%x uart %s\n", dev->base_addr, dev->irq, bc->options, 
+	       uart_str[u]);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev) 
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	free_irq(dev->irq, dev);	
+	release_region(dev->base_addr, SER12_EXTENT);
+	printk(KERN_INFO "baycom: close ser12 at iobase 0x%lx irq %u\n",
+	       dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== PAR96 specific routines =========================
+ */
+
+#define PAR96_DESCRAM_TAP1 0x20000
+#define PAR96_DESCRAM_TAP2 0x01000
+#define PAR96_DESCRAM_TAP3 0x00001
+
+#define PAR96_DESCRAM_TAPSH1 17
+#define PAR96_DESCRAM_TAPSH2 12
+#define PAR96_DESCRAM_TAPSH3 0
+
+#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
+#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static inline void par96_tx(struct device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data = hdlcdrv_getbits(&bc->hdrv);
+
+	for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
+		unsigned char val = PAR97_POWER;
+		bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
+					 (bc->modem.par96.scram & 1));
+		if (!(data & 1))
+			bc->modem.par96.scram ^= 1;
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
+			bc->modem.par96.scram ^= 
+				(PAR96_SCRAM_TAPN << 1);
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
+			val |= PAR96_TXBIT;
+		outb(val, LPT_DATA(dev->base_addr));
+		outb(val | PAR96_BURST, LPT_DATA(dev->base_addr));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void par96_rx(struct device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data, mask, mask2, descx;
+
+	/*
+	 * do receiver; differential decode and descramble on the fly
+	 */
+	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
+		if (inb(LPT_STATUS(dev->base_addr)) & PAR96_RXBIT)
+			bc->modem.par96.descram |= 1;
+		descx = bc->modem.par96.descram ^ 
+			(bc->modem.par96.descram >> 1);
+		/* now the diff decoded data is inverted in descram */
+		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev->base_addr));
+		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
+			  (descx >> PAR96_DESCRAM_TAPSH2));
+		data >>= 1;
+		if (!(descx & 1))
+			data |= 0x8000;
+		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, 
+		     LPT_DATA(dev->base_addr));
+	}
+	hdlcdrv_putbits(&bc->hdrv, data);
+	/*
+	 * do DCD algorithm
+	 */
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
+			| (data << 16);
+		/* search for flags and set the dcd counter appropriately */
+		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; 
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
+				bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
+		/* check for abort/noise sequences */
+		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; 
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
+			    (bc->modem.par96.dcd_count >= 0))
+				bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
+		/* decrement and set the dcd variable */
+		if (bc->modem.par96.dcd_count >= 0)
+			bc->modem.par96.dcd_count -= 2;
+		hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
+	} else {
+		hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev->base_addr))
+					     & PAR96_DCD));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		par96_tx(dev, bc);
+	else {
+		par96_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = 6;
+			sti();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	sti();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_check_lpt(unsigned int iobase)
+{
+	unsigned char b1,b2;
+	int i;
+
+	b1 = inb(LPT_DATA(iobase));
+	b2 = inb(LPT_CONTROL(iobase));
+	outb(0xaa, LPT_DATA(iobase));
+	i = inb(LPT_DATA(iobase)) == 0xaa;
+	outb(0x55, LPT_DATA(iobase));
+	i &= inb(LPT_DATA(iobase)) == 0x55;
+	outb(0x0a, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+	outb(0x05, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+	outb(b1, LPT_DATA(iobase));
+	outb(b2, LPT_CONTROL(iobase));
+	return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_open(struct device *dev) 
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-PAR96_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (check_region(dev->base_addr, PAR96_EXTENT))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	if (par96_check_lpt(dev->base_addr))
+		return -EIO;
+	/* disable interrupt */
+	outb(0, LPT_CONTROL(dev->base_addr));          
+	 /* switch off PTT */
+	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr));
+	printk(KERN_INFO "baycom: par96 at iobase 0x%lx irq %u "
+	       "options 0x%x\n", dev->base_addr, dev->irq, bc->options);
+	if (request_irq(dev->irq, par96_interrupt, SA_INTERRUPT, 
+			"baycom_par96", dev))
+		return -EBUSY;
+	request_region(dev->base_addr, PAR96_EXTENT, "baycom_par96");
+	/* enable interrupt */
+	outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev->base_addr)); 
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_close(struct device *dev) 
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/* disable interrupt */
+	outb(0, LPT_CONTROL(dev->base_addr));  
+	/* switch off PTT */
+	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr));
+	free_irq(dev->irq, dev);	
+	release_region(dev->base_addr, PAR96_EXTENT);
+	printk(KERN_INFO "baycom: close par96 at iobase 0x%lx irq %u\n",
+	       dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+	1200,
+	ser12_open,
+	ser12_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops par96_ops = {
+	9600,
+	par96_open,
+	par96_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops dummy_ops = {
+	0,
+	NULL,
+	NULL,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops *ops_tab[3] = {
+	&dummy_ops, &ser12_ops, &par96_ops
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+	unsigned long flags;
+		
+	if (!dev || !dev->priv || 
+	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	bc = (struct baycom_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case BAYCOMCTL_GETMODEMTYPE:
+		bi.data.mt.modem_type = BAYCOM_MODEM_INVALID;
+		if (bc->hdrv.ops == &ser12_ops)
+			bi.data.mt.modem_type = BAYCOM_MODEM_SER12;
+		else if (bc->hdrv.ops == &par96_ops)
+			bi.data.mt.modem_type = BAYCOM_MODEM_PAR96;
+		else if (bc->hdrv.ops != &dummy_ops) {
+			printk(KERN_ERR "baycom: BAYCOMCTL_GETMODEMTYPE: "
+			       "modem ops invalid\n");
+		}
+		bi.data.mt.options = bc->options;
+		break;
+
+	case BAYCOMCTL_SETMODEMTYPE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		if (bi.data.mt.modem_type < BAYCOM_MODEM_SER12 ||
+		    bi.data.mt.modem_type > BAYCOM_MODEM_PAR96)
+			return -EINVAL;
+		save_flags(flags);
+		cli();
+		bc->hdrv.ops = ops_tab[bi.data.mt.modem_type];
+		bc->options = bi.data.mt.options;
+		restore_flags(flags);
+		return 0;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static
+#endif /* MODULE */
+int baycom_init(void) 
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct baycom_state *bc;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	
+	printk(KERN_INFO "baycom: compiled %s %s\n", __TIME__, __DATE__);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		sprintf(ifname, "bc%d", i);
+
+		if (baycom_ports[i].modem < BAYCOM_MODEM_SER12 ||
+		    baycom_ports[i].modem > BAYCOM_MODEM_PAR96)
+			set_hw = 0;
+		if (set_hw) {
+			j = hdlcdrv_register_hdlcdrv(dev, ops_tab[baycom_ports[i].modem], 
+						     sizeof(struct baycom_state), 
+						     ifname, baycom_ports[i].iobase,
+						     baycom_ports[i].irq, 0);
+			if (!j) {
+				bc = (struct baycom_state *)dev->priv;
+				bc->options = baycom_ports[i].options;
+			}
+		} else 
+			j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops, 
+						     sizeof(struct baycom_state), 
+						     ifname, 0, 0, 0);
+		if (j) {
+			printk(KERN_WARNING "baycom: cannot register net "
+			       "device\n");
+		} else
+			found++;
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+int modem = BAYCOM_MODEM_INVALID;
+int iobase = 0x3f8;
+int irq = 4;
+int options = BAYCOM_OPTIONS_SOFTDCD;
+
+int init_module(void)
+{
+	printk(KERN_INFO "baycom: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+
+	baycom_ports[0].modem = modem;
+	baycom_ports[0].iobase = iobase;
+	baycom_ports[0].irq = irq;
+	baycom_ports[0].options = options;
+	baycom_ports[1].modem = BAYCOM_MODEM_INVALID;
+
+	return baycom_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	printk(KERN_INFO "baycom: cleanup_module called\n");
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+		if (bc) {
+			if (bc->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "baycom: invalid magic in "
+				       "cleanup_module\n");
+			else 
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom=modem,io,irq,options[,modem,io,irq,options]
+ * modem=1: ser12, modem=2: par96
+ * options=0: hardware DCD, options=1: software DCD
+ */
+
+void baycom_setup(char *str, int *ints)
+{
+	int i;
+
+	for (i = 0; i < NR_PORTS; i++) 
+		if (ints[0] >= 4*i+4) {
+			baycom_ports[i].modem = ints[4*i+1];
+			baycom_ports[i].iobase = ints[4*i+2];
+			baycom_ports[i].irq = ints[4*i+3];
+			baycom_ports[i].options = ints[4*i+4];
+		} else
+			baycom_ports[i].modem = BAYCOM_MODEM_INVALID;
+
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.6/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v2.1.6/linux/drivers/net/eql.c	Wed Oct 16 10:48:17 1996
+++ linux/drivers/net/eql.c	Wed Oct 30 18:41:44 1996
@@ -128,6 +128,7 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/uaccess.h>
 #include <linux/errno.h>              
 
 #include <linux/netdevice.h>
diff -u --recursive --new-file v2.1.6/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.1.6/linux/drivers/net/hdlcdrv.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/hdlcdrv.c	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,934 @@
+/*****************************************************************************/
+
+/*
+ *	hdlcdrv.c  -- HDLC packet radio network driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *  The driver was derived from Donald Beckers skeleton.c
+ *	Written 1993-94 by Donald Becker.
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/hdlcdrv.h>
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/net_alias.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*---------------------------------------------------------------------------*/
+
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ 	unsigned int crc = 0xffff;
+
+	for (;len>0;len--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+	crc ^= 0xffff;
+	*buffer++ = crc;
+	*buffer++ = crc >> 8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	return (crc & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	crc ^= 0xffff;
+	return (crc & 0xffff);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_2flags(s,tenms) ((tenms * s->ops->bitrate) / 100 / 16)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The HDLC routines
+ */
+
+static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, 
+			     int num)
+{
+	int added = 0;
+	
+	while (s->hdlcrx.rx_state && num >= 8) {
+		if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
+			s->hdlcrx.rx_state = 0;
+			return 0;
+		}
+		*s->hdlcrx.bp++ = bits >> (32-num);
+		s->hdlcrx.len++;
+		num -= 8;
+		added += 8;
+	}
+	return added;
+}
+
+static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s)
+{
+	struct sk_buff *skb;
+	int pkt_len;
+	unsigned char *cp;
+
+	if (s->hdlcrx.len < 4) 
+		return;
+	if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) 
+		return;
+	pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
+	if (!(skb = dev_alloc_skb(pkt_len))) {
+		printk("%s: memory squeeze, dropping packet\n", 
+		       s->ifname);
+		s->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+	cp = skb_put(skb, pkt_len);
+	*cp++ = 0; /* KISS kludge */
+	memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	IS_SKB(skb);
+	netif_rx(skb);
+	s->stats.rx_packets++;
+}
+
+void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s)
+{
+	int i;
+	unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
+	
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (set_bit(0, &s->hdlcrx.in_hdlc_rx))
+		return;
+
+	while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
+		word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);	
+
+#ifdef HDLCDRV_DEBUG
+		hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
+#endif /* HDLCDRV_DEBUG */
+	       	s->hdlcrx.bitstream >>= 16;
+		s->hdlcrx.bitstream |= word << 16;
+		s->hdlcrx.bitbuf >>= 16;
+		s->hdlcrx.bitbuf |= word << 16;
+		s->hdlcrx.numbits += 16;
+		for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+		    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
+		    i >= 0; 
+		    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
+		    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+			if ((s->hdlcrx.bitstream & mask1) == mask1)
+				s->hdlcrx.rx_state = 0; /* abort received */
+			else if ((s->hdlcrx.bitstream & mask2) == mask3) {
+				/* flag received */
+				if (s->hdlcrx.rx_state) {
+					hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf 
+							  << (8+i),
+							  s->hdlcrx.numbits
+							  -8-i);
+					hdlc_rx_flag(dev, s);
+				}
+				s->hdlcrx.len = 0;
+				s->hdlcrx.bp = s->hdlcrx.buffer;
+				s->hdlcrx.rx_state = 1;
+				s->hdlcrx.numbits = i;
+			} else if ((s->hdlcrx.bitstream & mask4) == mask5) {
+				/* stuffed bit */
+				s->hdlcrx.numbits--;
+				s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
+					((s->hdlcrx.bitbuf & mask6) << 1);
+			}
+		}
+		s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
+						       s->hdlcrx.numbits);
+	}
+	clear_bit(0, &s->hdlcrx.in_hdlc_rx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void inline do_kiss_params(struct hdlcdrv_state *s,
+				  unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b)
+#else /* KISS_VERBOSE */	      
+#define PKP(a,b) 
+#endif /* KISS_VERBOSE */	      
+
+	if (len < 2)
+		return;
+	switch(data[0]) {
+	case PARAM_TXDELAY:
+		s->ch_params.tx_delay = data[1];
+		PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
+		break;
+	case PARAM_PERSIST:   
+		s->ch_params.ppersist = data[1];
+		PKP("p persistence = %u", s->ch_params.ppersist);
+		break;
+	case PARAM_SLOTTIME:  
+		s->ch_params.slottime = data[1];
+		PKP("slot time = %ums", s->ch_params.slottime);
+		break;
+	case PARAM_TXTAIL:    
+		s->ch_params.tx_tail = data[1];
+		PKP("TX tail = %ums", s->ch_params.tx_tail);
+		break;
+	case PARAM_FULLDUP:   
+		s->ch_params.fulldup = !!data[1];
+		PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
+		break;
+	default:
+		break;
+	}
+#undef PKP
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s)
+{
+	unsigned int mask1, mask2, mask3;
+	int i;
+	struct sk_buff *skb;
+	int pkt_len;
+
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (set_bit(0, &s->hdlctx.in_hdlc_tx))
+		return;
+	for (;;) {
+		if (s->hdlctx.numbits >= 16) {
+			if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
+			s->hdlctx.bitbuf >>= 16;
+			s->hdlctx.numbits -= 16;
+		}
+		switch (s->hdlctx.tx_state) {
+		default:
+			clear_bit(0, &s->hdlctx.in_hdlc_tx);
+			return;
+		case 0:
+		case 1:
+			if (s->hdlctx.numflags) {
+				s->hdlctx.numflags--;
+				s->hdlctx.bitbuf |= 
+					0x7e7e << s->hdlctx.numbits;
+				s->hdlctx.numbits += 16;
+				break;
+			}
+			if (s->hdlctx.tx_state == 1) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			if (!(skb = skb_dequeue(&s->send_queue))) {
+				int flgs = tenms_to_2flags
+					(s, s->ch_params.tx_tail);
+				if (flgs < 2)
+					flgs = 2;
+				s->hdlctx.tx_state = 1;
+				s->hdlctx.numflags = flgs;
+				break;
+			}
+			if (skb->data[0] != 0) {
+				do_kiss_params(s, skb->data, skb->len);
+				dev_kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+			pkt_len = skb->len-1; /* strip KISS byte */
+			if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				dev_kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+			dev_kfree_skb(skb, FREE_WRITE);
+			s->hdlctx.bp = s->hdlctx.buffer;
+			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
+			s->hdlctx.len = pkt_len+2; /* the appended CRC */
+			s->hdlctx.tx_state = 2;
+			s->hdlctx.bitstream = 0;
+			s->stats.tx_packets++;
+			break;
+		case 2:
+			if (!s->hdlctx.len) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				break;
+			}
+			s->hdlctx.len--;
+			s->hdlctx.bitbuf |= *s->hdlctx.bp <<
+				s->hdlctx.numbits;
+			s->hdlctx.bitstream >>= 8;
+			s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
+			mask1 = 0x1f000;
+			mask2 = 0x10000;
+			mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
+			s->hdlctx.numbits += 8;
+			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
+			    mask3 = (mask3 << 1) | 1) {
+				if ((s->hdlctx.bitstream & mask1) != mask1) 
+					continue;
+				s->hdlctx.bitstream &= ~mask2;
+				s->hdlctx.bitbuf = 
+					(s->hdlctx.bitbuf & mask3) |
+						((s->hdlctx.bitbuf & 
+						 (~mask3)) << 1);
+				s->hdlctx.numbits++;
+				mask3 = (mask3 << 1) | 1;
+			}
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void start_tx(struct device *dev, struct hdlcdrv_state *s)
+{
+	s->hdlctx.tx_state = 0;
+	s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
+	s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
+	hdlcdrv_transmitter(dev, s);
+	s->hdlctx.ptt = 1;
+	s->ptt_keyed++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s)
+{
+	if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || 
+	    skb_queue_empty(&s->send_queue)) 
+		return;
+	if (s->ch_params.fulldup) {
+		start_tx(dev, s);
+		return;
+	}
+	if (s->hdlcrx.dcd) {
+		s->hdlctx.slotcnt = s->ch_params.slottime;
+		return;
+	}
+	if ((--s->hdlctx.slotcnt) > 0)
+		return;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	if ((random_num() % 256) > s->ch_params.ppersist)
+		return;
+	start_tx(dev, s);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static inline int hdlcdrv_paranoia_check(struct device *dev,
+					const char *routine)
+{
+	if (!dev || !dev->priv || 
+	    ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state "
+		       "struct in routine %s\n", routine);
+		return 1;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct hdlcdrv_state *sm;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
+		return 0;
+	sm = (struct hdlcdrv_state *)dev->priv;
+	/*
+	 * If some higher layer thinks we've missed an tx-done interrupt
+         * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	 * itself. 
+	 */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+	skb_queue_tail(&sm->send_queue, skb);
+	dev->trans_start = jiffies;	
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
+	return 0;                                         
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
+{
+	struct hdlcdrv_state *sm;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats"))
+		return NULL;
+	sm = (struct hdlcdrv_state *)dev->priv;
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &sm->stats;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int hdlcdrv_open(struct device *dev)
+{
+	struct hdlcdrv_state *s;
+	int i;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (dev->start)
+		return 0;
+	if (!s->ops || !s->ops->open)
+		return -ENODEV;
+
+	dev->start = 1;
+	/*
+	 * initialise some variables
+	 */
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+	i = s->ops->open(dev);
+	if (i) {
+		dev->start = 0;
+		return i;
+	}
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* 
+ * The inverse routine to hdlcdrv_open(). 
+ */
+
+static int hdlcdrv_close(struct device *dev)
+{
+	struct hdlcdrv_state *s;
+	struct sk_buff *skb;
+	int i = 0;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (!dev->start)
+		return 0;
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (s->ops && s->ops->close)
+		i = s->ops->close(dev);
+        /* Free any buffers left in the hardware transmit queue */
+        while ((skb = skb_dequeue(&s->send_queue)))
+			dev_kfree_skb(skb, FREE_WRITE);
+	return i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct hdlcdrv_state *s;
+	struct hdlcdrv_ioctl bi;
+		
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		if (s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETCHANNELPAR:
+		bi.data.cp.tx_delay = s->ch_params.tx_delay;
+		bi.data.cp.tx_tail = s->ch_params.tx_tail;
+		bi.data.cp.slottime = s->ch_params.slottime;
+		bi.data.cp.ppersist = s->ch_params.ppersist;
+		bi.data.cp.fulldup = s->ch_params.fulldup;
+		break;
+
+	case HDLCDRVCTL_SETCHANNELPAR:
+		if (!suser())
+			return -EACCES;
+		s->ch_params.tx_delay = bi.data.cp.tx_delay;
+		s->ch_params.tx_tail = bi.data.cp.tx_tail;
+		s->ch_params.slottime = bi.data.cp.slottime;
+		s->ch_params.ppersist = bi.data.cp.ppersist;
+		s->ch_params.fulldup = bi.data.cp.fulldup;
+		s->hdlctx.slotcnt = 1;
+		return 0;
+		
+	case HDLCDRVCTL_GETMODEMPAR:
+		bi.data.mp.iobase = dev->base_addr;
+		bi.data.mp.irq = dev->irq;
+		bi.data.mp.dma = dev->dma;
+		bi.data.mp.seriobase = s->ptt_out.seriobase;
+		bi.data.mp.pariobase = s->ptt_out.pariobase;
+		bi.data.mp.midiiobase = s->ptt_out.midiiobase;
+		break;
+
+	case HDLCDRVCTL_SETMODEMPAR:
+		if ((!suser()) || dev->start)
+			return -EACCES;
+		dev->base_addr = bi.data.mp.iobase;
+		dev->irq = bi.data.mp.irq;
+		dev->dma = bi.data.mp.dma;
+		s->ptt_out.seriobase = bi.data.mp.seriobase;
+		s->ptt_out.pariobase = bi.data.mp.pariobase;
+		s->ptt_out.midiiobase = bi.data.mp.midiiobase;
+		return 0;	
+	
+	case HDLCDRVCTL_GETSTAT:
+		bi.data.cs.ptt = hdlcdrv_ptt(s);
+		bi.data.cs.dcd = s->hdlcrx.dcd;
+		bi.data.cs.ptt_keyed = s->ptt_keyed;
+		bi.data.cs.stats = s->stats;
+		break;		
+
+	case HDLCDRVCTL_CALIBRATE:
+		s->hdlctx.calibrate = bi.data.calibrate * s->ops->bitrate / 16;
+		return 0;
+
+	case HDLCDRVCTL_GETSAMPLES:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
+		s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
+			sizeof(s->bitbuf_channel.buffer);
+		break;
+#endif /* HDLCDRV_DEBUG */
+				
+	case HDLCDRVCTL_GETBITS:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
+		s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
+			sizeof(s->bitbuf_hdlc.buffer);
+		break;		
+#endif /* HDLCDRV_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Fill in the MAC-level header 
+ */
+
+#ifdef CONFIG_AX25
+static int hdlcdrv_header(struct sk_buff *skb, struct device *dev, 
+			 unsigned short type, void *daddr, void *saddr, 
+			 unsigned len)
+{
+        return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Rebuild the MAC-level header
+ */
+
+static int hdlcdrv_rebuild_header(void *buff, struct device *dev,
+				 unsigned long raddr,
+				 struct sk_buff *skb)
+{
+        return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+#endif /* CONFIG_AX25 */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static int hdlcdrv_probe(struct device *dev)
+{
+	struct hdlcdrv_channel_params dflt_ch_params = { 20, 2, 10, 40, 0 };
+	int i;
+	struct hdlcdrv_state *s;
+
+	if (!dev)
+		return -ENXIO;
+	/*
+	 * not a real probe! only initialize data structures
+	 */
+	s = (struct hdlcdrv_state *)dev->priv;
+	/*
+	 * initialize the hdlcdrv_state struct
+	 */
+	s->ch_params = dflt_ch_params;
+	s->ptt_keyed = 0;
+
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+#ifdef HDLCDRV_DEBUG
+	s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
+	s->bitbuf_channel.shreg = 0x80;
+
+	s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
+	s->bitbuf_hdlc.shreg = 0x80;
+#endif /* HDLCDRV_DEBUG */
+
+	/*
+	 * initialize the device struct
+	 */
+	dev->open = hdlcdrv_open;
+	dev->stop = hdlcdrv_close;
+	dev->do_ioctl = hdlcdrv_ioctl;
+	dev->hard_start_xmit = hdlcdrv_send_packet;
+	dev->get_stats = hdlcdrv_get_stats;
+
+	/* Fill in the fields of the device structure */
+	for (i=0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	skb_queue_head_init(&s->send_queue);
+	
+#ifdef CONFIG_AX25
+	dev->hard_header = hdlcdrv_header;
+	dev->rebuild_header = hdlcdrv_rebuild_header;
+#else /* CONFIG_AX25 */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 */
+	dev->set_mac_address = hdlcdrv_set_mac_address;
+	
+	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
+	dev->hard_header_len = 73;         /* We do digipeaters now */
+	dev->mtu = 1500;                   /* eth_mtu is the default */
+	dev->addr_len = 7;                 /* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, 7);
+	memcpy(dev->dev_addr, ax25_test, 7);
+
+	/* New style flags */
+	dev->flags = 0;
+	dev->family = AF_INET;
+	dev->pa_addr = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask = 0;
+	dev->pa_alen = sizeof(unsigned long);
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+			     unsigned int privsize, char *ifname,
+			     unsigned int baseaddr, unsigned int irq, 
+			     unsigned int dma) 
+{
+	struct hdlcdrv_state *s;
+
+	if (!dev || !ops)
+		return -EACCES;
+	if (privsize < sizeof(struct hdlcdrv_state))
+		privsize = sizeof(struct hdlcdrv_state);
+	memset(dev, 0, sizeof(struct device));
+	if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL)))
+		return -ENOMEM;
+	/*
+	 * initialize part of the hdlcdrv_state struct
+	 */
+	memset(s, 0, privsize);
+	s->magic = HDLCDRV_MAGIC;
+	strncpy(s->ifname, ifname, sizeof(s->ifname));
+	s->ops = ops;
+	/*
+	 * initialize part of the device struct
+	 */
+	dev->name = s->ifname;
+	dev->if_port = 0;
+	dev->init = hdlcdrv_probe;
+	dev->start = 0;
+	dev->tbusy = 1;
+	dev->base_addr = baseaddr;
+	dev->irq = irq;
+	dev->dma = dma;
+	if (register_netdev(dev)) {
+		printk(KERN_WARNING "hdlcdrv: cannot register net "
+		       "device %s\n", s->ifname);
+		kfree(dev->priv);
+		return -ENXIO;
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_unregister_hdlcdrv(struct device *dev) 
+{
+	struct hdlcdrv_state *s;
+
+	if (!dev)
+		return -EINVAL;
+	if (!(s = (struct hdlcdrv_state *)dev->priv))
+		return -EINVAL;
+	if (s->magic != HDLCDRV_MAGIC)
+		return -EINVAL;
+	if (dev->start && s->ops->close)
+		s->ops->close(dev);
+	unregister_netdev(dev);
+	kfree(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct symbol_table hdlcdrv_syms = {
+#include <linux/symtab_begin.h>
+        X(hdlcdrv_receiver),
+        X(hdlcdrv_transmitter),
+        X(hdlcdrv_arbitrate),
+        X(hdlcdrv_register_hdlcdrv),
+        X(hdlcdrv_unregister_hdlcdrv),
+#include <linux/symtab_end.h>
+};
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	printk(KERN_INFO "hdlcdrv: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+	printk(KERN_INFO "hdlcdrv: compiled %s %s\n", __TIME__, __DATE__);
+
+        register_symtab(&hdlcdrv_syms);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	printk(KERN_INFO "hdlcdrv: cleanup\n");
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.6/linux/drivers/net/scc.c linux/drivers/net/scc.c
--- v2.1.6/linux/drivers/net/scc.c	Tue Oct 29 19:58:12 1996
+++ linux/drivers/net/scc.c	Thu Oct 31 09:52:32 1996
@@ -1,4 +1,4 @@
-#define RCS_ID "$Id: scc.c,v 1.63 1996/10/09 16:45:47 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.64 1996/10/30 18:58:26 jreuter Exp jreuter $"
 
 #define VERSION "3.0"
 #define BANNER  "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
@@ -78,13 +78,12 @@
    		  * Source moved to drivers/net/
    		  * Includes Z8530 defines from drivers/net/z8530.h
    		  * Uses sk_buffer memory management
-   		  * Doesn't have own queues anymore
    		  * Reduced overhead of /proc/net/z8530drv output
    		  * Streamlined quite a lot things
    		  * Invents brand new bugs... ;-)
 
    		  The move to version number 3.0 reflects theses changes.
-   		  You can use version 2.4 if you need a KISS TNC emulator.
+   		  You can use version 2.4a if you need a KISS TNC emulator.
 
    Thanks to all who contributed to this driver with ideas and bug
    reports!
@@ -120,6 +119,7 @@
 
 #define MAXSCC          4       /* number of max. supported chips */
 #define BUFSIZE         384     /* must not exceed 4096 */
+#define MAXQUEUE	8	/* number of buffers we queue ourself */
 #undef  DISABLE_ALL_INTS	/* use cli()/sti() in ISR instead of */
 				/* enable_irq()/disable_irq()        */
 #undef	SCC_DEBUG
@@ -129,8 +129,6 @@
 /* ----------------------------------------------------------------------- */
 
 #include <linux/module.h>
-#include <linux/config.h>
-
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -150,6 +148,8 @@
 #include <linux/if_arp.h>
 #include <linux/socket.h>
 
+#include <linux/config.h> /* for CONFIG_PROC_FS */
+
 #include <linux/scc.h>
 #include "z8530.h"
 
@@ -309,7 +309,6 @@
 static __inline__ void
 scc_unlock_dev(struct scc_channel *scc)
 {
-	scc->tx_next_buff = NULL;
 	scc->dev->tbusy = 0;
 }
 
@@ -327,12 +326,9 @@
 		scc->tx_buff = NULL;
 	}
 	
-	if (scc->tx_next_buff != NULL)
-	{
-		dev_kfree_skb(scc->tx_next_buff, FREE_WRITE);
-		scc->tx_next_buff = NULL;
-	}
-	
+	while (skb_queue_len(&scc->tx_queue))
+		dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE);
+
 	restore_flags(flags);
 }
 
@@ -399,8 +395,9 @@
 	
 	if (skb == NULL)
 	{
-		skb = scc->tx_next_buff;
+		skb = skb_dequeue(&scc->tx_queue);
 		scc->tx_buff = skb;
+		scc_unlock_dev(scc);
 
 		if (skb == NULL)
 		{
@@ -417,8 +414,6 @@
 			Outb(scc->ctrl, RES_Tx_P);
 			return;
 		}
-		
-		scc_unlock_dev(scc);
 
 		scc->stat.tx_state = TXS_ACTIVE;
 
@@ -1150,7 +1145,7 @@
 	
 	if (scc->stat.tx_state == TXS_WAIT)	/* maxkeyup or idle timeout */
 	{
-		if (scc->tx_next_buff == NULL)	/* nothing to send */
+		if (skb_queue_len(&scc->tx_queue) == 0)	/* nothing to send */
 		{
 			scc->stat.tx_state = TXS_IDLE;
 			scc_unlock_dev(scc);	/* t_maxkeyup locked it. */
@@ -1620,7 +1615,8 @@
 
 	MOD_INC_USE_COUNT;
 	
-	scc->tx_buff = scc->tx_next_buff = NULL;
+	scc->tx_buff = NULL;
+	skb_queue_head_init(&scc->tx_queue);
  
 	init_channel(scc);
 
@@ -1746,19 +1742,12 @@
 	save_flags(flags);
 	cli();
 	
-	scc_lock_dev(scc);
+	__skb_queue_tail(&scc->tx_queue, skb);
 	
-	if (scc->tx_next_buff != NULL)
-	{
-		printk(KERN_ERR "z8530drv: race condition, discarding frame\n");
-		dev_kfree_skb(skb, FREE_WRITE);
+	if (skb_queue_len(&scc->tx_queue) == MAXQUEUE)
 		scc_lock_dev(scc);
-		restore_flags(flags);
-		return 0;
-	}
-	
+
 	dev->trans_start = jiffies;
-	scc->tx_next_buff = skb;
 
 	/*
 	 * Start transmission if the trx state is idle or
@@ -1798,10 +1787,10 @@
 	struct scc_kiss_cmd kiss_cmd;
 	struct scc_mem_config memcfg;
 	struct scc_hw_config hwcfg;
-	unsigned char device_name[10];
-	struct scc_channel *scc;
 	int chan;
+	unsigned char device_name[10];
 	void *arg;
+	struct scc_channel *scc;
 	
 	scc = (struct scc_channel *) dev->priv;
 	if (scc == NULL || scc->magic != SCC_MAGIC)
@@ -1964,7 +1953,8 @@
 				scc->kiss.softdcd = 0;		/* hardware dcd */
 			}
 			
-			scc->tx_buff = scc->tx_next_buff = NULL;
+			scc->tx_buff = NULL;
+			skb_queue_head_init(&scc->tx_queue);
 			scc->init = 1;
 			
 			return 0;
diff -u --recursive --new-file v2.1.6/linux/drivers/net/sm_tables.h linux/drivers/net/sm_tables.h
--- v2.1.6/linux/drivers/net/sm_tables.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/sm_tables.h	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,320 @@
+#ifdef ENABLE_AFSK1200
+static const char tx_lo_i[] = {  127,   89,    0,  -89, -127,  -89,    0,   89 };
+
+static const char tx_lo_q[] = {    0,   89,  127,   89,    0,  -89, -127,  -89 };
+
+static const char tx_hi_i[] = {  127,   16, -122,  -48,  109,   77,  -89, -100 };
+
+static const char tx_hi_q[] = {    0,  125,   32, -117,  -63,  100,   89,  -77 };
+
+static const char sinetab[] = {
+	 128,  140,  152,  164,  176,  187,  198,  208,
+	 217,  226,  233,  240,  245,  249,  252,  254,
+	 255,  254,  252,  249,  245,  240,  233,  226,
+	 217,  208,  198,  187,  176,  164,  152,  140,
+	 128,  116,  104,   92,   80,   69,   58,   48,
+	  39,   30,   23,   16,   11,    7,    4,    2,
+	   1,    2,    4,    7,   11,   16,   23,   30,
+	  39,   48,   58,   69,   80,   92,  104,  116
+};
+#endif ENABLE_AFSK1200
+#ifdef ENABLE_FSK9600
+#ifdef ENABLE_SBC
+static unsigned char tx_filter_9k6_4[] = {
+	  65,  66,  63,  63,  76,  76,  73,  73,
+	  54,  54,  51,  51,  64,  65,  61,  62,
+	 214, 214, 211, 211, 224, 225, 221, 222,
+	 202, 202, 199, 200, 213, 213, 210, 210,
+	  38,  38,  35,  35,  48,  49,  45,  46,
+	  26,  27,  23,  24,  37,  37,  34,  34,
+	 186, 187, 183, 184, 197, 197, 194, 194,
+	 175, 175, 172, 172, 185, 185, 182, 182,
+	  73,  74,  70,  71,  84,  84,  81,  81,
+	  62,  62,  59,  59,  72,  72,  69,  69,
+	 221, 222, 218, 219, 232, 232, 229, 229,
+	 210, 210, 207, 207, 220, 221, 217, 218,
+	  46,  46,  43,  43,  56,  56,  53,  53,
+	  34,  34,  31,  31,  44,  45,  41,  42,
+	 194, 194, 191, 191, 204, 205, 201, 202,
+	 182, 183, 179, 180, 193, 193, 190, 190,
+	  65,  65,  62,  62,  75,  76,  72,  73,
+	  53,  54,  50,  51,  64,  64,  61,  61,
+	 213, 214, 210, 211, 224, 224, 221, 221,
+	 202, 202, 199, 199, 212, 212, 209, 209,
+	  37,  38,  34,  35,  48,  48,  45,  45,
+	  26,  26,  23,  23,  36,  37,  33,  34,
+	 186, 186, 183, 183, 196, 196, 193, 193,
+	 174, 174, 171, 171, 184, 185, 181, 182,
+	  73,  73,  70,  70,  83,  83,  80,  80,
+	  61,  61,  58,  58,  71,  72,  68,  69,
+	 221, 221, 218, 218, 231, 232, 228, 229,
+	 209, 210, 206, 207, 220, 220, 217, 217,
+	  45,  45,  42,  42,  55,  56,  53,  53,
+	  33,  34,  30,  31,  44,  44,  41,  41,
+	 193, 194, 190, 191, 204, 204, 201, 201,
+	 182, 182, 179, 179, 192, 192, 189, 190,
+	  65,  66,  62,  63,  67,  68,  64,  64,
+	 103, 104, 100, 101, 105, 106, 101, 102,
+	 168, 169, 164, 165, 169, 170, 166, 167,
+	 205, 206, 202, 203, 207, 208, 204, 205,
+	  49,  50,  46,  46,  51,  51,  47,  48,
+	  87,  88,  83,  84,  88,  89,  85,  86,
+	 151, 152, 148, 149, 153, 154, 149, 150,
+	 189, 190, 186, 187, 191, 192, 187, 188,
+	  66,  67,  63,  64,  68,  69,  64,  65,
+	 104, 105, 100, 101, 105, 106, 102, 103,
+	 168, 169, 165, 166, 170, 171, 167, 167,
+	 206, 207, 203, 204, 208, 209, 204, 205,
+	  50,  51,  46,  47,  51,  52,  48,  49,
+	  87,  88,  84,  85,  89,  90,  85,  86,
+	 152, 153, 149, 149, 154, 154, 150, 151,
+	 190, 191, 186, 187, 191, 192, 188, 189,
+	  66,  67,  63,  64,  68,  69,  64,  65,
+	 104, 105, 101, 101, 106, 106, 102, 103,
+	 169, 170, 165, 166, 170, 171, 167, 168,
+	 206, 207, 203, 204, 208, 209, 204, 205,
+	  50,  51,  46,  47,  51,  52,  48,  49,
+	  88,  88,  84,  85,  89,  90,  86,  87,
+	 152, 153, 149, 150, 154, 155, 150, 151,
+	 190, 191, 186, 187, 191, 192, 188, 189,
+	  67,  68,  63,  64,  68,  69,  65,  66,
+	 105, 106, 101, 102, 106, 107, 103, 104,
+	 169, 170, 166, 167, 171, 172, 167, 168,
+	 207, 208, 204, 204, 209, 209, 205, 206,
+	  50,  51,  47,  48,  52,  53,  49,  50,
+	  88,  89,  85,  86,  90,  91,  86,  87,
+	 153, 154, 149, 150, 154, 155, 151, 152,
+	 191, 191, 187, 188, 192, 193, 189, 190,
+	  65,  66,  66,  67,  49,  50,  50,  50,
+	 168, 169, 168, 169, 151, 152, 152, 153,
+	 103, 104, 104, 105,  87,  88,  87,  88,
+	 205, 206, 206, 207, 189, 190, 190, 191,
+	  67,  68,  68,  68,  51,  51,  51,  52,
+	 169, 170, 170, 171, 153, 154, 154, 154,
+	 105, 106, 105, 106,  88,  89,  89,  90,
+	 207, 208, 208, 209, 191, 191, 191, 192,
+	  62,  63,  63,  63,  46,  46,  46,  47,
+	 164, 165, 165, 166, 148, 149, 149, 149,
+	 100, 101, 100, 101,  83,  84,  84,  85,
+	 202, 203, 203, 204, 186, 186, 186, 187,
+	  64,  64,  64,  65,  47,  48,  48,  49,
+	 166, 167, 167, 167, 149, 150, 150, 151,
+	 101, 102, 102, 103,  85,  86,  85,  86,
+	 204, 204, 204, 205, 187, 188, 188, 189,
+	  66,  67,  67,  68,  50,  51,  51,  51,
+	 169, 170, 169, 170, 152, 153, 153, 154,
+	 104, 105, 105, 106,  88,  88,  88,  89,
+	 206, 207, 207, 208, 190, 191, 191, 191,
+	  68,  69,  69,  69,  51,  52,  52,  53,
+	 170, 171, 171, 172, 154, 155, 154, 155,
+	 106, 106, 106, 107,  89,  90,  90,  91,
+	 208, 209, 209, 209, 192, 192, 192, 193,
+	  63,  64,  64,  64,  46,  47,  47,  48,
+	 165, 166, 166, 167, 149, 150, 149, 150,
+	 101, 101, 101, 102,  84,  85,  85,  86,
+	 203, 204, 204, 204, 187, 187, 187, 188,
+	  64,  65,  65,  66,  48,  49,  49,  50,
+	 167, 168, 167, 168, 150, 151, 151, 152,
+	 102, 103, 103, 104,  86,  87,  86,  87,
+	 205, 205, 205, 206, 188, 189, 189, 190,
+	  65,  65,  73,  73,  38,  37,  46,  45,
+	 214, 213, 221, 221, 186, 186, 194, 193,
+	  54,  53,  62,  61,  26,  26,  34,  33,
+	 202, 202, 210, 209, 175, 174, 182, 182,
+	  76,  75,  84,  83,  48,  48,  56,  55,
+	 224, 224, 232, 231, 197, 196, 204, 204,
+	  64,  64,  72,  71,  37,  36,  44,  44,
+	 213, 212, 220, 220, 185, 184, 193, 192,
+	  63,  62,  70,  70,  35,  34,  43,  42,
+	 211, 210, 218, 218, 183, 183, 191, 190,
+	  51,  50,  59,  58,  23,  23,  31,  30,
+	 199, 199, 207, 206, 172, 171, 179, 179,
+	  73,  72,  81,  80,  45,  45,  53,  53,
+	 221, 221, 229, 228, 194, 193, 201, 201,
+	  61,  61,  69,  68,  34,  33,  41,  41,
+	 210, 209, 217, 217, 182, 181, 190, 189,
+	  66,  65,  74,  73,  38,  38,  46,  45,
+	 214, 214, 222, 221, 187, 186, 194, 194,
+	  54,  54,  62,  61,  27,  26,  34,  34,
+	 202, 202, 210, 210, 175, 174, 183, 182,
+	  76,  76,  84,  83,  49,  48,  56,  56,
+	 225, 224, 232, 232, 197, 196, 205, 204,
+	  65,  64,  72,  72,  37,  37,  45,  44,
+	 213, 212, 221, 220, 185, 185, 193, 192,
+	  63,  62,  71,  70,  35,  35,  43,  42,
+	 211, 211, 219, 218, 184, 183, 191, 191,
+	  51,  51,  59,  58,  24,  23,  31,  31,
+	 200, 199, 207, 207, 172, 171, 180, 179,
+	  73,  73,  81,  80,  46,  45,  53,  53,
+	 222, 221, 229, 229, 194, 193, 202, 201,
+	  62,  61,  69,  69,  34,  34,  42,  41,
+	 210, 209, 218, 217, 182, 182, 190, 190
+};
+
+#endif /* ENABLE_SBC */
+#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
+static unsigned char tx_filter_9k6_5[] = {
+	  78,  78,  75,  76,  85,  85,  82,  83,
+	  71,  72,  69,  69,  78,  78,  76,  76,
+	 193, 193, 190, 191, 199, 200, 197, 198,
+	 186, 187, 184, 184, 193, 193, 190, 191,
+	  58,  59,  56,  57,  65,  66,  63,  63,
+	  52,  52,  49,  50,  58,  59,  56,  57,
+	 173, 174, 171, 171, 180, 180, 177, 178,
+	 166, 167, 164, 165, 173, 174, 171, 171,
+	  85,  86,  83,  83,  92,  92,  89,  90,
+	  79,  79,  76,  77,  85,  86,  83,  83,
+	 200, 200, 198, 198, 207, 207, 204, 205,
+	 193, 194, 191, 191, 200, 201, 198, 198,
+	  66,  66,  63,  64,  72,  73,  70,  70,
+	  59,  60,  57,  57,  66,  66,  63,  64,
+	 180, 181, 178, 179, 187, 188, 185, 185,
+	 174, 174, 171, 172, 180, 181, 178, 179,
+	  76,  77,  74,  75,  83,  84,  81,  81,
+	  70,  70,  67,  68,  76,  77,  74,  75,
+	 191, 192, 189, 189, 198, 198, 195, 196,
+	 185, 185, 182, 183, 191, 192, 189, 189,
+	  57,  57,  54,  55,  64,  64,  61,  62,
+	  50,  51,  48,  48,  57,  57,  55,  55,
+	 172, 172, 169, 170, 178, 179, 176, 177,
+	 165, 166, 163, 163, 172, 172, 169, 170,
+	  84,  84,  81,  82,  90,  91,  88,  89,
+	  77,  78,  75,  75,  84,  84,  81,  82,
+	 198, 199, 196, 197, 205, 206, 203, 203,
+	 192, 192, 189, 190, 198, 199, 196, 197,
+	  64,  65,  62,  62,  71,  71,  68,  69,
+	  57,  58,  55,  56,  64,  65,  62,  62,
+	 179, 179, 177, 177, 186, 186, 183, 184,
+	 172, 173, 170, 170, 179, 180, 177, 177,
+	  78,  79,  76,  77,  79,  79,  77,  77,
+	  99, 100,  97,  98, 100, 100,  98,  98,
+	 171, 172, 169, 170, 172, 173, 170, 171,
+	 192, 193, 190, 191, 193, 194, 191, 192,
+	  59,  60,  57,  58,  60,  61,  58,  59,
+	  81,  81,  79,  79,  81,  82,  79,  80,
+	 153, 153, 151, 151, 153, 154, 151, 152,
+	 174, 175, 172, 173, 175, 175, 173, 173,
+	  82,  83,  80,  81,  83,  83,  81,  81,
+	 103, 104, 101, 102, 104, 105, 102, 103,
+	 175, 176, 173, 174, 176, 177, 174, 175,
+	 197, 197, 195, 195, 197, 198, 195, 196,
+	  64,  64,  62,  62,  64,  65,  62,  63,
+	  85,  85,  83,  83,  85,  86,  83,  84,
+	 157, 158, 155, 156, 158, 158, 156, 156,
+	 178, 179, 176, 177, 179, 179, 177, 177,
+	  78,  78,  76,  76,  78,  79,  76,  77,
+	  99,  99,  97,  97,  99, 100,  97,  98,
+	 171, 172, 169, 170, 172, 172, 170, 170,
+	 192, 193, 190, 191, 193, 193, 191, 191,
+	  59,  60,  57,  58,  60,  60,  58,  58,
+	  80,  81,  78,  79,  81,  82,  79,  80,
+	 152, 153, 150, 151, 153, 154, 151, 152,
+	 174, 174, 172, 172, 174, 175, 172, 173,
+	  82,  82,  80,  80,  82,  83,  80,  81,
+	 103, 104, 101, 102, 104, 104, 102, 102,
+	 175, 176, 173, 174, 176, 176, 174, 174,
+	 196, 197, 194, 195, 197, 198, 195, 196,
+	  63,  64,  61,  62,  64,  65,  62,  63,
+	  84,  85,  82,  83,  85,  86,  83,  84,
+	 157, 157, 155, 155, 157, 158, 155, 156,
+	 178, 178, 176, 176, 178, 179, 176, 177,
+	  78,  78,  78,  79,  69,  69,  69,  69,
+	 136, 136, 136, 137, 127, 127, 127, 128,
+	 136, 136, 136, 137, 127, 127, 127, 128,
+	 194, 195, 195, 195, 185, 185, 185, 186,
+	  69,  69,  69,  69,  59,  60,  60,  60,
+	 127, 127, 127, 128, 117, 118, 118, 118,
+	 127, 127, 127, 128, 117, 118, 118, 118,
+	 185, 185, 185, 186, 175, 176, 176, 176,
+	  78,  79,  79,  79,  69,  69,  69,  70,
+	 136, 137, 137, 137, 127, 128, 127, 128,
+	 136, 137, 137, 137, 127, 128, 127, 128,
+	 195, 195, 195, 195, 185, 186, 186, 186,
+	  69,  69,  69,  70,  60,  60,  60,  60,
+	 127, 128, 127, 128, 118, 118, 118, 119,
+	 127, 128, 127, 128, 118, 118, 118, 119,
+	 185, 186, 186, 186, 176, 176, 176, 177,
+	  78,  79,  79,  79,  69,  69,  69,  70,
+	 136, 137, 137, 137, 127, 128, 128, 128,
+	 136, 137, 137, 137, 127, 128, 128, 128,
+	 195, 195, 195, 195, 185, 186, 186, 186,
+	  69,  69,  69,  70,  60,  60,  60,  60,
+	 127, 128, 128, 128, 118, 118, 118, 119,
+	 127, 128, 128, 128, 118, 118, 118, 119,
+	 185, 186, 186, 186, 176, 176, 176, 177,
+	  79,  79,  79,  80,  69,  70,  70,  70,
+	 137, 137, 137, 138, 128, 128, 128, 128,
+	 137, 137, 137, 138, 128, 128, 128, 128,
+	 195, 195, 195, 196, 186, 186, 186, 186,
+	  69,  70,  70,  70,  60,  60,  60,  61,
+	 128, 128, 128, 128, 118, 119, 119, 119,
+	 128, 128, 128, 128, 118, 119, 119, 119,
+	 186, 186, 186, 186, 176, 177, 177, 177,
+	  78,  78,  82,  82,  59,  59,  64,  63,
+	 171, 171, 175, 175, 153, 152, 157, 157,
+	  99,  99, 103, 103,  81,  80,  85,  84,
+	 192, 192, 197, 196, 174, 174, 178, 178,
+	  79,  78,  83,  82,  60,  60,  64,  64,
+	 172, 172, 176, 176, 153, 153, 158, 157,
+	 100,  99, 104, 104,  81,  81,  85,  85,
+	 193, 193, 197, 197, 175, 174, 179, 178,
+	  76,  76,  80,  80,  57,  57,  62,  61,
+	 169, 169, 173, 173, 151, 150, 155, 155,
+	  97,  97, 101, 101,  79,  78,  83,  82,
+	 190, 190, 195, 194, 172, 172, 176, 176,
+	  77,  76,  81,  80,  58,  58,  62,  62,
+	 170, 170, 174, 174, 151, 151, 156, 155,
+	  98,  97, 102, 102,  79,  79,  83,  83,
+	 191, 191, 195, 195, 173, 172, 177, 176,
+	  79,  78,  83,  82,  60,  60,  64,  64,
+	 172, 172, 176, 176, 153, 153, 158, 157,
+	 100,  99, 104, 104,  81,  81,  85,  85,
+	 193, 193, 197, 197, 175, 174, 179, 178,
+	  79,  79,  83,  83,  61,  60,  65,  65,
+	 173, 172, 177, 176, 154, 154, 158, 158,
+	 100, 100, 105, 104,  82,  82,  86,  86,
+	 194, 193, 198, 198, 175, 175, 179, 179,
+	  77,  76,  81,  80,  58,  58,  62,  62,
+	 170, 170, 174, 174, 151, 151, 156, 155,
+	  98,  97, 102, 102,  79,  79,  83,  83,
+	 191, 191, 195, 195, 173, 172, 177, 176,
+	  77,  77,  81,  81,  59,  58,  63,  63,
+	 171, 170, 175, 174, 152, 152, 156, 156,
+	  98,  98, 103, 102,  80,  80,  84,  84,
+	 192, 191, 196, 196, 173, 173, 177, 177,
+	  78,  76,  85,  84,  58,  57,  66,  64,
+	 193, 191, 200, 198, 173, 172, 180, 179,
+	  71,  70,  79,  77,  52,  50,  59,  57,
+	 186, 185, 193, 192, 166, 165, 174, 172,
+	  85,  83,  92,  90,  65,  64,  72,  71,
+	 199, 198, 207, 205, 180, 178, 187, 186,
+	  78,  76,  85,  84,  58,  57,  66,  64,
+	 193, 191, 200, 198, 173, 172, 180, 179,
+	  75,  74,  83,  81,  56,  54,  63,  62,
+	 190, 189, 198, 196, 171, 169, 178, 177,
+	  69,  67,  76,  75,  49,  48,  57,  55,
+	 184, 182, 191, 189, 164, 163, 171, 170,
+	  82,  81,  89,  88,  63,  61,  70,  68,
+	 197, 195, 204, 203, 177, 176, 185, 183,
+	  76,  74,  83,  81,  56,  55,  63,  62,
+	 190, 189, 198, 196, 171, 169, 178, 177,
+	  78,  77,  86,  84,  59,  57,  66,  65,
+	 193, 192, 200, 199, 174, 172, 181, 179,
+	  72,  70,  79,  78,  52,  51,  60,  58,
+	 187, 185, 194, 192, 167, 166, 174, 173,
+	  85,  84,  92,  91,  66,  64,  73,  71,
+	 200, 198, 207, 206, 180, 179, 188, 186,
+	  78,  77,  86,  84,  59,  57,  66,  65,
+	 193, 192, 201, 199, 174, 172, 181, 180,
+	  76,  75,  83,  82,  57,  55,  64,  62,
+	 191, 189, 198, 197, 171, 170, 179, 177,
+	  69,  68,  77,  75,  50,  48,  57,  56,
+	 184, 183, 191, 190, 165, 163, 172, 170,
+	  83,  81,  90,  89,  63,  62,  70,  69,
+	 198, 196, 205, 203, 178, 177, 185, 184,
+	  76,  75,  83,  82,  57,  55,  64,  62,
+	 191, 189, 198, 197, 171, 170, 179, 177
+};
+
+#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
+#endif /* ENABLE_FSK9600 */
diff -u --recursive --new-file v2.1.6/linux/drivers/net/soundmodem.c linux/drivers/net/soundmodem.c
--- v2.1.6/linux/drivers/net/soundmodem.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/soundmodem.c	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,2120 @@
+/*****************************************************************************/
+
+/*
+ *	soundmodem.c  -- soundcard radio modem driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Command line options (insmod command line)
+ * 
+ *  hardware hardware type; 0=sbc, 1=wss, any other value invalid
+ *  mode     mode type; 0=1200 baud AFSK, 1=9600 baud FSK, any other
+ *           value invalid
+ *  iobase   base address of the soundcard; common values are 0x220 for sbc,
+ *           0x530 for wss
+ *  irq      interrupt number; common values are 7 or 5 for sbc, 11 for wss
+ *  dma      dma number; common values are 0 or 1
+ * 
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <limits.h>
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+#define SM_DEBUG
+
+#define ENABLE_SBC
+#define ENABLE_WSS
+#undef  ENABLE_WSSFDX
+
+#define ENABLE_AFSK1200
+#define ENABLE_FSK9600
+
+/* --------------------------------------------------------------------- */
+
+#include "sm_tables.h"
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+	int hardware, mode, iobase, irq, dma, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = { 
+{ SM_HARDWARE_INVALID, SM_MODE_INVALID, -1, 0, 0, -1, -1, -1 }, 
+};
+
+/* --------------------------------------------------------------------- */
+/* 
+ * the sbc converter's registers 
+ */
+#define DSP_RESET(iobase)        (iobase+0x6)
+#define DSP_READ_DATA(iobase)    (iobase+0xa)
+#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
+#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
+#define SBC_EXTENT               16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+#define SBC_OUTPUT             0x14
+#define SBC_INPUT              0x24
+#define SBC_HISPEED            0x48
+#define SBC_HI_OUTPUT          0x91 
+#define SBC_HI_INPUT           0x99 
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT  0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90 
+#define SBC_HI_INPUT_AUTOINIT  0x98
+#define SBC_IMMED_INT          0xf2
+#define SBC_GET_REVISION       0xe1
+#define ESS_GET_REVISION       0xe7
+#define SBC_SPEAKER_ON         0xd1
+#define SBC_SPEAKER_OFF        0xd3
+#define SBC_DMA_ON             0xd0
+#define SBC_DMA_OFF            0xd4
+#define SBC_SAMPLE_RATE        0x40
+#define SBC_MONO_8BIT          0xa0
+#define SBC_MONO_16BIT         0xa4
+#define SBC_STEREO_8BIT        0xa8
+#define SBC_STEREO_16BIT       0xac
+
+#define DMA_MODE_AUTOINIT      0x10
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase)       (iobase+0)
+#define WSS_STATUS(iobase)       (iobase+3)
+#define WSS_CODEC_IA(iobase)     (iobase+4)
+#define WSS_CODEC_ID(iobase)     (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase)   (iobase+7)
+
+#define WSS_EXTENT   8
+
+/* --------------------------------------------------------------------- */
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase)     (iobase)
+#define MIDI_STATUS(iobase)   (iobase+1)
+#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+/* ---------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+#define SP_SER  1
+#define SP_PAR  2
+#define SP_MIDI 4
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board. 
+ */
+
+struct sm_state {
+	struct hdlcdrv_state hdrv;
+
+	struct config {
+		int hardware;
+		int mode;
+	} config;
+
+	struct modem_state {
+		unsigned char revhi, revlo;
+		
+		unsigned int shreg;
+		
+		unsigned char last_sample;
+		unsigned int bit_pll;
+		unsigned int dcd_shreg;
+		int dcd_sum0, dcd_sum1, dcd_sum2;
+		unsigned int dcd_time;
+		unsigned char last_rxbit;
+		unsigned char tx_bit;
+		
+		signed char filt[9];
+		
+		unsigned long descram;
+		unsigned long scram;
+		
+		unsigned char *dmabufr;
+		unsigned char *dmabufw;
+		unsigned char dmabufidx;
+		unsigned char oldptt;
+	} modem;
+	
+#define DIAGDATALEN 64
+	struct diag_data {
+		unsigned int mode;
+		unsigned int flags;
+		volatile int ptr;
+		short data[DIAGDATALEN];
+	} diag;
+	
+#ifdef SM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+struct modem_info {
+	struct hdlcdrv_ops hops;
+	unsigned int samplerate;
+	unsigned char sbcmix;
+	unsigned char sperbit;
+	char *mode_name; /* used for request_{region,irq,dma} */
+	/*
+	 * low level chip informations
+	 */
+	unsigned char data_fmt;
+	unsigned int dmabuflen;
+	void (*modulator)(struct sm_state *, unsigned char *, int);
+	void (*demodulator)(struct sm_state *, unsigned char *, int);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline sm_int_freq(struct sm_state *sm)
+{
+#ifdef SM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/* 
+	 * measure the interrupt frequency
+	 */
+	sm->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
+		sm->debug_vals.last_jiffies = cur_jiffies;
+		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
+		sm->debug_vals.cur_intcnt = 0;
+	}
+#endif /* SM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+static int inline check_lpt(unsigned int iobase)
+{
+	unsigned char b1,b2;
+	int i;
+
+	if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+		return 0;
+	if (check_region(iobase, LPT_EXTENT))
+		return 0;
+	b1 = inb(LPT_DATA(iobase));
+	b2 = inb(LPT_CONTROL(iobase));
+	outb(0xaa, LPT_DATA(iobase));
+	i = inb(LPT_DATA(iobase)) == 0xaa;
+	outb(0x55, LPT_DATA(iobase));
+	i &= inb(LPT_DATA(iobase)) == 0x55;
+	outb(0x0a, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+	outb(0x05, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+	outb(b1, LPT_DATA(iobase));
+	outb(b2, LPT_CONTROL(iobase));
+	return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+	{ "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart inline check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+		return c_uart_unknown;
+	if (check_region(iobase, SER_EXTENT))
+		return c_uart_unknown;
+	b1 = inb(UART_MCR(iobase));
+	outb(b1 | 0x10, UART_MCR(iobase));	/* loopback mode */
+	b2 = inb(UART_MSR(iobase));
+	outb(0x1a, UART_MCR(iobase));
+	b3 = inb(UART_MSR(iobase)) & 0xf0;
+	outb(b1, UART_MCR(iobase));	   /* restore old values */
+	outb(b2, UART_MSR(iobase));
+	if (b3 != 0x90) 
+		return c_uart_unknown;
+	inb(UART_RBR(iobase));
+	inb(UART_RBR(iobase));
+	outb(0x01, UART_FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, UART_SCR(iobase));
+		b1 = inb(UART_SCR(iobase));
+		outb(0xa5, UART_SCR(iobase));
+		b2 = inb(UART_SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5)) 
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline check_midi(unsigned int iobase)
+{
+	unsigned long timeout;
+	unsigned long flags;
+	unsigned char b;
+
+	if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+		return 0;
+	if (check_region(iobase, MIDI_EXTENT))
+		return 0;
+	timeout = jiffies + (HZ / 100);
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	save_flags(flags);
+	cli();
+	outb(0xff, MIDI_DATA(iobase));
+	b = inb(MIDI_STATUS(iobase));
+	restore_flags(flags);
+	if (!(b & MIDI_WRITE_EMPTY))
+		return 0;
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline output_status(struct sm_state *sm)
+{
+	int invert_dcd = 0;
+	int invert_ptt = 0;
+
+	int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt;
+	int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
+
+	if (sm->hdrv.ptt_out.flags & SP_SER) {
+		outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
+		outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_PAR) {
+		outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
+		outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_open(struct sm_state *sm)
+{
+	enum uart u = c_uart_unknown;
+
+	sm->hdrv.ptt_out.flags = 0;
+	if (sm->hdrv.ptt_out.seriobase > 0 && 
+	    sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+	    ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
+		sm->hdrv.ptt_out.flags |= SP_SER;
+		request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
+		outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
+		/* 5 bits, 1 stop, no parity, no break, Div latch access */
+		outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase)); 
+		outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
+		outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
+		/* LCR and MCR set by output_status */
+	}
+	if (sm->hdrv.ptt_out.pariobase > 0 && 
+	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+	    check_lpt(sm->hdrv.ptt_out.pariobase)) {
+		sm->hdrv.ptt_out.flags |= SP_PAR;
+		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
+	}
+	if (sm->hdrv.ptt_out.midiiobase > 0 && 
+	    sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+	    check_midi(sm->hdrv.ptt_out.midiiobase)) {
+		sm->hdrv.ptt_out.flags |= SP_MIDI;
+		request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT, 
+			       "sm midi ptt");
+	}
+	output_status(sm);
+
+	printk(KERN_INFO "sm: ptt output:");
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+		       uart_str[u]);
+	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
+	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
+	if (!sm->hdrv.ptt_out.flags)
+		printk(" none");
+	printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_close(struct sm_state *sm)
+{
+	/* release regions used for PTT output */
+	sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+	output_status(sm);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_PAR) 
+		release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_MIDI) 
+		release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
+	sm->hdrv.ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+static inline void diag_trigger(struct sm_state *sm)
+{
+	if (sm->diag.ptr < 0) 
+		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+			sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+{
+	int val;
+
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT && 
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) || 
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_one(struct sm_state *sm, int val)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT && 
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) || 
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== modem routines 1200 baud =========================
+ */
+
+static inline unsigned int hweight32(unsigned int w)
+	__attribute__ ((unused));
+static inline unsigned int hweight16(unsigned short w)
+	__attribute__ ((unused));
+static inline unsigned int hweight8(unsigned char w)
+        __attribute__ ((unused));
+
+static inline unsigned int hweight32(unsigned int w) 
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+static inline unsigned int hweight16(unsigned short w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+static inline unsigned int hweight8(unsigned char w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_AFSK1200
+
+static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	static const int dds_inc[2] = { 8192, 15019 };
+	int j, k;
+
+	for (; buflen >= 8; buflen -= 8) {
+		if (sm->modem.shreg <= 1)
+			sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		sm->modem.tx_bit = (sm->modem.tx_bit ^ 
+				    (!(sm->modem.shreg & 1))) & 1;
+		sm->modem.shreg >>= 1;
+		k = dds_inc[sm->modem.tx_bit & 1];
+		for (j = 0; j < 8; j++) {
+			*buf++ = sinetab[(sm->modem.bit_pll >> 10) & 0x3f];
+			sm->modem.bit_pll += k;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	int j;
+	signed char *fp;
+	const signed char *coeffp;
+	int sum1, sum2;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sm->modem.filt[8] = (*buf - 128);
+		for (sum1 = j = 0, fp = sm->modem.filt+1, coeffp = tx_lo_i; 
+		     j < 8; j++, fp++, coeffp++) {
+			sum1 += (*coeffp) * (*fp);
+			fp[-1] = fp[0];
+		}
+		sum1 >>= 7;
+		sum2 = sum1 * sum1;
+		for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_lo_q; 
+		     j < 8; j++, fp++, coeffp++)
+			sum1 += (*coeffp) * (*fp);
+		sum1 >>= 7;
+		sum2 += sum1 * sum1;
+		for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_i; 
+		     j < 8; j++, fp++, coeffp++)
+			sum1 += (*coeffp) * (*fp);
+		sum1 >>= 7;
+		sum2 -= sum1 * sum1;
+		for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_q; 
+		     j < 8; j++, fp++, coeffp++)
+			sum1 += (*coeffp) * (*fp);
+		sum1 >>= 7;
+		sum2 -= sum1 * sum1;
+		sm->modem.dcd_shreg <<= 1;
+		sm->modem.bit_pll += 0x2000;
+		newsample = (sum2 > 0);
+		if (sm->modem.last_sample ^ newsample) {
+			sm->modem.last_sample = newsample;
+			sm->modem.dcd_shreg |= 1;
+			sm->modem.bit_pll += pll_corr
+				[sm->modem.bit_pll < 0x9000];
+			j = 4 * hweight8(sm->modem.dcd_shreg & 0x38)
+				- hweight16(sm->modem.dcd_shreg & 0x7c0);
+			sm->modem.dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
+		if ((--sm->modem.dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + 
+						   sm->modem.dcd_sum1 + 
+						   sm->modem.dcd_sum2) < 0);
+			sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
+			sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
+			sm->modem.dcd_sum0 = 2; /* slight bias */
+			sm->modem.dcd_time = 120;
+		}
+		if (sm->modem.bit_pll >= 0x10000) {
+			sm->modem.bit_pll &= 0xffff;
+			sm->modem.shreg >>= 1;
+			sm->modem.shreg |= (!(sm->modem.last_rxbit ^
+					      sm->modem.last_sample)) << 16;
+			sm->modem.last_rxbit = sm->modem.last_sample;
+			diag_trigger(sm);
+			if (sm->modem.shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
+				sm->modem.shreg = 0x10000;
+			}
+		}
+		diag_add(sm, sm->modem.filt[7] << 8, sum2);
+	}
+}
+
+#endif /* ENABLE_AFSK1200 */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== modem routines 9600 baud =========================
+ */
+
+#ifdef ENABLE_FSK9600
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_SBC
+
+static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 4; buflen -= 4) {
+		if (sm->modem.shreg <= 1)
+			sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		sm->modem.scram = ((sm->modem.scram << 1) |
+				   (sm->modem.scram & 1));
+		sm->modem.scram ^= (!(sm->modem.shreg & 1));
+		sm->modem.shreg >>= 1;
+		if (sm->modem.scram & (SCRAM_TAP1 << 1))
+			sm->modem.scram ^= (SCRAM_TAPN << 1);
+		sm->modem.tx_bit = (sm->modem.tx_bit << 1) | 
+			(!!(sm->modem.scram & (SCRAM_TAP1 << 2)));
+		cp = tx_filter_9k6_4 + (sm->modem.tx_bit & 0xff);
+		for (j = 0; j < 4; j++) {
+			*buf++ = *cp;
+			cp += 0x100;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sm->modem.dcd_shreg <<= 1;
+		sm->modem.bit_pll += 0x4000;
+		curbit = (*buf >= 0x80);
+		if (sm->modem.last_sample ^ curbit) {
+			sm->modem.dcd_shreg |= 1;
+			sm->modem.bit_pll += pll_corr
+				[sm->modem.bit_pll < 0xa000];
+			sm->modem.dcd_sum0 += 8 * 
+				hweight8(sm->modem.dcd_shreg & 0x0c) - 
+					!!(sm->modem.dcd_shreg & 0x10);
+		}
+		sm->modem.last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
+		if ((--sm->modem.dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + 
+						   sm->modem.dcd_sum1 + 
+						   sm->modem.dcd_sum2) < 0);
+			sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
+			sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
+			sm->modem.dcd_sum0 = 2; /* slight bias */
+			sm->modem.dcd_time = 240;
+		}
+		if (sm->modem.bit_pll >= 0x10000) {
+			sm->modem.bit_pll &= 0xffff;
+			sm->modem.descram = (sm->modem.descram << 1) | curbit;
+			descx = sm->modem.descram ^ (sm->modem.descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			sm->modem.shreg >>= 1;
+			sm->modem.shreg |= (!(descx & 1)) << 16;
+			if (sm->modem.shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
+				sm->modem.shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+#endif /* ENABLE_SBC */
+
+/* --------------------------------------------------------------------- */
+
+#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
+
+static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 5; buflen -= 5) {
+		if (sm->modem.shreg <= 1)
+			sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		sm->modem.scram = ((sm->modem.scram << 1) |
+				   (sm->modem.scram & 1));
+		sm->modem.scram ^= (!(sm->modem.shreg & 1));
+		sm->modem.shreg >>= 1;
+		if (sm->modem.scram & (SCRAM_TAP1 << 1))
+			sm->modem.scram ^= (SCRAM_TAPN << 1);
+		sm->modem.tx_bit = (sm->modem.tx_bit << 1) | 
+			(!!(sm->modem.scram & (SCRAM_TAP1 << 2)));
+		cp = tx_filter_9k6_5 + (sm->modem.tx_bit & 0xff);
+		for (j = 0; j < 5; j++) {
+			*buf++ = *cp;
+			cp += 0x100;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sm->modem.dcd_shreg <<= 1;
+		sm->modem.bit_pll += 0x3333;
+		curbit = (*buf >= 0x80);
+		if (sm->modem.last_sample ^ curbit) {
+			sm->modem.dcd_shreg |= 1;
+			sm->modem.bit_pll += pll_corr
+				[sm->modem.bit_pll < 0x9999];
+			sm->modem.dcd_sum0 += 16 * 
+				hweight8(sm->modem.dcd_shreg & 0x0c) - 
+					hweight8(sm->modem.dcd_shreg & 0x70);
+		}
+		sm->modem.last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
+		if ((--sm->modem.dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + 
+						   sm->modem.dcd_sum1 + 
+						   sm->modem.dcd_sum2) < 0);
+			sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
+			sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
+			sm->modem.dcd_sum0 = 2; /* slight bias */
+			sm->modem.dcd_time = 240;
+		}
+		if (sm->modem.bit_pll >= 0x10000) {
+			sm->modem.bit_pll &= 0xffff;
+			sm->modem.descram = (sm->modem.descram << 1) | curbit;
+			descx = sm->modem.descram ^ (sm->modem.descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			sm->modem.shreg >>= 1;
+			sm->modem.shreg |= (!(descx & 1)) << 16;
+			if (sm->modem.shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
+				sm->modem.shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
+#endif /* ENABLE_FSK9600 */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== soundblaster specific routines ===================
+ */
+
+#ifdef ENABLE_SBC
+
+static int inline reset_dsp(struct device *dev)
+{
+	int i;
+
+	outb(1, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0x100; i++)
+		SLOW_DOWN_IO;
+	outb(0, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0xffff; i++)
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
+			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
+				return 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline write_dsp(struct device *dev, unsigned char data)
+{
+	int i;
+	
+	for (i = 0; i < 0xffff; i++)
+		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
+			outb(data, DSP_WRITE_DATA(dev->base_addr));
+			return;
+		}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline read_dsp(struct device *dev, unsigned char *data)
+{
+	int i;
+
+	if (!data)
+		return 0;
+	for (i = 0; i < 0xffff; i++) 
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
+			*data = inb(DSP_READ_DATA(dev->base_addr));
+			return 1;
+		}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack(struct device *dev)
+{
+	inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_dsp(struct device *dev, int send)
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+        unsigned long flags;
+        static const unsigned char sbcmode[2][2] = {
+        { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
+	{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }};
+        static const unsigned char dmamode[2] = 
+        { DMA_MODE_READ | DMA_MODE_AUTOINIT, 
+		  DMA_MODE_WRITE  | DMA_MODE_AUTOINIT};
+	static const unsigned char sbcskr[2] = 
+	{ SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
+	unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr);
+
+	send = !!send;
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "sm: cannot reset sb dsp\n");
+                return;
+        }
+        if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000)
+                panic("sm: DMA buffer violates DMA boundary!");
+        save_flags(flags);
+        cli();
+        sbc_int_ack(dev);
+        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+        write_dsp(dev, mi->data_fmt);
+        write_dsp(dev, sbcskr[send]); 
+        disable_dma(dev->dma);
+        clear_dma_ff(dev->dma);
+        set_dma_mode(dev->dma, dmamode[send]);
+        set_dma_addr(dev->dma, dmabufaddr);
+        set_dma_count(dev->dma, mi->dmabuflen);
+        enable_dma(dev->dma);
+        sbc_int_ack(dev);
+        write_dsp(dev, SBC_HISPEED);
+        write_dsp(dev, ((mi->dmabuflen >> 1) - 1) & 0xff);
+        write_dsp(dev, ((mi->dmabuflen >> 1) - 1) >> 8);
+        write_dsp(dev, sbcmode[mi->samplerate >= 13000][send]);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+#if 0
+static int probe_int(struct device *dev, struct sm_state *sm)
+{
+	unsigned long irqs;
+	int irq;
+
+	irqs = probe_irq_on();
+	setup_dma_dsp(dev, virt_to_bus(sm->modem.dmabufr), 4, 256-77, 0);
+	udelay(2000);
+        irq = probe_irq_off(irqs);
+	disable_dma(dev->dma);
+	return irq;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+	unsigned char new_ptt;
+	unsigned char *buf;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	new_ptt = hdlcdrv_ptt(&sm->hdrv);
+ 	sbc_int_ack(dev);
+ 	buf = sm->modem.dmabufr;
+	if (sm->modem.dmabufidx)
+		buf += mi->dmabuflen/2;
+	sm->modem.dmabufidx = !sm->modem.dmabufidx;
+	if (sm->modem.oldptt != new_ptt) {
+		disable_dma(dev->dma);
+		sti();
+ 		sm->modem.dmabufidx = 0;
+		if (!new_ptt) {
+			setup_dma_dsp(dev, 0);
+			goto endint;
+		}
+		mi->demodulator(sm, buf, mi->dmabuflen/2);
+		mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2);
+ 		setup_dma_dsp(dev, 1);
+		mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2, 
+			      mi->dmabuflen/2);
+		goto endint;
+ 	}
+	sm_int_freq(sm);
+	sti();
+	/*
+	 * check if transmitter active
+	 */
+	if (new_ptt)
+		mi->modulator(sm, buf, mi->dmabuflen/2);
+	else {
+		mi->demodulator(sm, buf, mi->dmabuflen/2);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+	}
+ endint:
+	sm->modem.oldptt = new_ptt;
+	output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_open(struct device *dev) 
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+	unsigned char revreq = (mi->samplerate >= 13000) ? 3 : 2;
+	
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev))
+		return -ENODEV;
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &sm->modem.revhi) || 
+	    !read_dsp(dev, &sm->modem.revlo))
+		return -ENODEV;
+	if (sm->modem.revhi < revreq) {
+		printk(KERN_ERR "sm: sbc io 0x%lx: DSP rev %d.%02d too "
+		       "old, at least %d.00 required\n", dev->base_addr,
+		       sm->modem.revhi, sm->modem.revlo, revreq);
+		return -ENODEV;
+	}
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	sm->modem.shreg = sm->modem.last_sample = 0;
+	sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
+	sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
+	sm->modem.dmabufidx = sm->modem.oldptt = 0;
+	sm->modem.dmabufw = NULL;
+	sm->modem.dcd_time = 120;
+	sm->modem.dcd_sum0 = 2;
+#if 0
+	if (!dev->irq) {
+		int irq = probe_int(dev, sm);
+		if (irq < 0) {
+			printk(KERN_ERR "sm: irq autoprobe failed\n");
+			kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+			return -EBUSY;
+		}
+		dev->irq = irq;
+	}
+#endif
+	if (request_dma(dev->dma, mi->mode_name)) {
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
+			mi->mode_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, mi->mode_name);
+	setup_dma_dsp(dev, 0);
+	output_open(sm);
+	printk(KERN_INFO "sm: sbc at iobase 0x%lx irq %u dma "
+	       "%u DSP revision %u.%02u\n", dev->base_addr, dev->irq, 
+	       dev->dma, (unsigned int)sm->modem.revhi, 
+	       (unsigned int)sm->modem.revlo);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_close(struct device *dev) 
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree(sm->modem.dmabufr);
+	output_close(sm);
+	printk(KERN_INFO "sm: close sbc at iobase 0x%lx irq %u dma %u\n", 
+	       dev->base_addr, dev->irq, dev->dma);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#endif /* ENABLE_SBC */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== Windows Sound System specific routines ==========
+ */
+
+#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
+
+static void write_codec(struct device *dev, unsigned char idx,
+			unsigned char data)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx, WSS_CODEC_IA(dev->base_addr));
+	outb(data, WSS_CODEC_ID(dev->base_addr));
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct device *dev, unsigned char idx)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx & 0xf, WSS_CODEC_IA(dev->base_addr));
+	return inb(WSS_CODEC_ID(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline wss_ack_int(struct device *dev)
+{
+	outb(0, WSS_CODEC_STATUS(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct device *dev, unsigned char sdc, 
+			  unsigned char src_l, unsigned char src_r, 
+			  int igain_l, int igain_r,
+			  int ogain_l, int ogain_r)
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+
+	unsigned char tmp, reg0, reg1, reg6, reg7;
+	static const signed char irqtab[16] = 
+	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
+		  -1, -1 };
+	static const signed char dmatab[4] = { 1, 2, -1, 3 };
+	unsigned long time;
+				
+	tmp = inb(WSS_STATUS(dev->base_addr));
+	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
+	    (tmp & 0x3f) != 0x0f) {
+		printk(KERN_ERR "sm: WSS card not found, address 0x%lx, ID "
+		       "register 0x%02x\n", dev->base_addr, (int)tmp);
+		return -1;
+	}
+	if ((tmp & 0x80) && ((dev->dma == 0) || ((dev->irq >= 8) && 
+						 (dev->irq != 9)))) {
+		printk(KERN_ERR "sm: WSS: DMA0 and/or IRQ8..IRQ15 (except "
+		       "IRQ9) cannot be used on an 8bit card\n");
+		return -1;
+	}		
+	if (dev->irq > 15 || irqtab[dev->irq] == -1) {
+		printk(KERN_ERR "sm: WSS: invalid interrupt %d\n", 
+		       (int)dev->irq);
+		return -1;
+	}
+	if (dev->dma > 3 || dmatab[dev->dma] == -1) {
+		printk(KERN_ERR "sm: WSS: invalid dma channel %d\n", 
+		       (int)dev->dma);
+		return -1;
+	}
+	tmp = irqtab[dev->irq] | dmatab[dev->dma];
+	outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); /* irq probe */
+	if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
+		outb(0, WSS_CONFIG(dev->base_addr));
+		printk(KERN_ERR "sm: WSS: IRQ%d is not free!\n", dev->irq);
+	}
+	outb(tmp, WSS_CONFIG(dev->base_addr));
+	/*
+	 * initialize the codec
+	 */
+	if (igain_l < 0)
+		igain_l = 0;
+	if (igain_r < 0)
+		igain_r = 0;
+	if (ogain_l > 0)
+		ogain_l = 0;
+	if (ogain_r > 0)
+		ogain_r = 0;
+	reg0 = (src_l << 6) & 0xc0;
+	reg1 = (src_r << 6) & 0xc0;
+	if (reg0 == 0x80 && igain_l >= 20) {
+		reg0 |= 0x20;
+		igain_l -= 20;
+	}
+	if (reg1 == 0x80 && igain_r >= 20) {
+		reg1 |= 0x20;
+		igain_r -= 20;
+	}
+	if (igain_l > 23)
+		igain_l = 23;
+	if (igain_r > 23)
+		igain_r = 23;
+	reg0 |= igain_l * 2 / 3;
+	reg1 |= igain_r * 2 / 3;
+	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
+	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
+#if 1
+	write_codec(dev, 9, 0);
+	write_codec(dev, 15, 0xaa);
+	write_codec(dev, 14, 0x55);
+	if ((read_codec(dev, 15) != 0xaa) || (read_codec(dev, 14) != 0x55)) 
+		goto codec_err;
+#endif
+	write_codec(dev, 0x48, mi->data_fmt);  /* Clock and data format register */
+	write_codec(dev, 0x49, sdc ? 0xc : 0x8); /* MCE and interface config reg */
+	/* single DMA channel, disable both DMA */
+	/* clear MCE and wait until ACI set */
+	time = jiffies + HZ/4;
+	while (!(read_codec(dev, 0x0b) & 0x20) &&
+	       ((signed)(jiffies - time) < 0));
+	/* wait until ACI cleared */
+	while ((read_codec(dev, 0x0b) & 0x20) &&
+	       ((signed)(jiffies - time) < 0));
+	if ((signed)(jiffies - time) >= 0) {
+		printk(KERN_WARNING "sm: ad1848 auto calibration timed out\n");
+		goto codec_err;
+	}
+        write_codec(dev, 0, reg0); /* left input control */
+        write_codec(dev, 1, reg1); /* right input control */
+        write_codec(dev, 2, 0x80); /* left aux#1 input control */
+        write_codec(dev, 3, 0x80); /* right aux#1 input control */
+        write_codec(dev, 4, 0x80); /* left aux#2 input control */
+        write_codec(dev, 5, 0x80); /* right aux#2 input control */
+        write_codec(dev, 6, reg6); /* left dac control */
+        write_codec(dev, 7, reg7); /* right dac control */
+        write_codec(dev, 0xa, 0x2); /* pin control register */
+        write_codec(dev, 0xd, 0x0); /* digital mix control */
+	sm->modem.revhi = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
+	sm->modem.revlo = read_codec(dev, 0xc) & 0xf;
+	/*
+	 * print revisions
+	 */
+	printk(KERN_INFO "sm: WSS revision %d, CODEC revision %d\n", 
+	       (int)sm->modem.revhi, (int)sm->modem.revlo);
+	return 0;
+ codec_err:
+	outb(0, WSS_CONFIG(dev->base_addr));
+	printk(KERN_ERR "sm: no WSS soundcard found at address 0x%lx\n",
+	       dev->base_addr);
+	return -1;
+}
+
+#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_WSS
+
+static void setup_dma_wss(struct device *dev, int flg)
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+        unsigned long flags;
+        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
+        static const unsigned char dmamode[2] = 
+        { DMA_MODE_READ | DMA_MODE_AUTOINIT, 
+		  DMA_MODE_WRITE  | DMA_MODE_AUTOINIT};
+	unsigned char oldcodecmode, codecdma;
+	long abrt;
+	unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr);
+
+        if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000)
+                panic("sm: DMA buffer violates DMA boundary!");
+	flg = !!flg;
+        save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	wss_ack_int(dev);
+	if ((codecdma = read_codec(dev, 11)) & 0x10) {
+		disable_dma(dev->dma);
+		clear_dma_ff(dev->dma);
+		set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]);
+		set_dma_addr(dev->dma, dmabufaddr);
+		set_dma_count(dev->dma, mi->dmabuflen);
+		enable_dma(dev->dma);
+		abrt = 0;
+		while (((codecdma = read_codec(dev, 11)) & 0x10) ||
+		       ((++abrt) >= 0x10000));
+	}
+        disable_dma(dev->dma);
+        clear_dma_ff(dev->dma);
+        set_dma_mode(dev->dma, dmamode[flg]);
+        set_dma_addr(dev->dma, dmabufaddr);
+        set_dma_count(dev->dma, mi->dmabuflen);
+        enable_dma(dev->dma);
+	write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff);
+	write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8);
+	write_codec(dev, 9, codecmode[flg]);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+	unsigned char new_ptt;
+	unsigned char *buf;
+	unsigned long flags;
+	int dmares;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	new_ptt = hdlcdrv_ptt(&sm->hdrv);
+	save_flags(flags);
+	cli();
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	dmares = get_dma_residue(dev->dma);
+	enable_dma(dev->dma);
+	if (dmares <= 0)
+		dmares = mi->dmabuflen;
+	buf = sm->modem.dmabufr;
+	if (dmares > mi->dmabuflen/2)
+		buf += mi->dmabuflen/2;
+	if (dmares > mi->dmabuflen/2)
+		dmares -= mi->dmabuflen/2;
+#ifdef SM_DEBUG
+	if (!sm->debug_vals.last_pllcorr || 
+	    dmares < sm->debug_vals.last_pllcorr)
+		sm->debug_vals.last_pllcorr = dmares;
+#endif /* SM_DEBUG */
+	dmares--;
+	write_codec(dev, 15, dmares & 0xff);
+	write_codec(dev, 14, dmares >> 8);
+	restore_flags(flags);
+	if (sm->modem.oldptt != new_ptt) {
+		disable_dma(dev->dma);
+		sti();
+ 		sm->modem.dmabufidx = 0;
+		if (!new_ptt) {
+			setup_dma_wss(dev, 0);
+			goto endint;
+		}
+		mi->demodulator(sm, buf, mi->dmabuflen/2);
+		mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2);
+ 		setup_dma_wss(dev, 1);
+		mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2,
+			      mi->dmabuflen/2);
+		goto endint;
+ 	}
+	sm_int_freq(sm);
+	sti();
+	/*
+	 * check if transmitter active
+	 */
+	if (new_ptt)
+		mi->modulator(sm, buf, mi->dmabuflen/2);
+	else {
+		mi->demodulator(sm, buf, mi->dmabuflen/2);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+	}
+ endint:
+	sm->modem.oldptt = new_ptt;
+	output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_open(struct device *dev) 
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, 1, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	sm->modem.shreg = sm->modem.last_sample = 0;
+	sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
+	sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
+	sm->modem.dmabufidx = sm->modem.oldptt = 0;
+	sm->modem.dmabufw = NULL;
+	sm->modem.dcd_time = 120;
+	sm->modem.dcd_sum0 = 2;
+	if (request_dma(dev->dma, mi->mode_name)) {
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
+			mi->mode_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, mi->mode_name);
+	setup_dma_wss(dev, 0);
+	output_open(sm);
+	printk(KERN_INFO "sm: wss at iobase 0x%lx irq %u dma "
+	       "%u\n", dev->base_addr, dev->irq, dev->dma);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev) 
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+	output_close(sm);
+	printk(KERN_INFO "sm: close wss at iobase 0x%lx irq %u"
+	       " dma %u\n", dev->base_addr, dev->irq, dev->dma);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#endif /* ENABLE_WSS */
+
+/* --------------------------------------------------------------------- */
+/*
+ * =========== Windows Sound System Fullduplex specific routines ==========
+ */
+
+/*
+ * This does _not_ work on my hardware
+ */
+
+#ifdef ENABLE_WSSFDX
+
+static void setup_dma_wssfdx(struct device *dev)
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+        unsigned long flags;
+	unsigned char codecdma;
+	long abrt;
+	unsigned long dmabufraddr = virt_to_bus(sm->modem.dmabufr);
+	unsigned long dmabufwaddr = virt_to_bus(sm->modem.dmabufw);
+
+        if (((dmabufraddr & 0xffff) + mi->dmabuflen > 0x10000) ||
+	    ((dmabufwaddr & 0xffff) + mi->dmabuflen > 0x10000))
+                panic("sm: DMA buffer violates DMA boundary!");
+        save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+        write_codec(dev, 9, 0x8); /* disable codec */
+	wss_ack_int(dev);
+	if ((codecdma = read_codec(dev, 11)) & 0x10) {
+		disable_dma(dev->dma);
+		disable_dma(!dev->dma);
+		clear_dma_ff(dev->dma);
+		set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+		set_dma_addr(dev->dma, dmabufwaddr);
+		set_dma_count(dev->dma, mi->dmabuflen);
+		set_dma_mode(!dev->dma,  DMA_MODE_READ | DMA_MODE_AUTOINIT);
+		set_dma_addr(!dev->dma, dmabufraddr);
+		set_dma_count(!dev->dma, mi->dmabuflen);
+		enable_dma(dev->dma);
+		enable_dma(!dev->dma);
+		abrt = 0;
+		while (((codecdma = read_codec(dev, 11)) & 0x10) ||
+		       ((++abrt) >= 0x10000));
+	}
+	disable_dma(dev->dma);
+	disable_dma(!dev->dma);
+	clear_dma_ff(dev->dma);
+	set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+	set_dma_addr(dev->dma, dmabufwaddr);
+	set_dma_count(dev->dma, mi->dmabuflen);
+	set_dma_mode(!dev->dma,  DMA_MODE_READ | DMA_MODE_AUTOINIT);
+	set_dma_addr(!dev->dma, dmabufraddr);
+	set_dma_count(!dev->dma, mi->dmabuflen);
+	enable_dma(dev->dma);
+	enable_dma(!dev->dma);
+	write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff);
+	write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8);
+	write_codec(dev, 9, 0x0b);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int msgcnt = 10;
+
+static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+	unsigned char new_ptt;
+	unsigned char *bufr;
+	unsigned char *bufw;
+	unsigned long flags;
+	int dmares, dmares2, i;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	new_ptt = hdlcdrv_ptt(&sm->hdrv);
+	save_flags(flags);
+	cli();
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	disable_dma(!dev->dma);
+	clear_dma_ff(dev->dma);
+	dmares = get_dma_residue(dev->dma);
+	dmares2 = get_dma_residue(!dev->dma);
+	enable_dma(dev->dma);
+	enable_dma(!dev->dma);
+	if (dmares <= 0)
+		dmares = mi->dmabuflen;
+	if (dmares2 <= 0)
+		dmares2 = mi->dmabuflen;
+	bufw = sm->modem.dmabufw;
+	if (dmares > mi->dmabuflen/2)
+		bufw += mi->dmabuflen/2;
+	bufr = sm->modem.dmabufr;
+	if (dmares2 > mi->dmabuflen/2)
+		bufr += mi->dmabuflen/2;
+	if ((i = dmares) > mi->dmabuflen/2)
+		i -= mi->dmabuflen/2;
+#ifdef SM_DEBUG
+	if (!sm->debug_vals.last_pllcorr || 
+	    i < sm->debug_vals.last_pllcorr)
+		sm->debug_vals.last_pllcorr = i;
+#endif /* SM_DEBUG */
+	i--;
+	write_codec(dev, 15, i & 0xff);
+	write_codec(dev, 14, i >> 8);
+	restore_flags(flags);
+	sm_int_freq(sm);
+	sti();
+
+	if (msgcnt > 0) {
+		msgcnt--;
+		printk(KERN_DEBUG "sm: DMA residue: playback: %d "
+		       "capture: %d\n", dmares, dmares2);
+	}
+
+	/*
+	 * check if transmitter active
+	 */
+	if (new_ptt)
+		mi->modulator(sm, bufw, mi->dmabuflen/2);
+	else 
+		memset(bufw, 0x80, mi->dmabuflen/2);
+	mi->demodulator(sm, bufr, mi->dmabuflen/2);
+	hdlcdrv_arbitrate(dev, &sm->hdrv);
+	sm->modem.oldptt = new_ptt;
+	output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev) 
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, 0, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(sm->modem.dmabufw = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) {
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		return -ENOMEM;
+	}
+	sm->modem.shreg = sm->modem.last_sample = 0;
+	sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
+	sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
+	sm->modem.dmabufidx = sm->modem.oldptt = 0;
+	sm->modem.dcd_time = 120;
+	sm->modem.dcd_sum0 = 2;
+	if (request_dma(dev->dma, mi->mode_name)) {
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		kfree_s(sm->modem.dmabufw, mi->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_dma(!dev->dma, mi->mode_name)) {
+		free_dma(dev->dma);
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		kfree_s(sm->modem.dmabufw, mi->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
+			mi->mode_name, dev)) {
+		free_dma(dev->dma);
+		free_dma(!dev->dma);
+		kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+		kfree_s(sm->modem.dmabufw, mi->dmabuflen);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, mi->mode_name);
+	setup_dma_wssfdx(dev);
+	output_open(sm);
+	printk(KERN_INFO "sm: wss fdx at iobase 0x%lx irq %u dma1 "
+	       "%u dma2 %u\n", dev->base_addr, dev->irq, dev->dma, !dev->dma);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_close(struct device *dev) 
+{
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	disable_dma(!dev->dma);
+        write_codec(dev, 9, 0x8); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(!dev->dma);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree(sm->modem.dmabufr);
+	kfree(sm->modem.dmabufw);
+	output_close(sm);
+	printk(KERN_INFO "sm: close wss fdx at iobase 0x%lx irq %u"
+	       " dma %u\n", dev->base_addr, dev->irq, dev->dma);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#endif /* ENABLE_WSSFDX */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+
+#define SBC1200_SRATE (256-104) /* the SBC sampling rate, 256-(1E6/srate) */
+#define SBC1200_DMABUFLEN 192   /* DMA buffer duration exactly 20ms */
+
+#define SBC9600_SRATE (256-26) /* the SBC sampling rate, 256-(1E6/srate) */
+#define SBC9600_DMABUFLEN 768    /* DMA buffer duration exactly 20ms */
+
+#define WSS1200_DATAFMT 0x0e   /* 8bit unsigned PCM, Mono, XTAL1, 9.6kHz */
+#define WSS1200_DMABUFLEN 192        /* DMA buffer duration exactly 20ms */
+
+#define WSS9600_DATAFMT 0x0c    /* 8bit unsigned PCM, Mono, XTAL1, 48kHz */
+#define WSS9600_DMABUFLEN 960        /* DMA buffer duration exactly 20ms */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_SBC
+
+static const struct modem_info sbc1200_ops = { 
+{ 1200, sbc_open, sbc_close, sm_ioctl }, 9600, 1, 8, "sbc_1200",
+SBC1200_SRATE, SBC1200_DMABUFLEN,
+modulator_1200, demodulator_1200
+};
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info sbc9600_ops = {
+{ 9600, sbc_open, sbc_close, sm_ioctl }, 38400, 1, 4, "sbc_9600",
+SBC9600_SRATE, SBC9600_DMABUFLEN,
+modulator_9600_4, demodulator_9600_4
+};
+
+#endif /* ENABLE_SBC */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_WSS
+
+static const struct modem_info wss1200_ops = {
+{ 1200, wss_open, wss_close, sm_ioctl }, 9600, 0, 8, "wss_1200",
+WSS1200_DATAFMT, WSS1200_DMABUFLEN,
+modulator_1200, demodulator_1200
+};
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info wss9600_ops = {
+{ 9600, wss_open, wss_close, sm_ioctl }, 48000, 0, 5, "wss_9600",
+WSS9600_DATAFMT, WSS9600_DMABUFLEN,
+modulator_9600_5, demodulator_9600_5
+};
+
+#endif /* ENABLE_WSS */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_WSSFDX
+
+static const struct modem_info wss1200fdx_ops = {
+{ 1200, wssfdx_open, wssfdx_close, sm_ioctl }, 9600, 0, 8, "wss_fdx_1200",
+WSS1200_DATAFMT, WSS1200_DMABUFLEN,
+modulator_1200, demodulator_1200
+};
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info wss9600fdx_ops = {
+{ 9600, wssfdx_open, wssfdx_close, sm_ioctl }, 48000, 0, 5, "wss_fdx_9600",
+WSS9600_DATAFMT, WSS9600_DMABUFLEN,
+modulator_9600_5, demodulator_9600_5
+};
+
+#endif /* ENABLE_WSSFDX */
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info dummy_ops = {
+{ 0, NULL, NULL, sm_ioctl }, 0, 0, 0, "none",
+0, 0,
+NULL, NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info *ops_tab[3][2] = {
+#ifdef ENABLE_SBC
+{ &sbc1200_ops, &sbc9600_ops }, 
+#else /* ENABLE_SBC */
+{ &dummy_ops, &dummy_ops }, 
+#endif /* ENABLE_SBC */
+#ifdef ENABLE_WSS
+{ &wss1200_ops, &wss9600_ops },
+#else /* ENABLE_WSS */
+{ &dummy_ops, &dummy_ops }, 
+#endif /* ENABLE_WSS */
+#ifdef ENABLE_WSSFDX
+{ &wss1200fdx_ops, &wss9600fdx_ops }
+#else /* ENABLE_WSSFDX */
+{ &dummy_ops, &dummy_ops }
+#endif /* ENABLE_WSSFDX */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	int i;
+	struct sm_state *sm;
+	struct sm_ioctl bi;
+	unsigned long flags;
+	unsigned int newdiagmode;
+	unsigned int newdiagflags;
+	
+	if (!dev || !dev->priv || 
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMODEMTYPE:
+		bi.data.cfg.hardware = sm->config.hardware;
+		bi.data.cfg.mode = sm->config.mode;
+		break;
+
+	case SMCTL_SETMODEMTYPE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		if (bi.data.cfg.hardware < SM_HARDWARE_INVALID ||
+		    bi.data.cfg.hardware > SM_HARDWARE_WSSFDX ||
+		    bi.data.cfg.mode < SM_MODE_INVALID ||
+		    bi.data.cfg.mode > SM_MODE_FSK9600)
+			return -EINVAL;
+		sm->config.hardware = bi.data.cfg.hardware;
+		sm->config.mode = bi.data.cfg.mode;
+		if (bi.data.cfg.hardware == SM_HARDWARE_INVALID ||
+		    bi.data.cfg.mode == SM_MODE_INVALID)
+			sm->hdrv.ops = &dummy_ops.hops;
+		else {
+			sm->hdrv.ops = &ops_tab[sm->config.hardware][sm->config.mode]->hops;
+			if (!((struct modem_info *)sm->hdrv.ops)->samplerate)
+				return -ENODEV;
+		}
+		return 0;
+
+#ifdef SM_DEBUG
+	case SMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = sm->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = sm->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = sm->debug_vals.last_pllcorr;
+		break;
+#endif /* SM_DEBUG */
+
+	case SMCTL_GETMIXER:
+		i = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(bi));
+		if (i)
+			return i;
+		i = 0;
+		bi.data.mix.sample_rate = ((struct modem_info *)sm->hdrv.ops)->samplerate;
+		bi.data.mix.bit_rate = sm->hdrv.ops->bitrate;
+		if (((struct modem_info *)sm->hdrv.ops)->sbcmix) {
+			switch (sm->modem.revhi) {
+			case 2:
+				bi.data.mix.mixer_type = SM_MIXER_CT1335;
+				break;
+			case 3:
+				bi.data.mix.mixer_type = SM_MIXER_CT1345;
+				break;
+			case 4:
+				bi.data.mix.mixer_type = SM_MIXER_CT1745;
+				break;
+			}
+			if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
+			    bi.data.mix.reg < 0x80) {
+				save_flags(flags);
+				cli();
+				outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+				bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
+				restore_flags(flags);
+				i = 1;
+			}
+		} else {
+			bi.data.mix.mixer_type = SM_MIXER_AD1848;
+			if ((0x20ff >> bi.data.mix.reg) & 1) {
+				bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
+				i = 1;
+			}
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		if (((struct modem_info *)sm->hdrv.ops)->sbcmix) {
+			switch (sm->modem.revhi) {
+			case 2:
+				if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
+					return -EINVAL;
+				break;
+			case 3:
+				if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
+					return -EINVAL;
+				break;
+			case 4:
+				if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
+					return -EINVAL;
+				break;
+			default:
+				return -ENODEV;
+			}
+			if (bi.data.mix.reg >= 0x80)
+				return -EACCES;
+			save_flags(flags);
+			cli();
+			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+			outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
+			restore_flags(flags);
+			return 0;
+		} else {
+			if (bi.data.mix.mixer_type != SM_MIXER_AD1848)
+				return -EINVAL;
+			if (!((0x20ff >> bi.data.mix.reg) & 1))
+				return -EACCES;
+			write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
+			return 0;
+		}
+
+	case SMCTL_DIAGNOSE:
+		newdiagmode = bi.data.diag.mode;
+		newdiagflags = bi.data.diag.flags;
+		if (newdiagmode > SM_DIAGMODE_DEMOD)
+			return -EINVAL;
+		bi.data.diag.mode = sm->diag.mode;
+		bi.data.diag.flags = sm->diag.flags;
+		bi.data.diag.samplesperbit = ((struct modem_info *)sm->hdrv.ops)->sperbit;
+		if (sm->diag.mode != newdiagmode) {
+			save_flags(flags);
+			cli();
+			sm->diag.ptr = -1;
+			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+			sm->diag.mode = newdiagmode;
+			restore_flags(flags);
+			break;
+		}
+		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF)
+			break;
+		if (bi.data.diag.datalen > DIAGDATALEN)
+			bi.data.diag.datalen = DIAGDATALEN;
+		if (sm->diag.ptr < bi.data.diag.datalen)
+			break;
+		i = verify_area(VERIFY_WRITE, bi.data.diag.data,
+				bi.data.diag.datalen * 
+				sizeof(short));
+		if (i)
+			return i;
+		if (copy_to_user(bi.data.diag.data, sm->diag.data, 
+				 bi.data.diag.datalen * sizeof(short)))
+			return -EFAULT;
+		bi.data.diag.flags |= SM_DIAGFLAG_VALID;
+		save_flags(flags);
+		cli();
+		sm->diag.ptr = -1;
+		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+		sm->diag.mode = newdiagmode;
+		restore_flags(flags);
+		break;
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static
+#endif /* MODULE */
+int sm_init(void)
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct sm_state *sm;
+	char ifname[HDLCDRV_IFNAMELEN];
+	
+	printk(KERN_INFO "sm: compiled %s %s\n", __TIME__, __DATE__);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		sprintf(ifname, "sm%d", i);
+
+		if (sm_ports[i].hardware < SM_HARDWARE_INVALID ||
+		    sm_ports[i].hardware > SM_HARDWARE_WSSFDX ||
+		    sm_ports[i].mode < SM_MODE_INVALID ||
+		    sm_ports[i].mode > SM_MODE_FSK9600)
+			set_hw = 0;
+		if (set_hw) {
+			j = hdlcdrv_register_hdlcdrv(dev, &ops_tab[sm_ports[i].hardware]
+						     [sm_ports[i].mode]->hops, 
+						     sizeof(struct sm_state), ifname, 
+						     sm_ports[i].iobase,
+						     sm_ports[i].irq,
+						     sm_ports[i].dma);
+			if (!j) {
+				sm = (struct sm_state *)dev->priv;
+				sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+				sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+				sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+			}
+		} else 
+			j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops.hops, 
+						     sizeof(struct sm_state), 
+						     ifname, 0, 0, 0);
+		if (j) {
+			printk(KERN_WARNING "sm: cannot register net "
+			       "device\n");
+		} else
+			found++;
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+int hardware = SM_HARDWARE_INVALID;
+int mode = SM_MODE_INVALID;
+int iobase = -1;
+int irq = -1;
+int dma = -1;
+int seriobase = 0;
+int pariobase = 0;
+int midiiobase = 0;
+
+int init_module(void)
+{
+	printk(KERN_INFO "sm: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+
+	if (hardware != SM_HARDWARE_INVALID) {
+		if (iobase == -1)
+			iobase = (hardware == SM_HARDWARE_SBC) ? 0x220 : 0x530;
+		if (irq == -1)
+			irq = (hardware == SM_HARDWARE_SBC) ? 5 : 11;
+		if (dma == -1)
+			dma = 1;
+	}
+	sm_ports[0].hardware = hardware;
+	sm_ports[0].mode = mode;
+	sm_ports[0].iobase = iobase;
+	sm_ports[0].irq = irq;
+	sm_ports[0].dma = dma;
+	sm_ports[0].seriobase = seriobase;
+	sm_ports[0].pariobase = pariobase;
+	sm_ports[0].midiiobase = midiiobase;
+	sm_ports[1].hardware = SM_HARDWARE_INVALID;
+
+	return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	printk(KERN_INFO "sm: cleanup_module called\n");
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		struct sm_state *sm = (struct sm_state *)dev->priv;
+
+		if (sm) {
+			if (sm->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "sm: invalid magic in "
+				       "cleanup_module\n");
+			else 
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: sm=hw,mode,io,irq,dma,serio,pario[,hw,mode,io,irq,dma,serio,pario]
+ * hw=0: SBC, hw=1: WSS; mode=0: AFSK1200, mode=1: FSK9600
+ */
+
+void sm_setup(char *str, int *ints)
+{
+	int i;
+
+	for (i = 0; i < NR_PORTS; i++) 
+		if (ints[0] >= 8*(i+1)) {
+			sm_ports[i].hardware = ints[8*i+1];
+			sm_ports[i].mode = ints[8*i+2];
+			sm_ports[i].iobase = ints[8*i+3];
+			sm_ports[i].irq = ints[8*i+4];
+			sm_ports[i].dma = ints[8*i+5];
+			sm_ports[i].seriobase = ints[8*i+6];
+			sm_ports[i].pariobase = ints[8*i+7];
+			sm_ports[i].midiiobase = ints[8*i+8];
+		} else
+			sm_ports[i].hardware = SM_HARDWARE_INVALID;
+
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.6/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.1.6/linux/drivers/net/tulip.c	Thu Oct 10 19:10:56 1996
+++ linux/drivers/net/tulip.c	Fri Nov  1 13:58:44 1996
@@ -1133,6 +1133,25 @@
 		} while (++i < 15);
 
 		/* Now add this frame to the Tx list. */
+		{
+			unsigned long flags;
+			unsigned int entry;
+			
+			save_flags(flags); cli();
+			entry = tp->cur_tx++ % TX_RING_SIZE;
+			tp->dirty_tx++;
+			restore_flags(flags);
+
+			tp->tx_skbuff[entry] = 0;
+			/* Put the setup frame on the Tx list. */
+			tp->tx_ring[entry].length = 192 |
+			      (entry == TX_RING_SIZE-1 ? 0x0a000000 : 0x08000000);
+			tp->tx_ring[entry].buffer1 = virt_to_bus((char *)tp->setup_frame);
+			tp->tx_ring[entry].buffer2 = 0;
+			tp->tx_ring[entry].status = TRING_OWN;
+			/* Trigger an immediate transmit demand. */
+			tio_write(TPOLL_TRIGGER, CSR1);
+		}
 	}
 }
 
diff -u --recursive --new-file v2.1.6/linux/drivers/scsi/aha1740.c linux/drivers/scsi/aha1740.c
--- v2.1.6/linux/drivers/scsi/aha1740.c	Fri Mar  1 07:50:52 1996
+++ linux/drivers/scsi/aha1740.c	Wed Oct 30 01:20:26 1996
@@ -336,11 +336,11 @@
 	if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n");
 	for(i=0; i<SCpnt->use_sg; i++)
 	{
-	    cptr[i].dataptr = (long) sgpnt[i].address;
 	    cptr[i].datalen = sgpnt[i].length;
+	    cptr[i].dataptr = virt_to_bus(sgpnt[i].address);
 	}
 	ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
-	ecb[ecbno].dataptr = (long) cptr;
+	ecb[ecbno].dataptr = virt_to_bus(cptr);
 #ifdef DEBUG
 	printk("cptr %x: ",cptr);
 	ptr = (unsigned char *) cptr;
@@ -351,15 +351,15 @@
     {
 	SCpnt->host_scribble = NULL;
 	ecb[ecbno].datalen = bufflen;
-	ecb[ecbno].dataptr = (long) buff;
+	ecb[ecbno].dataptr = virt_to_bus(buff);
     }
     ecb[ecbno].lun = SCpnt->lun;
     ecb[ecbno].ses = 1;	/* Suppress underrun errors */
     ecb[ecbno].dir= direction;
     ecb[ecbno].ars=1;  /* Yes, get the sense on an error */
     ecb[ecbno].senselen = 12;
-    ecb[ecbno].senseptr = (long) ecb[ecbno].sense;
-    ecb[ecbno].statusptr = (long) ecb[ecbno].status;
+    ecb[ecbno].senseptr = virt_to_bus(ecb[ecbno].sense);
+    ecb[ecbno].statusptr = virt_to_bus(ecb[ecbno].status);
     ecb[ecbno].done = done;
     ecb[ecbno].SCpnt = SCpnt;
 #ifdef DEBUG
diff -u --recursive --new-file v2.1.6/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.1.6/linux/drivers/scsi/hosts.c	Tue Oct 29 19:58:13 1996
+++ linux/drivers/scsi/hosts.c	Thu Oct 31 09:16:01 1996
@@ -224,12 +224,6 @@
 #ifdef CONFIG_SCSI_ADVANSYS
 	ADVANSYS,
 #endif
-#ifdef CONFIG_SCSI_EATA_DMA
-    EATA_DMA,
-#endif
-#ifdef CONFIG_SCSI_EATA_PIO
-    EATA_PIO,
-#endif
 /* BusLogic must come before aha1542.c */
 #ifdef CONFIG_SCSI_BUSLOGIC
     BUSLOGIC,
@@ -287,6 +281,12 @@
 #endif
 #ifdef CONFIG_SCSI_NCR53C8XX
     NCR53C8XX,
+#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+    EATA_DMA,
+#endif
+#ifdef CONFIG_SCSI_EATA_PIO
+    EATA_PIO,
 #endif
 #ifdef CONFIG_SCSI_7000FASST
     WD7000,
diff -u --recursive --new-file v2.1.6/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.6/linux/drivers/scsi/scsi.c	Tue Oct 29 19:58:13 1996
+++ linux/drivers/scsi/scsi.c	Wed Oct 30 03:11:51 1996
@@ -1799,7 +1799,8 @@
 	if ((++SCpnt->retries) < SCpnt->allowed)
 	{
 	    if ((SCpnt->retries >= (SCpnt->allowed >> 1))
-		&& !(jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
+		&& !(SCpnt->host->last_reset > 0 &&
+		     jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
 		&& !(SCpnt->flags & WAS_RESET))
 	    {
 		printk("scsi%d channel %d : resetting for second half of retries.\n",
diff -u --recursive --new-file v2.1.6/linux/fs/ioctl.c linux/fs/ioctl.c
--- v2.1.6/linux/fs/ioctl.c	Tue Oct 29 19:58:42 1996
+++ linux/fs/ioctl.c	Wed Oct 30 18:41:44 1996
@@ -25,27 +25,18 @@
 				return -EBADF;
 		    	if (filp->f_inode->i_op->bmap == NULL)
 				return -EINVAL;
-			error = verify_area(VERIFY_WRITE,(void *) arg,4);
-			if (error)
+			if ((error = get_user(block, (int *) arg)) != 0)
 				return error;
-			get_user(block, (int *) arg);
 			block = filp->f_inode->i_op->bmap(filp->f_inode,block);
-			put_user(block,(int *) arg);
-			return 0;
+			return put_user(block, (int *) arg);
 		case FIGETBSZ:
 			if (filp->f_inode->i_sb == NULL)
 				return -EBADF;
-			error = verify_area(VERIFY_WRITE,(void *) arg,4);
-			if (error)
-				return error;
-			put_user(filp->f_inode->i_sb->s_blocksize, (int *) arg);
-			return 0;
+			return put_user(filp->f_inode->i_sb->s_blocksize,
+					(int *) arg);
 		case FIONREAD:
-			error = verify_area(VERIFY_WRITE,(void *) arg,sizeof(int));
-			if (error)
-				return error;
-			put_user(filp->f_inode->i_size - filp->f_pos, (int *) arg);
-			return 0;
+			return put_user(filp->f_inode->i_size - filp->f_pos,
+					(int *) arg);
 	}
 	if (filp->f_op && filp->f_op->ioctl)
 		return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
@@ -56,7 +47,7 @@
 asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {	
 	struct file * filp;
-	int on;
+	int on, error;
 
 	if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
 		return -EBADF;
@@ -70,11 +61,8 @@
 			return 0;
 
 		case FIONBIO:
-			on = verify_area(VERIFY_READ, (unsigned int *)arg,
-				sizeof(unsigned int));
-			if(on)	
-				return on;
-			get_user(on, (unsigned int *) arg);
+			if ((error = get_user(on, (int *)arg)) != 0)
+				return error;
 			if (on)
 				filp->f_flags |= O_NONBLOCK;
 			else
@@ -83,11 +71,8 @@
 
 		case FIOASYNC: /* O_SYNC is not yet implemented,
 				  but it's here for completeness. */
-			on = verify_area(VERIFY_READ, (unsigned int *)arg,
-				sizeof(unsigned int));
-			if(on)	
-				return on;
-			get_user(on, (unsigned int *) arg);
+			if ((error = get_user(on, (int *)arg)) != 0)
+				return error;
 			if (on)
 				filp->f_flags |= O_SYNC;
 			else
diff -u --recursive --new-file v2.1.6/linux/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h
--- v2.1.6/linux/fs/ncpfs/ncplib_kernel.h	Sat Mar  2 13:15:35 1996
+++ linux/fs/ncpfs/ncplib_kernel.h	Wed Oct 30 01:24:35 1996
@@ -17,7 +17,7 @@
 #include <linux/malloc.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
 #include <asm/string.h>
 
 #include <linux/ncp.h>
diff -u --recursive --new-file v2.1.6/linux/fs/super.c linux/fs/super.c
--- v2.1.6/linux/fs/super.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/super.c	Wed Oct 30 01:58:50 1996
@@ -146,6 +146,8 @@
 		if (vfsmnttail->mnt_dev == dev)
 			vfsmnttail = lptr;
 	}
+	if (tofree == mru_vfsmnt)
+		mru_vfsmnt = NULL;
 	kfree(tofree->mnt_devname);
 	kfree(tofree->mnt_dirname);
 	kfree_s(tofree, sizeof(struct vfsmount));
diff -u --recursive --new-file v2.1.6/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- v2.1.6/linux/include/asm-alpha/processor.h	Wed Oct 16 10:48:29 1996
+++ linux/include/asm-alpha/processor.h	Thu Oct 31 13:59:59 1996
@@ -20,15 +20,6 @@
 #define MCA_bus 0
 #define MCA_bus__is_a_macro /* for versions in ksyms.c */
 
-/*
- * The VM exception save area. We need to save only the
- * exception count, so that the exception handling can know
- * whether the system is set up to handle exceptions..
- */
-struct exception_struct {
-	unsigned long count;
-};
-
 struct thread_struct {
 	/* the fields below are used by PALcode and must match struct pcb: */
 	unsigned long ksp;
@@ -45,12 +36,10 @@
 	unsigned long res1, res2;
 
 	/* the fields below are Linux-specific: */
-	/*
-	 * bit 0:    perform syscall argument validation (get/set_fs)
-	 * bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h)
-	 */
+	/* bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h) */
 	unsigned long flags;
-	struct exception_struct ex;
+	/* perform syscall argument validation (get/set_fs) */
+	unsigned long fs;
 };
 
 #define INIT_MMAP { &init_mm, 0xfffffc0000000000,  0xfffffc0010000000, \
@@ -61,7 +50,7 @@
 	0, 0, 0, \
 	0, 0, 0, \
 	0, \
-	{ 0 } \
+	0 \
 }
 
 #define alloc_kernel_stack()    __get_free_page(GFP_KERNEL)
diff -u --recursive --new-file v2.1.6/linux/include/asm-alpha/uaccess.h linux/include/asm-alpha/uaccess.h
--- v2.1.6/linux/include/asm-alpha/uaccess.h	Tue Oct 29 19:58:47 1996
+++ linux/include/asm-alpha/uaccess.h	Thu Oct 31 13:59:59 1996
@@ -1,5 +1,9 @@
-#ifndef _ASM_SEGMENT_H
-#define _ASM_SEGMENT_H
+#ifndef __ALPHA_UACCESS_H
+#define __ALPHA_UACCESS_H
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+
 
 /*
  * The fs value determines whether argument validity checking should be
@@ -9,19 +13,15 @@
  * For historical reasons, these macros are grossly misnamed.
  */
 
-#define KERNEL_DS	0
-#define USER_DS		1
+#define KERNEL_DS	(0UL)
+#define USER_DS		(-0x40000000000UL)
 
 #define VERIFY_READ	0
 #define VERIFY_WRITE	1
 
-#define get_fs()  (current->tss.flags & 0x1)
-#define set_fs(x) (current->tss.flags = (current->tss.flags & ~0x1) | ((x) & 0x1))
-
-static inline unsigned long get_ds(void)
-{
-	return 0;
-}
+#define get_fs()  (current->tss.fs)
+#define set_fs(x) (current->tss.fs = (x))
+#define get_ds()  (KERNEL_DS)
 
 /*
  * Is a address valid? This does a straighforward calculation rather
@@ -34,12 +34,17 @@
  *  - OR we are in kernel mode.
  */
 #define __access_ok(addr,size,mask) \
-	(((mask)&((addr | size | (addr+size)) >> 42))==0)
-#define __access_mask (-(long)get_fs())
+	(((mask) & (addr | size | (addr+size))) == 0)
+#define __access_mask get_fs()
 
 #define access_ok(type,addr,size) \
 	__access_ok(((unsigned long)(addr)),(size),__access_mask)
 
+extern inline int verify_area(int type, const void * addr, unsigned long size)
+{
+	return access_ok(type,addr,size) ? 0 : -EFAULT;
+}
+
 /*
  * These are the main single-value transfer routines.  They automatically
  * use the right size if we just have the right pointer type.
@@ -52,56 +57,39 @@
  * (a) re-use the arguments for side effects (sizeof/typeof is ok)
  * (b) require any knowledge of processes at this stage
  */
-#define put_user(x,ptr)	__put_user_check((x),(ptr),sizeof(*(ptr)),__access_mask)
-#define get_user(x,ptr) __get_user_check((x),(ptr),sizeof(*(ptr)),__access_mask)
+#define put_user(x,ptr) \
+  __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)),__access_mask)
+#define get_user(x,ptr) \
+  __get_user_check((x),(ptr),sizeof(*(ptr)),__access_mask)
 
 /*
  * The "__xxx" versions do not do address space checking, useful when
- * doing multiple accesses to the same area (the user has to do the
+ * doing multiple accesses to the same area (the programmer has to do the
  * checks by hand with "access_ok()")
  */
-#define __put_user(x,ptr) __put_user_nocheck((x),(ptr),sizeof(*(ptr)))
-#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
-
-#define copy_to_user(to,from,n)   __copy_tofrom_user((to),(from),(n),__cu_to)
-#define copy_from_user(to,from,n) __copy_tofrom_user((to),(from),(n),__cu_from)
+#define __put_user(x,ptr) \
+  __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+#define __get_user(x,ptr) \
+  __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
 
 /*
- * Not pretty? What do you mean not "not pretty"?
+ * The "lda %1, 2b-1b(%0)" bits are magic to get the assembler to
+ * encode the bits we need for resolving the exception.  See the
+ * more extensive comments with fixup_inline_exception below for
+ * more information.
  */
-extern void __copy_user(void);
-
-#define __copy_tofrom_user(to,from,n,v)					    \
-({									    \
-	register void * __cu_to __asm__("$6") = (to);			    \
-	register const void * __cu_from __asm__("$7") = (from);		    \
-	register long __cu_len __asm__("$0") = (n);			    \
-	if (__access_ok(((long)(v)),__cu_len,__access_mask)) {		    \
-		register void * __cu_ex __asm__("$8");			    \
-		__cu_ex = &current->tss.ex;				    \
-		__asm__ __volatile__(					    \
-			"jsr $28,(%7),__copy_user"			    \
-			: "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to) \
-			: "0" (__cu_len), "1" (__cu_from), "2" (__cu_to),   \
-			  "r" (__cu_ex), "r" (__copy_user)		    \
-			: "$1","$2","$3","$4","$5","$28","memory");	    \
-	}								    \
-	__cu_len;							    \
-})
 
 extern void __get_user_unknown(void);
 
 #define __get_user_nocheck(x,ptr,size)				\
 ({								\
-	long __gu_err = -EFAULT, __gu_val = 0;			\
-	const __typeof__(*(ptr)) *__gu_addr = (ptr);		\
-	long __gu_ex_count = current->tss.ex.count;		\
+	long __gu_err = 0, __gu_val;				\
 	switch (size) {						\
-	case 1: __get_user_8; break;				\
-	case 2: __get_user_16; break;				\
-	case 4: __get_user_32; break;				\
-	case 8: __get_user_64; break;				\
-	default: __get_user_unknown(); break;			\
+	  case 1: __get_user_8(ptr); break;			\
+	  case 2: __get_user_16(ptr); break;			\
+	  case 4: __get_user_32(ptr); break;			\
+	  case 8: __get_user_64(ptr); break;			\
+	  default: __get_user_unknown(); break;			\
 	}							\
 	(x) = (__typeof__(*(ptr))) __gu_val;			\
 	__gu_err;						\
@@ -112,99 +100,110 @@
 	long __gu_err = -EFAULT, __gu_val = 0;			\
 	const __typeof__(*(ptr)) *__gu_addr = (ptr);		\
 	if (__access_ok((long)__gu_addr,size,mask)) {		\
-		long __gu_ex_count = current->tss.ex.count;	\
+		__gu_err = 0;					\
 		switch (size) {					\
-		case 1: __get_user_8; break;			\
-		case 2: __get_user_16; break;			\
-		case 4: __get_user_32; break;			\
-		case 8: __get_user_64; break;			\
-		default: __get_user_unknown(); break;		\
+		  case 1: __get_user_8(__gu_addr); break;	\
+		  case 2: __get_user_16(__gu_addr); break;	\
+		  case 4: __get_user_32(__gu_addr); break;	\
+		  case 8: __get_user_64(__gu_addr); break;	\
+		  default: __get_user_unknown(); break;		\
 		}						\
 	}							\
 	(x) = (__typeof__(*(ptr))) __gu_val;			\
 	__gu_err;						\
 })
 
-#define __get_user_64							   \
-	__asm__("/* Inline __get_user_64 */\n\t"			   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"br 2f\n"		/* exception! */		   \
-		"1:\t"							   \
-		"stq %5,%3\n\t"		/* store inc'ed exception count */ \
-		"ldq %1,%2\n\t"		/* actual data load */		   \
-		"stq %4,%3\n\t"		/* restore exception count */	   \
-		"clr %0\n"		/* no exception: error = 0 */	   \
-		"2:\t/* End __get_user_64 */"				   \
-		: "=r"(__gu_err), "=r"(__gu_val)			   \
-		: "m"(*__gu_addr), "m"(current->tss.ex.count),		   \
-		  "r"(__gu_ex_count), "r"(__gu_ex_count+1),		   \
-		  "0"(__gu_err), "1"(__gu_val)				   \
-		: "$28")
-
-#define __get_user_32							   \
-	__asm__("/* Inline __get_user_32 */\n\t"			   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"br 2f\n"		/* exception! */		   \
-		"1:\t"							   \
-		"stq %5,%3\n\t"		/* store inc'ed exception count */ \
-		"ldl %1,%2\n\t"		/* actual data load */		   \
-		"stq %4,%3\n\t"		/* restore exception count */	   \
-		"clr %0\n"		/* no exception: error = 0 */	   \
-		"2:\t/* End __get_user_32 */"				   \
-		: "=r"(__gu_err), "=r"(__gu_val)			   \
-		: "m"(*__gu_addr), "m"(current->tss.ex.count),		   \
-		  "r"(__gu_ex_count), "r"(__gu_ex_count+1),		   \
-		  "0"(__gu_err), "1"(__gu_val)				   \
-		: "$28")
-
-#define __get_user_16							   \
-	__asm__("/* Inline __get_user_16 */\n\t"			   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"br 2f\n"		/* exception! */		   \
-		"1:\t"							   \
-		"stq %6,%4\n\t"		/* store inc'ed exception count */ \
-		"ldq_u %1,%2\n\t"	/* actual data load */		   \
-		"stq %5,%4\n\t"		/* restore exception count */	   \
-		"clr %0\n\t"		/* no exception: error = 0 */	   \
-		"extwl %1,%3,%1\n"	/* extract the short */		   \
-		"2:\t/* End __get_user_16 */"				   \
-		: "=r"(__gu_err), "=r"(__gu_val)			   \
-		: "m"(*__gu_addr), "r"(__gu_addr),			   \
-		  "m"(current->tss.ex.count), "r"(__gu_ex_count),	   \
-		  "r"(__gu_ex_count+1), "0"(__gu_err), "1"(__gu_val)	   \
-		: "$28")
-
-#define __get_user_8							   \
-	__asm__("/* Inline __get_user_8 */\n\t"				   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"br 2f\n"		/* exception! */		   \
-		"1:\t"							   \
-		"stq %6,%4\n\t"		/* store inc'ed exception count */ \
-		"ldq_u %1,%2\n\t"	/* actual data load */		   \
-		"stq %5,%4\n\t"		/* restore exception count */	   \
-		"clr %0\n\t"		/* no exception: error = 0 */	   \
-		"extbl %1,%3,%1\n"	/* extract the byte */		   \
-		"2:\t/* End __get_user_8 */"				   \
-		: "=r"(__gu_err), "=r"(__gu_val)			   \
-		: "m"(*__gu_addr), "r"(__gu_addr),			   \
-		  "m"(current->tss.ex.count), "r"(__gu_ex_count),	   \
-		  "r"(__gu_ex_count+1), "0"(__gu_err), "1"(__gu_val)	   \
-		: "$28")
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) (*(struct __large_struct *)(x))
+
+#define __get_user_64(addr)				\
+	__asm__("1: ldq %0,%2\n"			\
+	"2:\n"						\
+	".section __ex_table,\"a\"\n"			\
+	"	.gprel32 1b\n"				\
+	"	lda %0, 2b-1b(%1)\n"			\
+	".text"						\
+		: "=r"(__gu_val), "=r"(__gu_err)	\
+		: "m"(__m(addr)), "1"(__gu_err))
+
+#define __get_user_32(addr)				\
+	__asm__("1: ldl %0,%2\n"			\
+	"2:\n"						\
+	".section __ex_table,\"a\"\n"			\
+	"	.gprel32 1b\n"				\
+	"	lda %0, 2b-1b(%1)\n"			\
+	".text"						\
+		: "=r"(__gu_val), "=r"(__gu_err)	\
+		: "m"(__m(addr)), "1"(__gu_err))
+
+#ifdef __HAVE_CPU_BWX
+/* Those lucky bastards with ev56 and later cpus can do byte/word moves.  */
+
+#define __get_user_16(addr)				\
+	__asm__("1: ldwu %0,%2\n"			\
+	"2:\n"						\
+	".section __ex_table,\"a\"\n"			\
+	"	.gprel32 1b\n"				\
+	"	lda %0, 2b-1b(%1)\n"			\
+	".text"						\
+		: "=r"(__gu_val), "=r"(__gu_err)	\
+		: "m"(__m(addr)), "1"(__gu_err))
+
+#define __get_user_8(addr)				\
+	__asm__("1: ldbu %0,%2\n"			\
+	"2:\n"						\
+	".section __ex_table,\"a\"\n"			\
+	"	.gprel32 1b\n"				\
+	"	lda %0, 2b-1b(%1)\n"			\
+	".text"						\
+		: "=r"(__gu_val), "=r"(__gu_err)	\
+		: "m"(__m(addr)), "1"(__gu_err))
+#else
+/* Unfortunately, we can't get an unaligned access trap for the sub-word
+   load, so we have to do a general unaligned operation.  */
+
+#define __get_user_16(addr)						\
+{									\
+	long __gu_tmp;							\
+	__asm__("1: ldq_u %0,0(%3)\n"					\
+	"2:	ldq_u %1,1(%3)\n"					\
+	"	extwl %0,%3,%0\n"					\
+	"	extwh %1,%3,%1\n"					\
+	"	or %0,%1,%0\n"						\
+	"3:\n"								\
+	".section __ex_table,\"a\"\n"					\
+	"	.gprel32 1b\n"						\
+	"	lda %0, 3b-1b(%2)\n"					\
+	"	.gprel32 2b\n"						\
+	"	lda %0, 2b-1b(%2)\n"					\
+	".text"								\
+		: "=&r"(__gu_val), "=&r"(__gu_tmp), "=r"(__gu_err)	\
+		: "r"(addr), "2"(__gu_err));				\
+}
+
+#define __get_user_8(addr)						\
+	__asm__("1: ldq_u %0,0(%2)\n"					\
+	"	extbl %0,%2,%0\n"					\
+	"2:\n"								\
+	".section __ex_table,\"a\"\n"					\
+	"	.gprel32 1b\n"						\
+	"	lda %0, 2b-1b(%1)\n"					\
+	".text"								\
+		: "=&r"(__gu_val), "=r"(__gu_err)			\
+		: "r"(addr), "1"(__gu_err))
+#endif
 
 extern void __put_user_unknown(void);
 
 #define __put_user_nocheck(x,ptr,size)				\
 ({								\
-	long __pu_err = -EFAULT;				\
-	__typeof__(*(ptr)) *__pu_addr = (ptr);			\
-        __typeof__(*(ptr)) __pu_val = (x);			\
-	long __pu_ex_count = current->tss.ex.count;		\
+	long __pu_err = 0;					\
 	switch (size) {						\
-	case 1: __put_user_8; break;				\
-	case 2: __put_user_16; break;				\
-	case 4: __put_user_32; break;				\
-	case 8: __put_user_64; break;				\
-	default: __put_user_unknown(); break;			\
+	  case 1: __put_user_8(x,ptr); break;			\
+	  case 2: __put_user_16(x,ptr); break;			\
+	  case 4: __put_user_32(x,ptr); break;			\
+	  case 8: __put_user_64(x,ptr); break;			\
+	  default: __put_user_unknown(); break;			\
 	}							\
 	__pu_err;						\
 })
@@ -213,95 +212,149 @@
 ({								\
 	long __pu_err = -EFAULT;				\
 	__typeof__(*(ptr)) *__pu_addr = (ptr);			\
-        __typeof__(*(ptr)) __pu_val = (x);			\
 	if (__access_ok((long)__pu_addr,size,mask)) {		\
-		long __pu_ex_count = current->tss.ex.count;	\
+		__pu_err = 0;					\
 		switch (size) {					\
-		case 1: __put_user_8; break;			\
-		case 2: __put_user_16; break;			\
-		case 4: __put_user_32; break;			\
-		case 8: __put_user_64; break;			\
-		default: __put_user_unknown(); break;		\
+		  case 1: __put_user_8(x,__pu_addr); break;	\
+		  case 2: __put_user_16(x,__pu_addr); break;	\
+		  case 4: __put_user_32(x,__pu_addr); break;	\
+		  case 8: __put_user_64(x,__pu_addr); break;	\
+		  default: __put_user_unknown(); break;		\
 		}						\
 	}							\
 	__pu_err;						\
 })
 
-#define __put_user_64							   \
-	__asm__("/* Inline __put_user_64 */\n\t"			   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"br 2f\n"		/* exception! */		   \
-		"1:\t"							   \
-		"stq %5,%3\n\t"		/* store inc'ed exception count */ \
-		"stq %2,%1\n\t"		/* actual data store */		   \
-		"stq %4,%3\n\t"		/* restore exception count */	   \
-		"clr %0\n"		/* no exception: error = 0 */	   \
-		"2:\t/* End __put_user_64 */"				   \
-		: "=r"(__pu_err), "=m"(*__pu_addr)			   \
-		: "r"(__pu_val), "m"(current->tss.ex.count),		   \
-		  "r"(__pu_ex_count), "r"(__pu_ex_count+1),		   \
-		  "0"(__pu_err)						   \
-		: "$28")
-
-#define __put_user_32							   \
-	__asm__("/* Inline __put_user_32 */\n\t"			   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"br 2f\n"		/* exception! */		   \
-		"1:\t"							   \
-		"stq %5,%3\n\t"		/* store inc'ed exception count */ \
-		"stl %2,%1\n\t"		/* actual data store */		   \
-		"stq %4,%3\n\t"		/* restore exception count */	   \
-		"clr %0\n"		/* no exception: error = 0 */	   \
-		"2:\t/* End __put_user_32 */"				   \
-		: "=r"(__pu_err), "=m"(*__pu_addr)			   \
-		: "r"(__pu_val), "m"(current->tss.ex.count),		   \
-		  "r"(__pu_ex_count), "r"(__pu_ex_count+1),		   \
-		  "0"(__pu_err)						   \
-		: "$28")
-
-#define __put_user_16							   \
-	__asm__("/* Inline __put_user_16 */\n\t"			   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"lda %0,%7\n\t"		/* exception! error = -EFAULT */   \
-		"br 2f\n"						   \
-		"1:\t"							   \
-		"stq %6,%4\n\t"		/* store inc'ed exception count */ \
-		"ldq_u %0,%1\n\t"	/* masked data store */		   \
-		"inswl %2,%3,%2\n\t"					   \
-		"mskwl %0,%3,%0\n\t"					   \
-		"or %0,%2,%2\n\t"					   \
-		"stq_u %2,%1\n\t"					   \
-		"stq %5,%4\n\t"		/* restore exception count */	   \
-		"clr %0\n"		/* no exception: error = 0 */	   \
-		"2:\t/* End __put_user_16 */"				   \
-		: "=r"(__pu_err), "=m"(*__pu_addr), "=r"(__pu_val)	   \
-		: "r"(__pu_addr), "m"(current->tss.ex.count),		   \
-		  "r"(__pu_ex_count), "r"(__pu_ex_count+1), "i"(-EFAULT),  \
-		  "2"(__pu_val)						   \
-		: "$28")
-
-#define __put_user_8							   \
-	__asm__("/* Inline __put_user_8 */\n\t"				   \
-		"br $28,1f\n\t"		/* set up exception address */	   \
-		"lda %0,%7\n\t"		/* exception! error = -EFAULT */   \
-		"br 2f\n"						   \
-		"1:\t"							   \
-		"stq %6,%4\n\t"		/* store inc'ed exception count */ \
-		"ldq_u %0,%1\n\t"	/* masked data store */		   \
-		"insbl %2,%3,%2\n\t"					   \
-		"mskbl %0,%3,%0\n\t"					   \
-		"or %0,%2,%2\n\t"					   \
-		"stq_u %2,%1\n\t"					   \
-		"stq %5,%4\n\t"		/* restore exception count */	   \
-		"clr %0\n"		/* no exception: error = 0 */	   \
-		"2:\t/* End __put_user_8 */"				   \
-		: "=r"(__pu_err), "=m"(*__pu_addr), "=r"(__pu_val)	   \
-		: "r"(__pu_addr), "m"(current->tss.ex.count),		   \
-		  "r"(__pu_ex_count), "r"(__pu_ex_count+1), "i"(-EFAULT),  \
-		  "2"(__pu_val)						   \
-		: "$28")
+/*
+ * The "__put_user_xx()" macros tell gcc they read from memory
+ * instead of writing: this is because they do not write to
+ * any memory gcc knows about, so there are no aliasing issues
+ */
+#define __put_user_64(x,addr)					\
+__asm__ __volatile__("1: stq %r2,%1\n"				\
+	"2:\n"							\
+	".section __ex_table,\"a\"\n"				\
+	"	.gprel32 1b\n"					\
+	"	lda $31,2b-1b(%0)\n"				\
+	".text"							\
+		: "=r"(__pu_err)				\
+		: "m" (__m(addr)), "rJ" (x), "0"(__pu_err))
+
+#define __put_user_32(x,addr)					\
+__asm__ __volatile__("1: stl %r2,%1\n"				\
+	"2:\n"							\
+	".section __ex_table,\"a\"\n"				\
+	"	.gprel32 1b\n"					\
+	"	lda $31,2b-1b(%0)\n"				\
+	".text"							\
+		: "=r"(__pu_err)				\
+		: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
+
+#ifdef __HAVE_CPU_BWX
+/* Those lucky bastards with ev56 and later cpus can do byte/word moves.  */
+
+#define __put_user_16(x,addr)					\
+__asm__ __volatile__("1: stw %r2,%1\n"				\
+	"2:\n"							\
+	".section __ex_table,\"a\"\n"				\
+	"	.gprel32 1b\n"					\
+	"	lda $31,2b-1b(%0)\n"				\
+	".text"							\
+		: "=r"(__pu_err)				\
+		: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
+
+#define __put_user_8(x,addr)					\
+__asm__ __volatile__("1: stb %r2,%1\n"				\
+	"2:\n"							\
+	".section __ex_table,\"a\"\n"				\
+	"	.gprel32 1b\n"					\
+	"	lda $31,2b-1b(%0)\n"				\
+	".text"							\
+		: "=r"(__pu_err)				\
+		: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
+#else
+/* Unfortunately, we can't get an unaligned access trap for the sub-word
+   write, so we have to do a general unaligned operation.  */
+
+#define __put_user_16(x,addr)					\
+{								\
+	long __pu_tmp1, __pu_tmp2, __pu_tmp3, __pu_tmp4;	\
+	__asm__ __volatile__(					\
+	"1:	ldq_u %2,1(%5)\n"				\
+	"2:	ldq_u %1,0(%5)\n"				\
+	"	inswh %6,%5,%4\n"				\
+	"	inswl %6,%5,%3\n"				\
+	"	mskwh %2,%5,%2\n"				\
+	"	mskwl %1,%5,%1\n"				\
+	"	or %2,%4,%2\n"					\
+	"	or %1,%3,%1\n"					\
+	"3:	stq_u %2,1(%5)\n"				\
+	"4:	stq_u %1,0(%5)\n"				\
+	"5:\n"							\
+	".section __ex_table,\"a\"\n"				\
+	"	.gprel32 1b\n"					\
+	"	lda $31, 5b-1b(%0)\n"				\
+	"	.gprel32 2b\n"					\
+	"	lda $31, 5b-2b(%0)\n"				\
+	"	.gprel32 3b\n"					\
+	"	lda $31, 5b-3b(%0)\n"				\
+	"	.gprel32 4b\n"					\
+	"	lda $31, 5b-4b(%0)\n"				\
+	".text"							\
+		: "=r"(__pu_err), "=&r"(__pu_tmp1),		\
+		  "=&r"(__pu_tmp2), "=&r"(__pu_tmp3),		\
+		  "=&r"(__pu_tmp4)				\
+		: "r"(addr), "r"((unsigned long)(x)), "0"(__pu_err)); \
+}
+
+#define __put_user_8(x,addr)					\
+{								\
+	long __pu_tmp1, __pu_tmp2;				\
+	__asm__ __volatile__(					\
+	"1:	ldq_u %1,0(%4)\n"				\
+	"	insbl %3,%4,%2\n"				\
+	"	mskbl %1,%4,%1\n"				\
+	"	or %1,%2,%1\n"					\
+	"2:	stq_u %1,0(%4)\n"				\
+	"3:\n"							\
+	".section __ex_table,\"a\"\n"				\
+	"	.gprel32 1b\n"					\
+	"	lda $31, 3b-1b(%0)\n"				\
+	"	.gprel32 2b\n"					\
+	"	lda $31, 3b-2b(%0)\n"				\
+	".text"							\
+		: "=r"(__pu_err),				\
+	  	  "=&r"(__pu_tmp1), "=&r"(__pu_tmp2)		\
+		: "r"((unsigned long)(x)), "r"(addr), "0"(__pu_err)); \
+}
+#endif
 
 
+/*
+ * Complex access routines
+ */
+
+#define copy_to_user(to,from,n)   __copy_tofrom_user((to),(from),(n),__cu_to)
+#define copy_from_user(to,from,n) __copy_tofrom_user((to),(from),(n),__cu_from)
+
+extern void __copy_user(void);
+
+#define __copy_tofrom_user(to,from,n,v)					    \
+({									    \
+	register void * __cu_to __asm__("$6") = (to);			    \
+	register const void * __cu_from __asm__("$7") = (from);		    \
+	register long __cu_len __asm__("$0") = (n);			    \
+	if (__access_ok(((long)(v)),__cu_len,__access_mask)) {		    \
+		__asm__ __volatile__(					    \
+			"jsr $28,(%3),__copy_user"			    \
+			: "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to) \
+			: "r" (__copy_user), "0" (__cu_len),		    \
+			  "1" (__cu_from), "2" (__cu_to)   		    \
+			: "$1","$2","$3","$4","$5","$28","memory");	    \
+	}								    \
+	__cu_len;							    \
+})
+
 extern void __clear_user(void);
 
 #define clear_user(to,n)						\
@@ -309,13 +362,10 @@
 	register void * __cl_to __asm__("$6") = (to);			\
 	register long __cl_len __asm__("$0") = (n);			\
 	if (__access_ok(((long)__cl_to),__cl_len,__access_mask)) {	\
-		register void * __cl_ex __asm__("$7");			\
-		__cl_ex = &current->tss.ex;				\
 		__asm__ __volatile__(					\
 			"jsr $28,(%2),__clear_user"			\
 			: "=r"(__cl_len), "=r"(__cl_to)			\
-			: "r"(__clear_user), "r"(__cl_ex),		\
-			  "0"(__cl_len), "1"(__cl_to)			\
+			: "r"(__clear_user), "0"(__cl_len), "1"(__cl_to)\
 			: "$1","$2","$3","$4","$5","$28","memory");	\
 	}								\
 	__cl_len;							\
@@ -325,24 +375,66 @@
 /* Returns: -EFAULT if exception before terminator, N if the entire
    buffer filled, else strlen.  */
 
-struct exception_struct;
-extern long __strncpy_from_user(char *__to, const char *__from,
-				long __to_len, struct exception_struct *);
+extern long __strncpy_from_user(char *__to, const char *__from, long __to_len);
 
 #define strncpy_from_user(to,from,n)					      \
 ({									      \
 	char * __sfu_to = (to);						      \
 	const char * __sfu_from = (from);				      \
 	long __sfu_len = (n), __sfu_ret = -EFAULT;			      \
-	if (__access_ok(((long)__sfu_from),__sfu_len,__access_mask)) {	      \
-		__sfu_ret = __strncpy_from_user(__sfu_to,__sfu_from,	      \
-						__sfu_len, &current->tss.ex); \
+	if (__access_ok(((long)__sfu_from),__sfu_len,__access_mask))	      \
+		__sfu_ret=__strncpy_from_user(__sfu_to,__sfu_from,__sfu_len); \
 	__sfu_ret;							      \
 })
 
-extern inline int verify_area(int type, const void * addr, unsigned long size)
+
+/*
+ * About the exception table:
+ *
+ * - insn is a 32-bit offset off of the kernel's or module's gp.
+ * - nextinsn is a 16-bit offset off of the faulting instruction
+ *   (not off of the *next* instruction as branches are).
+ * - errreg is the register in which to place -EFAULT.
+ * - valreg is the final target register for the load sequence
+ *   and will be zeroed.
+ *
+ * Either errreg or valreg may be $31, in which case nothing happens.
+ *
+ * The exception fixup information "just so happens" to be arranged
+ * as in a MEM format instruction.  This lets us emit our three
+ * values like so:
+ *
+ *      lda valreg, nextinsn(errreg)
+ *
+ */
+
+struct exception_table_entry
 {
-	return access_ok(type,addr,size)?0:-EFAULT;
-}
+	signed int insn;
+	union exception_fixup {
+		unsigned unit;
+		struct {
+			signed int nextinsn : 16;
+			unsigned int errreg : 5;
+			unsigned int valreg : 5;
+		} bits;
+	} fixup;
+};
+
+/* Returns 0 if exception not found and fixup.unit otherwise.  */
+extern unsigned search_exception_table(unsigned long);
+
+/* Returns the new pc */
+#define fixup_exception(map_reg, fixup_unit, pc)		\
+({								\
+	union exception_fixup __fie_fixup;			\
+	__fie_fixup.unit = fixup_unit;				\
+	if (__fie_fixup.bits.valreg != 31)			\
+		map_reg(__fie_fixup.bits.valreg) = 0;		\
+	if (__fie_fixup.bits.errreg != 31)			\
+		map_reg(__fie_fixup.bits.errreg) = -EFAULT;	\
+	(pc) + __fie_fixup.bits.nextinsn;			\
+})
+
 
-#endif /* _ASM_SEGMENT_H */
+#endif /* __ALPHA_UACCESS_H */
diff -u --recursive --new-file v2.1.6/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.6/linux/include/asm-i386/processor.h	Wed Oct 16 10:48:29 1996
+++ linux/include/asm-i386/processor.h	Fri Nov  1 11:56:43 1996
@@ -41,16 +41,6 @@
 #define TASK_SIZE	(0xC0000000UL)
 
 /*
- * VM exception register save area..
- *
- * When no exceptions are active, count = -1.
- */
-struct exception_struct {
-	unsigned long count;
-	unsigned long eip;
-};
-
-/*
  * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
  */
 #define IO_BITMAP_SIZE	32
@@ -120,7 +110,6 @@
 	struct vm86_struct * vm86_info;
 	unsigned long screen_bitmap;
 	unsigned long v86flags, v86mask, v86mode;
-	struct exception_struct ex;
 };
 
 #define INIT_MMAP { &init_mm, 0xC0000000, 0xFFFFF000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
@@ -139,7 +128,6 @@
 	_TSS(0), 0, 0, 0, KERNEL_DS, \
 	{ { 0, }, },  /* 387 state */ \
 	NULL, 0, 0, 0, 0 /* vm86_info */, \
-	{ -1, } \
 }
 
 #define alloc_kernel_stack()    __get_free_page(GFP_KERNEL)
diff -u --recursive --new-file v2.1.6/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.1.6/linux/include/asm-i386/uaccess.h	Tue Oct 29 19:58:47 1996
+++ linux/include/asm-i386/uaccess.h	Fri Nov  1 15:25:03 1996
@@ -5,7 +5,6 @@
  * User space memory access functions
  */
 #include <linux/sched.h>
-
 #include <asm/segment.h>
 
 #define VERIFY_READ 0
@@ -15,37 +14,65 @@
  * The fs value determines whether argument validity checking should be
  * performed or not.  If get_fs() == USER_DS, checking is performed, with
  * get_fs() == KERNEL_DS, checking is bypassed.
- * 
+ *
  * For historical reasons, these macros are grossly misnamed.
  */
-      
+
 #define get_fs()	(current->tss.segment)
 #define set_fs(x)	(current->tss.segment = (x))
 #define get_ds()	(KERNEL_DS)
 
 #define __user_ok(addr,size) \
-((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size))
+	((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size))
 #define __kernel_ok \
-(get_fs() == KERNEL_DS)
+	(get_fs() == KERNEL_DS)
 
 extern int __verify_write(const void *, unsigned long);
 
 #if CPU > 386
 #define __access_ok(type,addr,size) \
-(__kernel_ok || __user_ok(addr,size))
+	(__kernel_ok || __user_ok(addr,size))
 #else
 #define __access_ok(type,addr,size) \
-(__kernel_ok || (__user_ok(addr,size) && \
-  ((type) == VERIFY_READ || wp_works_ok || __verify_write((void *)(addr),(size)))))
+	(__kernel_ok || (__user_ok(addr,size) && \
+			 ((type) == VERIFY_READ || wp_works_ok || \
+			  __verify_write((void *)(addr),(size)))))
 #endif /* CPU */
 
 #define access_ok(type,addr,size) \
-__access_ok((type),(unsigned long)(addr),(size))
+	__access_ok((type),(unsigned long)(addr),(size))
+
+extern inline int verify_area(int type, const void * addr, unsigned long size)
+{
+	return access_ok(type,addr,size) ? 0 : -EFAULT;
+}
+
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue.  No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+
+struct exception_table_entry
+{
+	unsigned long insn, fixup;
+};
+
+/* Returns 0 if exception not found and fixup otherwise.  */
+extern unsigned long search_exception_table(unsigned long);
+
 
 /*
- * Uh, these should become the main single-value transfer routines..
- * They automatically use the right size if we just have the right
- * pointer type..
+ * These are the main single-value transfer routines.  They automatically
+ * use the right size if we just have the right pointer type.
  *
  * This gets kind of ugly. We want to return _two_ values in "get_user()"
  * and yet we don't want to do any pointers, because that is too much
@@ -57,280 +84,328 @@
  * with a separate "access_ok()" call (this is used when we do multiple
  * accesses to the same area of user memory).
  */
+#define get_user(x,ptr) \
+  __get_user_check((x),(ptr),sizeof(*(ptr)))
 #define put_user(x,ptr) \
-__do_put_user((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr))))
-#define __put_user(x,ptr) \
-__do_put_user_nocheck((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr))))
+  __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
 
-struct __large_struct { unsigned long buf[100]; };
-#define __m(x) (*(struct __large_struct *)(x))
+#define __get_user(x,ptr) \
+  __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
+#define __put_user(x,ptr) \
+  __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
 
-#define __put_user_asm(x,addr,ret,bwl,reg,rtype) \
-__asm__ __volatile__( \
-	"movl $1f,%0\n\t" \
-	"incl %3\n\t" \
-	"mov" #bwl " %" reg "1,%2\n\t" \
-	"xorl %0,%0\n\t" \
-	"decl %3\n1:" \
-:"=&d" (ret) \
-:#rtype (x), "m" (__m(addr)),"m" (current->tss.ex.count))
-
-extern int __put_user_bad(void);
-
-#define __put_user_size(x,ptr,size,retval) \
-switch (size) { \
-case 1: __put_user_asm(x,ptr,retval,b,"b","iq"); break; \
-case 2: __put_user_asm(x,ptr,retval,w,"w","ir"); break; \
-case 4: __put_user_asm(x,ptr,retval,l,"","ir"); break; \
-default: retval = __put_user_bad(); }
 
-static inline int __do_put_user(unsigned long x, void * ptr, int size)
-{
-	int retval = -EFAULT;
-	if (access_ok(VERIFY_WRITE, ptr, size))
-		__put_user_size(x,ptr,size,retval);
-	return retval;
-}
+extern long __put_user_bad(void);
 
-#define __do_put_user_nocheck(x, ptr, size) \
-({ int retval; __put_user_size(x,ptr,size,retval); retval; })
+#define __put_user_nocheck(x,ptr,size)			\
+({							\
+	long __pu_err;					\
+	__put_user_size((x),(ptr),(size),__pu_err);	\
+	__pu_err;					\
+})
+
+#define __put_user_check(x,ptr,size)				\
+({								\
+	long __pu_err = -EFAULT;				\
+	__typeof__(*(ptr)) *__pu_addr = (ptr);			\
+	if (access_ok(VERIFY_WRITE,__pu_addr,size))		\
+		__put_user_size((x),__pu_addr,(size),__pu_err);	\
+	__pu_err;						\
+})
+
+#define __put_user_size(x,ptr,size,retval)				\
+do {									\
+	retval = 0;							\
+	switch (size) {							\
+	  case 1: __put_user_asm(x,ptr,retval,"b","b","iq"); break;	\
+	  case 2: __put_user_asm(x,ptr,retval,"w","w","ir"); break;	\
+	  case 4: __put_user_asm(x,ptr,retval,"l","","ir"); break;	\
+	  default: __put_user_bad();					\
+	}								\
+} while (0)
 
-#define get_user(x,ptr) \
-__do_get_user((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) (*(struct __large_struct *)(x))
 
-#define __get_user(x,ptr) \
-__do_get_user_nocheck((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
+/*
+ * Tell gcc we read from memory instead of writing: this is because
+ * we do not write to any memory gcc knows about, so there are no
+ * aliasing issues.
+ */
+#define __put_user_asm(x, addr, err, itype, rtype, ltype)	\
+	__asm__ __volatile__(					\
+		"1:	mov"itype" %"rtype"1,%2\n"		\
+		"2:\n"						\
+		".section .fixup,\"ax\"\n"			\
+		"3:	movl %3,%0\n"				\
+		"	jmp 2b\n"				\
+		".section __ex_table,\"a\"\n"			\
+		"	.align 4\n"				\
+		"	.long 1b,3b\n"				\
+		".text"						\
+		: "=r"(err)					\
+		: ltype (x), "m"(__m(addr)), "i"(-EFAULT), "0"(err))
+
+
+#define __get_user_nocheck(x,ptr,size)				\
+({								\
+	long __gu_err, __gu_val;				\
+	__get_user_size(__gu_val,(ptr),(size),__gu_err);	\
+	(x) = (__typeof__(*(ptr)))__gu_val;			\
+	__gu_err;						\
+})
+
+#define __get_user_check(x,ptr,size)					\
+({									\
+	long __gu_err = -EFAULT, __gu_val = 0;				\
+	const __typeof__(*(ptr)) *__gu_addr = (ptr);			\
+	if (access_ok(VERIFY_READ,__gu_addr,size))			\
+		__get_user_size(__gu_val,__gu_addr,(size),__gu_err);	\
+	(x) = (__typeof__(*(ptr)))__gu_val;				\
+	__gu_err;							\
+})
+
+extern long __get_user_bad(void);
+
+#define __get_user_size(x,ptr,size,retval)				\
+do {									\
+	retval = 0;							\
+	switch (size) {							\
+	  case 1: __get_user_asm(x,ptr,retval,"b","b","=q"); break;	\
+	  case 2: __get_user_asm(x,ptr,retval,"w","w","=r"); break;	\
+	  case 4: __get_user_asm(x,ptr,retval,"l","","=r"); break;	\
+	  default: (x) = __get_user_bad();				\
+	}								\
+} while (0)
+
+#define __get_user_asm(x, addr, err, itype, rtype, ltype)	\
+	__asm__ __volatile__(					\
+		"1:	mov"itype" %2,%"rtype"1\n"		\
+		"2:\n"						\
+		".section .fixup,\"ax\"\n"			\
+		"3:	movl %3,%0\n"				\
+		"	xor"itype" %"rtype"1,%"rtype"1\n"	\
+		"	jmp 2b\n"				\
+		".section __ex_table,\"a\"\n"			\
+		"	.align 4\n"				\
+		"	.long 1b,3b\n"				\
+		".text"						\
+		: "=r"(err), ltype (x)				\
+		: "m"(__m(addr)), "i"(-EFAULT), "0"(err))
 
-#define __do_get_user(x,ptr,size,type) ({ \
-unsigned long __gu_addr = ptr; \
-int __gu_ret = -EFAULT; \
-unsigned long __gu_val = 0; \
-if (access_ok(VERIFY_READ,__gu_addr,size)) { \
-switch (size) { \
-case 1: __do_get_user_8(__gu_val,__gu_addr,__gu_ret); break; \
-case 2: __do_get_user_16(__gu_val,__gu_addr,__gu_ret); break; \
-case 4: __do_get_user_32(__gu_val,__gu_addr,__gu_ret); break; \
-default: __gu_ret = __do_get_user_bad(); break; \
-} } x = (type) __gu_val; __gu_ret; })
-
-#define __do_get_user_nocheck(x,ptr,size,type) ({ \
-int __gu_ret; \
-unsigned long __gu_val; \
-switch (size) { \
-case 1: __do_get_user_8(__gu_val,ptr,__gu_ret); break; \
-case 2: __do_get_user_16(__gu_val,ptr,__gu_ret); break; \
-case 4: __do_get_user_32(__gu_val,ptr,__gu_ret); break; \
-default: __gu_ret = __do_get_user_bad(); __gu_val = 0; break; \
-} x = (type) __gu_val; __gu_ret; })
-
-#define __do_get_user_asm(x,addr,ret,bwl,reg,rtype) \
-__asm__ __volatile__( \
-	"movl $1f,%0\n\t" \
-	"incl %3\n\t" \
-	"mov" #bwl " %2,%" reg "1\n\t" \
-	"xorl %0,%0\n\t" \
-	"decl %3\n1:" \
-:"=&d" (ret), #rtype (x) \
-:"m" (__m(addr)),"m" (current->tss.ex.count))
-
-#define __do_get_user_8(x,addr,ret) \
-__do_get_user_asm(x,addr,ret,b,"b","=&q")
-#define __do_get_user_16(x,addr,ret) \
-__do_get_user_asm(x,addr,ret,w,"w","=&r")
-#define __do_get_user_32(x,addr,ret) \
-__do_get_user_asm(x,addr,ret,l,"","=&r")
-
-extern int __do_get_user_bad(void);
-
-#define __copy_user(to,from,size) \
-__asm__ __volatile__( \
-	"shrl $2,%1\n\t" \
-	"movl $3f,%0\n\t" \
-	"incl %3\n\t" \
-	"rep; movsl\n\t" \
-	"testl $2,%2\n\t" \
-	"je 1f\n\t" \
-	"movsw\n\t" \
-	"subl $2,%2\n" \
-	"1:\t" \
-	"testl $1,%2\n\t" \
-	"je 2f\n\t" \
-	"movsb\n\t" \
-	"decl %2\n" \
-	"2:\t" \
-	"decl %3\n" \
-	"3:\tlea 0(%2,%1,4),%0" \
-	:"=&d" (size) \
-	:"c" (size), "r" (size & 3), "m" (current->tss.ex), \
-	 "D" (to), "S" (from) \
-	:"cx","di","si","memory");
 
-static inline unsigned long __constant_copy_user(void * to, const void * from, unsigned long size)
-{
-	unsigned long result;
+/*
+ * Copy To/From Userspace
+ */
 
-	switch (size & 3) {
-	default:
-		__asm__ __volatile__(
-			"movl $1f,%0\n\t"
-			"incl %1\n\t"
-			"rep ; movsl\n\t"
-			"decl %1\n"
-			"1:\tlea 0(,%%ecx,4),%0"
-			:"=&d" (result)
-			:"m" (current->tss.ex),
-			 "S" (from),"D" (to),"c" (size/4)
-			:"cx","di","si","memory");
-		break;
-	case 1:
-		__asm__ __volatile__(
-			"movl $1f,%0\n\t"
-			"incl %3\n\t"
-			"rep ; movsl\n\t"
-			"movsb\n\t"
-			"decl %1\n\t"
-			"decl %3\n"
-			"1:\tlea 0(%1,%%ecx,4),%0"
-			:"=&d" (result)
-			:"ab" (1),"m" (current->tss.ex),
-			 "S" (from),"D" (to), "c" (size/4)
-			:"cx","di","si","memory");
-		break;
-	case 2:
-		__asm__ __volatile__(
-			"movl $1f,%0\n\t"
-			"incl %2\n\t"
-			"rep ; movsl\n\t"
-			"movsw\n\t"
-			"subl $2,%1\n\t"
-			"decl %2\n"
-			"1:\tlea 0(%1,%%ecx,4),%0"
-			:"=&d" (result)
-			:"ab" (2),"m" (current->tss.ex),
-			 "S" (from),"D" (to),"c" (size/4)
-			:"cx","di","si","memory");
-		break;
-	case 3:
-		__asm__ __volatile__(
-			"movl $1f,%0\n\t"
-			"incl %2\n\t"
-			"rep ; movsl\n\t"
-			"movsw\n\t"
-			"subl $2,%1\n\t"
-			"movsb\n\t"
-			"decl %1\n\t"
-			"decl %2\n"
-			"1:\tlea 0(%1,%%ecx,4),%0"
-			:"=&d" (result)
-			:"ab" (3),"m" (current->tss.ex),
-			 "S" (from),"D" (to),"c" (size/4)
-			:"cx","di","si","memory");
-		break;
-	}
-	return result;
-}
+/* Generic arbitrary sized copy.  */
+#define __copy_user(to,from,size)					\
+	__asm__ __volatile__(						\
+		"0:	rep; movsl\n"					\
+		"	movl %1,%0\n"					\
+		"1:	rep; movsb\n"					\
+		"2:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"3:	lea 0(%1,%0,4),%0\n"				\
+		"	jmp 2b\n"					\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 0b,3b\n"					\
+		"	.long 1b,2b\n"					\
+		".text"							\
+		: "=c"(size)						\
+		: "r"(size & 3), "0"(size / 4), "D"(to), "S"(from)	\
+		: "di", "si", "memory")
+
+/* Optimize just a little bit when we know the size of the move. */
+#define __constant_copy_user(to, from, size)			\
+do {								\
+	switch (size & 3) {					\
+	default:						\
+		__asm__ __volatile__(				\
+			"0:	rep; movsl\n"			\
+			"1:\n"					\
+			".section .fixup,\"ax\"\n"		\
+			"2:	shl $2,%0\n"			\
+			"	jmp 1b\n"			\
+			".section __ex_table,\"a\"\n"		\
+			"	.align 4\n"			\
+			"	.long 0b,2b\n"			\
+			".text"					\
+			: "=c"(size)				\
+			: "S"(from), "D"(to), "0"(size/4)	\
+			: "di", "si", "memory");		\
+		break;						\
+	case 1:							\
+		__asm__ __volatile__(				\
+			"0:	rep; movsl\n"			\
+			"1:	movsb\n"			\
+			"2:\n"					\
+			".section .fixup,\"ax\"\n"		\
+			"3:	shl $2,%0\n"			\
+			"4:	incl %0\n"			\
+			"	jmp 2b\n"			\
+			".section __ex_table,\"a\"\n"		\
+			"	.align 4\n"			\
+			"	.long 0b,3b\n"			\
+			"	.long 1b,4b\n"			\
+			".text"					\
+			: "=c"(size)				\
+			: "S"(from), "D"(to), "0"(size/4)	\
+			: "di", "si", "memory");		\
+		break;						\
+	case 2:							\
+		__asm__ __volatile__(				\
+			"0:	rep; movsl\n"			\
+			"1:	movsw\n"			\
+			"2:\n"					\
+			".section .fixup,\"ax\"\n"		\
+			"3:	shl $2,%0\n"			\
+			"4:	addl $2,%0\n"			\
+			"	jmp 2b\n"			\
+			".section __ex_table,\"a\"\n"		\
+			"	.align 4\n"			\
+			"	.long 0b,3b\n"			\
+			"	.long 1b,4b\n"			\
+			".text"					\
+			: "=c"(size)				\
+			: "S"(from), "D"(to), "0"(size/4)	\
+			: "di", "si", "memory");		\
+		break;						\
+	case 3:							\
+		__asm__ __volatile__(				\
+			"0:	rep; movsl\n"			\
+			"1:	movsw\n"			\
+			"2:	movsb\n"			\
+			"3:\n"					\
+			".section .fixup,\"ax\"\n"		\
+			"4:	shl $2,%0\n"			\
+			"5:	addl $2,%0\n"			\
+			"6:	incl %0\n"			\
+			"	jmp 3b\n"			\
+			".section __ex_table,\"a\"\n"		\
+			"	.align 4\n"			\
+			"	.long 0b,4b\n"			\
+			"	.long 1b,5b\n"			\
+			"	.long 2b,6b\n"			\
+			".text"					\
+			: "=c"(size)				\
+			: "S"(from), "D"(to), "0"(size/4)	\
+			: "di", "si", "memory");		\
+		break;						\
+	}							\
+} while (0)
 
-static inline unsigned long __generic_copy_to_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__generic_copy_to_user(void *to, const void *from, unsigned long n)
 {
 	if (access_ok(VERIFY_WRITE, to, n))
 		__copy_user(to,from,n);
 	return n;
 }
 
-static inline unsigned long __constant_copy_to_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__constant_copy_to_user(void *to, const void *from, unsigned long n)
 {
 	if (access_ok(VERIFY_WRITE, to, n))
-		n = __constant_copy_user(to,from,n);
+		__constant_copy_user(to,from,n);
 	return n;
 }
 
-static inline unsigned long __generic_copy_from_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__generic_copy_from_user(void *to, const void *from, unsigned long n)
 {
 	if (access_ok(VERIFY_READ, from, n))
 		__copy_user(to,from,n);
 	return n;
 }
 
-static inline unsigned long __constant_copy_from_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__constant_copy_from_user(void *to, const void *from, unsigned long n)
 {
 	if (access_ok(VERIFY_READ, from, n))
-		n = __constant_copy_user(to,from,n);
+		__constant_copy_user(to,from,n);
 	return n;
 }
 
-#define copy_to_user(to,from,n) \
-(__builtin_constant_p(n) ? \
- __constant_copy_to_user((to),(from),(n)) : \
- __generic_copy_to_user((to),(from),(n)))
-
-#define copy_from_user(to,from,n) \
-(__builtin_constant_p(n) ? \
- __constant_copy_from_user((to),(from),(n)) : \
- __generic_copy_from_user((to),(from),(n)))
-
-#define __clear_user(addr,size) \
-__asm__ __volatile__( \
-	"movl $3f,%0\n\t" \
-	"incl %2\n\t" \
-	"rep; stosl\n\t" \
-	"testl $2,%3\n\t" \
-	"je 1f\n\t" \
-	"stosw\n\t" \
-	"subl $2,%3\n" \
-	"1:\t" \
-	"testl $1,%3\n\t" \
-	"je 2f\n\t" \
-	"stosb\n\t" \
-	"decl %3\n" \
-	"2:\t" \
-	"decl %2\n" \
-	"3:\tlea 0(%3,%1,4),%0" \
-	:"=&d" (size) \
-	:"c" (size >> 2), "m" (current->tss.ex), "r" (size & 3), \
-	 "D" (addr), "a" (0) \
-	:"cx","di","memory");
-
-#define clear_user(addr,n) ({ \
-void * __cl_addr = (addr); \
-unsigned long __cl_size = (n); \
-if (__cl_size && __access_ok(VERIFY_WRITE, ((unsigned long)(__cl_addr)), __cl_size)) \
-__clear_user(__cl_addr, __cl_size); \
-__cl_size; })
-
-#define __strncpy_from_user(dst,src,count,res) \
-__asm__ __volatile__( \
-	"cld\n\t" \
-	"movl $3f,%0\n\t" \
-	"incl %2\n" \
-	"1:\tdecl %1\n\t" \
-	"js 2f\n\t" \
-	"lodsb\n\t" \
-	"stosb\n\t" \
-	"testb %%al,%%al\n\t" \
-	"jne 1b\n" \
-	"2:\t" \
-	"incl %1\n\t" \
-	"xorl %0,%0\n\t" \
-	"decl %2\n" \
-	"3:" \
-	:"=&d" (res), "=r" (count) \
-	:"m" (current->tss.ex), "1" (count), "S" (src),"D" (dst) \
-	:"si","di","ax","memory")
-
-#define strncpy_from_user(dest,src,count) ({ \
-const void * __sc_src = (src); \
-unsigned long __sc_count = (count); \
-long __sc_res = -EFAULT; \
-if (__access_ok(VERIFY_READ, ((unsigned long)(__sc_src)), __sc_count)) { \
-	unsigned long __sc_residue = __sc_count; \
-	__strncpy_from_user(dest,__sc_src,__sc_count,__sc_res); \
-	if (!__sc_res) __sc_res = __sc_residue - __sc_count; \
-} __sc_res; })
 
+#define copy_to_user(to,from,n)				\
+	(__builtin_constant_p(n) ?			\
+	 __constant_copy_to_user((to),(from),(n)) :	\
+	 __generic_copy_to_user((to),(from),(n)))
+
+#define copy_from_user(to,from,n)			\
+	(__builtin_constant_p(n) ?			\
+	 __constant_copy_from_user((to),(from),(n)) :	\
+	 __generic_copy_from_user((to),(from),(n)))
 
-extern inline int verify_area(int type, const void * addr, unsigned long size)
+
+/*
+ * Zero Userspace
+ */
+
+#define __clear_user(addr,size)						\
+	__asm__ __volatile__(						\
+		"0:	rep; stosl\n"					\
+		"	movl %1,%0\n"					\
+		"1:	rep; stosb\n"					\
+		"2:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"3:	lea 0(%1,%0,4),%0\n"				\
+		"	jmp 2b\n"					\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 0b,3b\n"					\
+		"	.long 1b,2b\n"					\
+		".text"							\
+		: "=c"(size)						\
+		: "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0)	\
+		: "di")
+
+static inline unsigned long
+clear_user(void *to, unsigned long n)
+{
+	if (access_ok(VERIFY_WRITE, to, n))
+		__clear_user(to, n);
+	return n;
+}
+
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+#define __strncpy_from_user(dst,src,count,res)				   \
+	__asm__ __volatile__(						   \
+		"	testl %1,%1\n"					   \
+		"	jz 2f\n"					   \
+		"0:	lodsb\n"					   \
+		"	stosb\n"					   \
+		"	testb %%al,%%al\n"				   \
+		"	jz 1f\n"					   \
+		"	decl %1\n"					   \
+		"	jnz 0b\n"					   \
+		"1:	subl %1,%0\n"					   \
+		"2:\n"							   \
+		".section .fixup,\"ax\"\n"				   \
+		"3:	movl %2,%0\n"					   \
+		"	jmp 2b\n"					   \
+		".section __ex_table,\"a\"\n"				   \
+		"	.align 4\n"					   \
+		"	.long 0b,3b\n"					   \
+		".text"							   \
+		: "=r"(res), "=r"(count)				   \
+		: "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \
+		: "si", "di", "ax", "memory")
+
+static inline long
+strncpy_from_user(char *dst, const char *src, long count)
 {
-	return access_ok(type,addr,size)?0:-EFAULT;
+	long res = -EFAULT;
+	if (access_ok(VERIFY_READ, src, 1))
+		__strncpy_from_user(dst, src, count, res);
+	return res;
 }
+
 
 #endif /* __i386_UACCESS_H */
diff -u --recursive --new-file v2.1.6/linux/include/linux/baycom.h linux/include/linux/baycom.h
--- v2.1.6/linux/include/linux/baycom.h	Mon Jul  8 10:21:46 1996
+++ linux/include/linux/baycom.h	Wed Oct 30 14:22:22 1996
@@ -7,61 +7,38 @@
 #ifndef _BAYCOM_H
 #define _BAYCOM_H
 
-#include <linux/ioctl.h>
-#undef BAYCOM_DEBUG
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
 
 /* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
 
-struct baycom_statistics {
-	unsigned long rx_packets, tx_packets;
-	unsigned long ptt_keyed;
-	unsigned long rx_bufferoverrun, tx_bufferoverrun;
+struct baycom_debug_data {
+	unsigned long debug1;
+	unsigned long debug2;
+	long debug3;
 };
 
-struct baycom_params {
-	int modem_type;
-	int iobase;
-	int irq;
-	int options;
-	int tx_delay;  /* the transmitter keyup delay in 10ms units */
-	int tx_tail;   /* the transmitter keyoff delay in 10ms units */
-	int slottime;  /* the slottime in 10ms; usually 10 = 100ms */
-	int ppersist;  /* the p-persistence 0..255 */
-	int fulldup;   /* the driver does not support full duplex, setting */
-	               /* this just makes the driver send even if DCD is on */
-};	
-
-/* -------------------------------------------------------------------- */
-
-#define BAYCOM_MAJOR 		51
-
-/* maximum packet length, excluding CRC */
-#define BAYCOM_MAXFLEN 		400	
-
-/* the ioctl type of this driver */
-#define BAYCOM_IOCTL_TYPE       'B'
+struct baycom_modem_type {
+	unsigned char modem_type;
+	unsigned int options;
+};
 
-#define KISS_FEND   ((unsigned char)0300)
-#define KISS_FESC   ((unsigned char)0333)
-#define KISS_TFEND  ((unsigned char)0334)
-#define KISS_TFESC  ((unsigned char)0335)
-
-#define KISS_CMD_DATA       0
-#define KISS_CMD_TXDELAY    1
-#define KISS_CMD_PPERSIST   2
-#define KISS_CMD_SLOTTIME   3
-#define KISS_CMD_TXTAIL     4
-#define KISS_CMD_FULLDUP    5
+struct baycom_ioctl {
+	int cmd;
+	union {
+		struct baycom_modem_type mt;
+		struct baycom_debug_data dbg;
+	} data;
+};
 
-/*
- * use bottom halves? (HDLC processing done with interrupts on or off)
- */
-#define BAYCOM_USE_BH
+/* -------------------------------------------------------------------- */
 
 /*
  * modem types
  */
-
 #define BAYCOM_MODEM_INVALID 0
 #define BAYCOM_MODEM_SER12   1
 #define BAYCOM_MODEM_PAR96   2
@@ -71,59 +48,15 @@
  */
 #define BAYCOM_OPTIONS_SOFTDCD  1
 
-
 /*
- * ioctl constants
+ * ioctl values change for baycom_net
  */
-#define BAYCOMCTL_GETDCD           _IOR(BAYCOM_IOCTL_TYPE, 0, unsigned char)
-#define BAYCOMCTL_GETPTT           _IOR(BAYCOM_IOCTL_TYPE, 1, unsigned char)
-#define BAYCOMCTL_PARAM_TXDELAY    _IO(BAYCOM_IOCTL_TYPE, 2)
-#define BAYCOMCTL_PARAM_PPERSIST   _IO(BAYCOM_IOCTL_TYPE, 3)
-#define BAYCOMCTL_PARAM_SLOTTIME   _IO(BAYCOM_IOCTL_TYPE, 4)
-#define BAYCOMCTL_PARAM_TXTAIL     _IO(BAYCOM_IOCTL_TYPE, 5)
-#define BAYCOMCTL_PARAM_FULLDUP    _IO(BAYCOM_IOCTL_TYPE, 6)
-
-#define BAYCOMCTL_GETSTAT          _IOR(BAYCOM_IOCTL_TYPE, 7, \
-					struct baycom_statistics)
-
-#define BAYCOMCTL_GETPARAMS        _IOR(BAYCOM_IOCTL_TYPE, 8, \
-					struct baycom_params)
-#define BAYCOMCTL_SETPARAMS        _IOR(BAYCOM_IOCTL_TYPE, 9, \
-					struct baycom_params)
-
-#define BAYCOMCTL_CALIBRATE        _IO(BAYCOM_IOCTL_TYPE, 10)
-
-#ifdef BAYCOM_DEBUG
-/*
- * these are mainly for debugging purposes
- */
-#define BAYCOMCTL_GETSAMPLES       _IOR(BAYCOM_IOCTL_TYPE, 16, unsigned char)
-#define BAYCOMCTL_GETBITS          _IOR(BAYCOM_IOCTL_TYPE, 17, unsigned char)
-
-#define BAYCOMCTL_DEBUG1           _IOR(BAYCOM_IOCTL_TYPE, 18, unsigned long)
-#define BAYCOMCTL_DEBUG2           _IOR(BAYCOM_IOCTL_TYPE, 19, unsigned long)
-#define BAYCOMCTL_DEBUG3           _IOR(BAYCOM_IOCTL_TYPE, 20, unsigned long)
-#endif /* BAYCOM_DEBUG */
+#define BAYCOMCTL_GETMODEMTYPE   0x90
+#define BAYCOMCTL_SETMODEMTYPE   0x91
+#define BAYCOMCTL_GETDEBUG       0x92
 
 /* -------------------------------------------------------------------- */
 
 #endif /* _BAYCOM_H */
 
 /* --------------------------------------------------------------------- */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file v2.1.6/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.1.6/linux/include/linux/hdlcdrv.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/hdlcdrv.h	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,326 @@
+/*
+ *	hdlcdrv.h  -- HDLC packet radio network driver.
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _HDLCDRV_H
+#define _HDLCDRV_H
+
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct hdlcdrv_params {
+	int iobase;
+	int irq;
+	int dma;
+	int seriobase;
+	int pariobase;
+	int midiiobase;
+};	
+
+struct hdlcdrv_channel_params {
+	int tx_delay;  /* the transmitter keyup delay in 10ms units */
+	int tx_tail;   /* the transmitter keyoff delay in 10ms units */
+	int slottime;  /* the slottime in 10ms; usually 10 = 100ms */
+	int ppersist;  /* the p-persistence 0..255 */
+	int fulldup;   /* some driver do not support full duplex, setting */
+	               /* this just makes them send even if DCD is on */
+};	
+
+struct hdlcdrv_channel_state {
+	int ptt;
+	int dcd;
+	int ptt_keyed;
+	struct enet_statistics stats;
+};
+
+struct hdlcdrv_ioctl {
+	int cmd;
+	union {
+		struct hdlcdrv_params mp;
+		struct hdlcdrv_channel_params cp;
+		struct hdlcdrv_channel_state cs;
+		unsigned int calibrate;
+		unsigned char bits;
+	} data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * ioctl values
+ */
+#define HDLCDRVCTL_GETMODEMPAR      0
+#define HDLCDRVCTL_SETMODEMPAR      1
+#define HDLCDRVCTL_GETCHANNELPAR    2
+#define HDLCDRVCTL_SETCHANNELPAR    3
+#define HDLCDRVCTL_GETSTAT          4
+#define HDLCDRVCTL_CALIBRATE        5
+
+/*
+ * these are mainly for debugging purposes
+ */
+#define HDLCDRVCTL_GETSAMPLES       10
+#define HDLCDRVCTL_GETBITS          11
+
+/* -------------------------------------------------------------------- */
+
+#ifdef __KERNEL__
+
+#define HDLCDRV_MAGIC      0x5ac6e778
+#define HDLCDRV_IFNAMELEN    6
+#define HDLCDRV_HDLCBUFFER  16 /* should be a power of 2 for speed reasons */
+#define HDLCDRV_BITBUFFER  256 /* should be a power of 2 for speed reasons */
+#undef HDLCDRV_LOOPBACK  /* define for HDLC debugging purposes */
+#undef HDLCDRV_DEBUG
+
+/* maximum packet length, excluding CRC */
+#define HDLCDRV_MAXFLEN             400	
+
+
+struct hdlcdrv_hdlcbuffer {
+	unsigned rd, wr;
+	unsigned short buf[HDLCDRV_HDLCBUFFER];
+};
+
+#ifdef HDLCDRV_DEBUG
+struct hdlcdrv_bitbuffer {
+	unsigned int rd;
+	unsigned int wr;
+	unsigned int shreg;
+	unsigned char buffer[HDLCDRV_BITBUFFER];
+};
+
+extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, 
+					 unsigned int bit)
+{
+	unsigned char new;
+
+	new = buf->shreg & 1;
+	buf->shreg >>= 1;
+	buf->shreg |= (!!bit) << 7;
+	if (new) {
+		buf->buffer[buf->wr] = buf->shreg;
+		buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+		buf->shreg = 0x80;
+	}
+}
+
+extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, 
+					      unsigned int bits)
+{
+	buf->buffer[buf->wr] = bits & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+	buf->buffer[buf->wr] = (bits >> 8) & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+
+}
+#endif /* HDLCDRV_DEBUG */
+
+/* -------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each driver. 
+ */
+
+struct hdlcdrv_ops {
+	/*
+	 * first some informations needed by the hdlcdrv routines
+	 */
+	int bitrate;
+	/*
+	 * the routines called by the hdlcdrv routines
+	 */
+	int (*open)(struct device *);
+	int (*close)(struct device *);
+	int (*ioctl)(struct device *, struct ifreq *, int);
+};
+
+struct hdlcdrv_state {
+	int magic;
+
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	const struct hdlcdrv_ops *ops;
+
+	struct hdlcdrv_pttoutput {
+		int seriobase;
+		int pariobase;
+		int midiiobase;
+		unsigned int flags;
+	} ptt_out;
+
+	struct hdlcdrv_channel_params ch_params;
+
+	struct hdlcdrv_hdlcrx {
+		struct hdlcdrv_hdlcbuffer hbuf;
+		int in_hdlc_rx;
+		/* 0 = sync hunt, != 0 receiving */
+		int rx_state;	
+		unsigned int bitstream;
+		unsigned int bitbuf;
+		int numbits;
+		unsigned char dcd;
+		
+		int len;
+		unsigned char *bp;
+		unsigned char buffer[HDLCDRV_MAXFLEN+2];
+	} hdlcrx;
+
+	struct hdlcdrv_hdlctx {
+		struct hdlcdrv_hdlcbuffer hbuf;
+		int in_hdlc_tx;
+		/*
+		 * 0 = send flags
+		 * 1 = send txtail (flags)
+		 * 2 = send packet
+		 */
+		int tx_state;	
+		int numflags;
+		unsigned int bitstream;
+		unsigned char ptt;
+		int calibrate;
+		int slotcnt;
+
+		unsigned int bitbuf;
+		int numbits;
+		
+		int len;
+		unsigned char *bp;
+		unsigned char buffer[HDLCDRV_MAXFLEN+2];
+	} hdlctx;
+
+#ifdef HDLCDRV_DEBUG
+	struct hdlcdrv_bitbuffer bitbuf_channel;
+	struct hdlcdrv_bitbuffer bitbuf_hdlc;
+#endif /* HDLCDRV_DEBUG */
+
+	struct enet_statistics stats;
+	int ptt_keyed;
+
+	struct sk_buff_head send_queue;  /* Packets awaiting transmission */
+};
+
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) 
+{
+	return !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) 
+		 % HDLCDRV_HDLCBUFFER);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
+{
+	return hb->rd == hb->wr;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
+{
+	unsigned newr;
+	unsigned short val;
+	unsigned long flags;
+
+	if (hb->rd == hb->wr)
+		return 0;
+	save_flags(flags);
+	cli();
+	newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
+	val = hb->buf[hb->rd];
+	hb->rd = newr;
+	restore_flags(flags);
+	return val;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, 
+				    unsigned short val)
+{
+	unsigned newp;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER;
+	if (newp != hb->rd) { 
+		hb->buf[hb->wr] = val & 0xffff;
+		hb->wr = newp;
+	}
+	restore_flags(flags);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
+{
+	hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
+}
+
+extern unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
+{
+	unsigned int ret;
+
+	if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) {
+		if (s->hdlctx.calibrate > 0)
+			s->hdlctx.calibrate--;
+		else
+			s->hdlctx.ptt = 0;
+		ret = 0;
+	} else 
+		ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf);
+#ifdef HDLCDRV_LOOPBACK
+	hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret);
+#endif /* HDLCDRV_LOOPBACK */
+	return ret;
+}
+
+extern void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
+{
+#ifdef HDLCDRV_DEBUG
+	hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
+#endif /* HDLCDRV_DEBUG */
+}
+
+extern void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
+{
+	s->hdlcrx.dcd = !!dcd;
+}
+
+extern int hdlcdrv_ptt(struct hdlcdrv_state *s)
+{
+	return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
+}
+
+/* -------------------------------------------------------------------- */
+
+void hdlcdrv_receiver(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_transmitter(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_arbitrate(struct device *, struct hdlcdrv_state *);
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+			     unsigned int privsize, char *ifname,
+			     unsigned int baseaddr, unsigned int irq, 
+			     unsigned int dma);
+int hdlcdrv_unregister_hdlcdrv(struct device *dev);
+
+/* -------------------------------------------------------------------- */
+
+
+
+#endif /* __KERNEL__ */
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _HDLCDRV_H */
+
+/* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.6/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.1.6/linux/include/linux/interrupt.h	Thu Jun  6 12:59:42 1996
+++ linux/include/linux/interrupt.h	Wed Oct 30 14:15:12 1996
@@ -33,7 +33,6 @@
 	DIGI_BH,
 	SERIAL_BH,
 	RISCOM8_BH,
-	BAYCOM_BH,
 	NET_BH,
 	IMMEDIATE_BH,
 	KEYBOARD_BH,
diff -u --recursive --new-file v2.1.6/linux/include/linux/md.h linux/include/linux/md.h
--- v2.1.6/linux/include/linux/md.h	Mon May 20 07:50:46 1996
+++ linux/include/linux/md.h	Wed Oct 30 03:06:23 1996
@@ -18,7 +18,6 @@
 #ifndef _MD_H
 #define _MD_H
 
-#include <asm/segment.h>
 #include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/ioctl.h>
diff -u --recursive --new-file v2.1.6/linux/include/linux/scc.h linux/include/linux/scc.h
--- v2.1.6/linux/include/linux/scc.h	Tue Oct 29 19:58:47 1996
+++ linux/include/linux/scc.h	Thu Oct 31 08:05:23 1996
@@ -1,10 +1,8 @@
-/* $Id: scc.h,v 1.26 1996/10/09 16:35:56 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.28 1996/10/30 20:01:15 jreuter Exp jreuter $ */
 
 #ifndef	_SCC_H
 #define	_SCC_H
 
-#include <linux/if_ether.h>
-
 /* selection of hardware types */
 
 #define PA0HZP		0x00	/* hardware type for PA0HZP SCC card and compatible */
@@ -14,10 +12,6 @@
 #define DRSI		0x08	/* hardware type for DRSI PC*Packet card */
 #define BAYCOM		0x10	/* hardware type for BayCom (U)SCC */
 
-/* Paranoia check... */
-
-#define SCC_PARANOIA_CHECK	/* tell the user if something is going wrong */
-
 /* DEV ioctl() commands */
 
 #define SIOCSCCRESERVED (SIOCDEVPRIVATE+0)
@@ -103,49 +97,8 @@
 #define TXS_WAIT	5	/* Waiting for Mintime to expire */
 #define TXS_TIMEOUT	6	/* We had a transmission timeout */
 
-#define TX_ON		1	/* command for scc_key_trx() */
-#define TX_OFF		0	/* dto */
-
-/* Vector masks in RR2B */
-
-#define VECTOR_MASK	0x06
-#define TXINT		0x00
-#define EXINT		0x02
-#define RXINT		0x04
-#define SPINT		0x06
-
 typedef unsigned long io_port;	/* type definition for an 'io port address' */
 
-#ifdef SCC_DELAY
-#define Inb(port)	inb_p(port)
-#define Outb(port, val)	outb_p(val, port)
-#else
-#define Inb(port)	inb(port)
-#define Outb(port, val)	outb(val, port)
-#endif
-
-#define TIMER_OFF 65535U
-
-/* SCC channel control structure for KISS */
-
-struct scc_kiss {
-	unsigned char txdelay;		/* Transmit Delay 10 ms/cnt */
-	unsigned char persist;		/* Persistence (0-255) as a % */
-	unsigned char slottime;		/* Delay to wait on persistence hit */
-	unsigned char tailtime;		/* Delay after last byte written */
-	unsigned char fulldup;		/* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
-	unsigned char waittime;		/* Waittime before any transmit attempt */
-	unsigned int  maxkeyup;		/* Maximum time to transmit (seconds) */
-	unsigned char mintime;		/* Minimal offtime after MAXKEYUP timeout (seconds) */
-	unsigned int  idletime;		/* Maximum idle time in ALWAYS KEYED mode (seconds) */
-	unsigned int  maxdefer;		/* Timer for CSMA channel busy limit */
-	unsigned char tx_inhibit;	/* Transmit is not allowed when set */	
-	unsigned char group;		/* Group ID for AX.25 TX interlocking */
-	unsigned char mode;		/* 'normal' or 'hwctrl' mode (unused) */
-	unsigned char softdcd;		/* Use DPLL instead of DCD pin for carrier detect */
-};
-
-
 /* SCC statistical information */
 
 struct scc_stat {
@@ -207,6 +160,48 @@
 };
 
 
+#ifdef __KERNEL__
+
+#define TX_ON		1	/* command for scc_key_trx() */
+#define TX_OFF		0	/* dto */
+
+/* Vector masks in RR2B */
+
+#define VECTOR_MASK	0x06
+#define TXINT		0x00
+#define EXINT		0x02
+#define RXINT		0x04
+#define SPINT		0x06
+
+
+#ifdef SCC_DELAY
+#define Inb(port)	inb_p(port)
+#define Outb(port, val)	outb_p(val, port)
+#else
+#define Inb(port)	inb(port)
+#define Outb(port, val)	outb(val, port)
+#endif
+
+/* SCC channel control structure for KISS */
+
+struct scc_kiss {
+	unsigned char txdelay;		/* Transmit Delay 10 ms/cnt */
+	unsigned char persist;		/* Persistence (0-255) as a % */
+	unsigned char slottime;		/* Delay to wait on persistence hit */
+	unsigned char tailtime;		/* Delay after last byte written */
+	unsigned char fulldup;		/* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
+	unsigned char waittime;		/* Waittime before any transmit attempt */
+	unsigned int  maxkeyup;		/* Maximum time to transmit (seconds) */
+	unsigned char mintime;		/* Minimal offtime after MAXKEYUP timeout (seconds) */
+	unsigned int  idletime;		/* Maximum idle time in ALWAYS KEYED mode (seconds) */
+	unsigned int  maxdefer;		/* Timer for CSMA channel busy limit */
+	unsigned char tx_inhibit;	/* Transmit is not allowed when set */	
+	unsigned char group;		/* Group ID for AX.25 TX interlocking */
+	unsigned char mode;		/* 'normal' or 'hwctrl' mode (unused) */
+	unsigned char softdcd;		/* Use DPLL instead of DCD pin for carrier detect */
+};
+
+
 /* SCC channel structure */
 
 struct scc_channel {
@@ -235,7 +230,7 @@
         struct scc_stat stat;		/* statistical information */
         struct scc_modem modem; 	/* modem information */
         
-        struct sk_buff *tx_next_buff;	/* next tx buffer */
+        struct sk_buff_head tx_queue;	/* next tx buffer */
         struct sk_buff *rx_buff;	/* pointer to frame currently received */
         struct sk_buff *tx_buff;	/* pointer to frame currently transmitted */
 
@@ -246,4 +241,5 @@
 };
 
 int scc_init(void);
-#endif
+#endif /* defined(__KERNEL__) */
+#endif /* defined(_SCC_H) */
diff -u --recursive --new-file v2.1.6/linux/include/linux/soundmodem.h linux/include/linux/soundmodem.h
--- v2.1.6/linux/include/linux/soundmodem.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/soundmodem.h	Wed Oct 30 14:15:12 1996
@@ -0,0 +1,107 @@
+/*
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _SOUNDMODEM_H
+#define _SOUNDMODEM_H
+
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct sm_debug_data {
+	unsigned long debug1;
+	unsigned long debug2;
+	long debug3;
+};
+
+struct sm_diag_data {
+	unsigned int mode;
+	unsigned int flags;
+	unsigned int samplesperbit;
+	unsigned int datalen;
+	short *data;
+};
+
+struct sm_mixer_data {
+	unsigned int mixer_type;
+	unsigned int sample_rate;
+	unsigned int bit_rate;
+	unsigned int reg;
+	unsigned int data;
+};
+
+struct sm_config {
+	int hardware;
+	int mode;
+};
+
+struct sm_ioctl {
+	int cmd;
+	union {
+		struct sm_config cfg;
+		struct sm_diag_data diag;	
+		struct sm_mixer_data mix;
+		struct sm_debug_data dbg;
+	} data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * config: hardware
+ */
+#define SM_HARDWARE_INVALID   -1
+#define SM_HARDWARE_SBC       0
+#define SM_HARDWARE_WSS       1
+#define SM_HARDWARE_WSSFDX    2  /* currently does not work! */
+
+/*
+ * config: mode
+ */
+#define SM_MODE_INVALID       -1
+#define SM_MODE_AFSK1200      0
+#define SM_MODE_FSK9600       1
+
+/*
+ * diagnose modes
+ */
+#define SM_DIAGMODE_OFF        0
+#define SM_DIAGMODE_INPUT      1
+#define SM_DIAGMODE_DEMOD      2
+
+/*
+ * diagnose flags
+ */
+#define SM_DIAGFLAG_DCDGATE    (1<<0)
+#define SM_DIAGFLAG_VALID      (1<<1)
+
+/*
+ * mixer types
+ */
+#define SM_MIXER_INVALID       0
+#define SM_MIXER_AD1848        0x10
+#define SM_MIXER_CT1335        0x20
+#define SM_MIXER_CT1345        0x21
+#define SM_MIXER_CT1745        0x22
+
+/*
+ * ioctl values
+ */
+#define SMCTL_GETMODEMTYPE     0x80
+#define SMCTL_SETMODEMTYPE     0x81
+#define SMCTL_DIAGNOSE         0x82
+#define SMCTL_GETMIXER         0x83
+#define SMCTL_SETMIXER         0x84
+#define SMCTL_GETDEBUG         0x85
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _SOUNDMODEM_H */
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.6/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.6/linux/include/linux/tty.h	Mon Sep 30 11:19:00 1996
+++ linux/include/linux/tty.h	Fri Nov  1 15:24:52 1996
@@ -292,7 +292,6 @@
 extern int stl_init(void);
 extern int stli_init(void);
 extern int riscom8_init(void);
-extern int baycom_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
 			      const char *routine);
diff -u --recursive --new-file v2.1.6/linux/init/main.c linux/init/main.c
--- v2.1.6/linux/init/main.c	Wed Oct  9 08:55:24 1996
+++ linux/init/main.c	Wed Oct 30 14:15:12 1996
@@ -172,6 +172,9 @@
 #ifdef CONFIG_BAYCOM
 extern void baycom_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_SOUNDMODEM
+extern void sm_setup(char *str, int *ints);
+#endif
 
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
@@ -414,6 +417,9 @@
 #endif
 #ifdef CONFIG_BAYCOM
 	{ "baycom=", baycom_setup },
+#endif
+#ifdef CONFIG_SOUNDMODEM
+	{ "soundmodem=", sm_setup },
 #endif
 	{ 0, 0 }
 };
diff -u --recursive --new-file v2.1.6/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.1.6/linux/net/core/datagram.c	Tue Oct 29 19:58:49 1996
+++ linux/net/core/datagram.c	Wed Oct 30 19:52:11 1996
@@ -68,9 +68,7 @@
  
 static inline int connection_based(struct sock *sk)
 {
-	if(sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM)
-		return 1;
-	return 0;
+	return (sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM);
 }
 
 /*
@@ -190,29 +188,26 @@
 
 int datagram_select(struct sock *sk, int sel_type, select_table *wait)
 {
-	select_wait(sk->sleep, wait);
+	if (sk->err)
+		return 1;
 	switch(sel_type)
 	{
 		case SEL_IN:
-			if (sk->err)
-				return 1;
 			if (sk->shutdown & RCV_SHUTDOWN)
 				return 1;
 			if (connection_based(sk) && sk->state==TCP_CLOSE)
 			{
 				/* Connection closed: Wake up */
-				return(1);
+				return 1;
 			}
-			if (skb_peek(&sk->receive_queue) != NULL)
+			if (!skb_queue_empty(&sk->receive_queue))
 			{	/* This appears to be consistent
 				   with other stacks */
-				return(1);
+				return 1;
 			}
-			return(0);
+			break;
 
 		case SEL_OUT:
-			if (sk->err)
-				return 1;
 			if (sk->shutdown & SEND_SHUTDOWN)
 				return 1;
 			if (connection_based(sk) && sk->state==TCP_SYN_SENT)
@@ -222,18 +217,19 @@
 			}
 			if (sk->prot && sock_wspace(sk) >= MIN_WRITE_SPACE)
 			{
-				return(1);
+				return 1;
 			}
 			if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE)
 			{
-				return(1);
+				return 1;
 			}
-			return(0);
+			break;
 
 		case SEL_EX:
-			if (sk->err)
-				return(1); /* Socket has gone into error state (eg icmp error) */
-			return(0);
+			break;
 	}
-	return(0);
+
+	/* select failed.. */
+	select_wait(sk->sleep, wait);
+	return 0;
 }
diff -u --recursive --new-file v2.1.6/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.6/linux/net/core/dev.c	Tue Oct 29 19:58:49 1996
+++ linux/net/core/dev.c	Wed Oct 30 14:15:12 1996
@@ -1369,6 +1369,9 @@
 extern int scc_init(void);
 extern void sdla_setup(void);
 extern void dlci_setup(void);
+extern int pt_init(void);
+extern int sm_init(void);
+extern int baycom_init(void);
 
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_net_dev = {
@@ -1419,6 +1422,12 @@
 #endif
 #if defined(CONFIG_SDLA)
 	sdla_setup();
+#endif
+#if defined(CONFIG_BAYCOM)
+	baycom_init();
+#endif
+#if defined(CONFIG_SOUNDMODEM)
+	sm_init();
 #endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
