]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.7 2.1.7
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:12:37 +0000 (15:12 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:12:37 +0000 (15:12 -0500)
61 files changed:
CREDITS
Documentation/Configure.help
Makefile
arch/alpha/kernel/entry.S
arch/alpha/kernel/traps.c
arch/alpha/lib/clear_user.S
arch/alpha/lib/copy_user.S
arch/alpha/lib/strncpy_from_user.S
arch/alpha/mm/Makefile
arch/alpha/mm/extable.c [new file with mode: 0644]
arch/alpha/mm/fault.c
arch/i386/mm/Makefile
arch/i386/mm/extable.c [new file with mode: 0644]
arch/i386/mm/fault.c
drivers/block/md.c
drivers/cdrom/cdrom.c
drivers/char/README.baycom [deleted file]
drivers/char/baycom.c [deleted file]
drivers/char/ftape/ftape-ctl.c
drivers/char/ftape/ftape-io.c
drivers/char/ftape/ftape-read.c
drivers/char/ftape/ftape-write.c
drivers/char/ftape/kernel-interface.c
drivers/char/misc.c
drivers/char/tty_io.c
drivers/isdn/isdn_common.c
drivers/isdn/isdn_net.c
drivers/isdn/isdn_ppp.c
drivers/isdn/isdn_tty.c
drivers/isdn/teles/callc.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/README.baycom [new file with mode: 0644]
drivers/net/README.soundmodem [new file with mode: 0644]
drivers/net/baycom.c [new file with mode: 0644]
drivers/net/eql.c
drivers/net/hdlcdrv.c [new file with mode: 0644]
drivers/net/scc.c
drivers/net/sm_tables.h [new file with mode: 0644]
drivers/net/soundmodem.c [new file with mode: 0644]
drivers/net/tulip.c
drivers/scsi/aha1740.c
drivers/scsi/hosts.c
drivers/scsi/scsi.c
fs/ioctl.c
fs/ncpfs/ncplib_kernel.h
fs/super.c
include/asm-alpha/processor.h
include/asm-alpha/uaccess.h
include/asm-i386/processor.h
include/asm-i386/uaccess.h
include/linux/baycom.h
include/linux/hdlcdrv.h [new file with mode: 0644]
include/linux/interrupt.h
include/linux/md.h
include/linux/scc.h
include/linux/soundmodem.h [new file with mode: 0644]
include/linux/tty.h
init/main.c
net/core/datagram.c
net/core/dev.c

diff --git a/CREDITS b/CREDITS
index 4b1cd4fcef9d42a412eaca524368b7e25c12edfe..3b9dd035587fcf1936286be3e2063183baf7524f 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -668,7 +668,7 @@ S: Chapel Hill, North Carolina 27514-4818
 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 @@ D: the gpm mouse server and kernel support for it
 
 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
index 90aee7f3eed79473fb8752718e3f7777441910a8..0eb664a73a73655fe2be63ff56b28ed9a1fd6439 100644 (file)
@@ -1875,17 +1875,29 @@ CONFIG_SCC
   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
index f559827c224fcb606ff9b1fb10a5dc79d0ada929..e688cc234a4d7e00ddf15ce1fe30258619ba496d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 6
+SUBLEVEL = 7
 
 ARCH = i386
 
index 8bada12c69c32512dc5742b00b830432210508af..d091429f38bcb528429bcc49c5ea800fa0f13a39 100644 (file)
@@ -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 @@ entInt:
 .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 @@ kernel_clone:
        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 @@ entUnaUser:
 
 /*
  * A fork is the same as clone(SIGCHLD, 0);
-*/
+ */
 .align 3
 .globl sys_fork
 .ent   sys_fork
@@ -508,7 +528,7 @@ ret_from_sys_call:
        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 @@ ret_from_reschedule:
 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 @@ strace_error:
        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 */
index 22172c2af845deba16c334de96ae6de813654ac6..a9e0c6cf2a2bc4bcd9321f39da81dae5e2f901e0 100644 (file)
@@ -178,63 +178,217 @@ struct unaligned_stat {
        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);
 }
 
index ab12797d645fe20cba1d3a84f77ee34e36910c4c..dff21e4d73a78c08c18709820de180f5964229f7 100644 (file)
  *     $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 @@ $loop:
        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 @@ $loop:
 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 @@ $loop:
 
 $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 @@ $oneword:
        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
index bd27608dded0b7c5be1d44851a6fccfce973df0f..9b9efc3384a2133cad7c62acf2c944a22ca58e82 100644 (file)
@@ -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
  *     $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 @@ $36:
        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 @@ $48:
        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 @@ $57:
 $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 @@ $66:
        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
index 04d83f8ec2230dd4533e17d54ccc2a8a98910551..4232932d43aa9f25d6625c4cfb70001f4478dfe1 100644 (file)
  *
  * -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
+
+       /* 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
 
-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
-
-       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
index c8906d83b456eb32db1161485a515c2374aba517..d9614a7c506712063b7231eb9c3e930f630623b9 100644 (file)
@@ -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 --git a/arch/alpha/mm/extable.c b/arch/alpha/mm/extable.c
new file mode 100644 (file)
index 0000000..31d0891
--- /dev/null
@@ -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;
+}
index 3fb73d12879181177c7fba272c04858dabe5d016..14df6c0660024dcd18260c07a85f50463276642b 100644 (file)
@@ -52,14 +52,24 @@ extern void die_if_kernel(char *,struct pt_regs *,long);
  *     -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 @@ good_area:
  */
 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 @@ bad_area:
  * 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);
 }
index e10f814ececb2cbbca3ae2a2bbcc57efe844f0d8..cee7d4e6d129f323c3cf456d3592f30d1ac57c70 100644 (file)
@@ -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 --git a/arch/i386/mm/extable.c b/arch/i386/mm/extable.c
new file mode 100644 (file)
index 0000000..b5b0367
--- /dev/null
@@ -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;
+}
index 9c7df1109c453f828b4eb9e798154f475383a2e5..50dcd0735e9cfacc63966fbd03808fca3f137663 100644 (file)
@@ -93,6 +93,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
        struct vm_area_struct * vma;
        unsigned long address;
        unsigned long page;
+       unsigned long fixup;
        int write;
 
        /* get the address */
@@ -161,14 +162,14 @@ good_area:
  */
 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;
index 2243256b9586223942daa5bdaacad4c0f0ed32c0..bb9992d807a130cd3cca357fdbbc31128d400e47 100644 (file)
@@ -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 @@ static int md_ioctl (struct inode *inode, struct file *file,
       
     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 @@ static int md_ioctl (struct inode *inode, struct file *file,
     
     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 @@ static int md_ioctl (struct inode *inode, struct file *file,
     
     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 void md_release (struct inode *inode, struct file *file)
 }
 
 
-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 @@ static int md_read (struct inode *inode, struct file *file,
   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);
index 4b7bb962c2e3a5267a602fc8b03128de95297324..b6af37e53110523e25448680b245586f94b0ed3b 100644 (file)
@@ -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 --git a/drivers/char/README.baycom b/drivers/char/README.baycom
deleted file mode 100644 (file)
index 25f2f58..0000000
+++ /dev/null
@@ -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 --git a/drivers/char/baycom.c b/drivers/char/baycom.c
deleted file mode 100644 (file)
index e3a89e3..0000000
+++ /dev/null
@@ -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 */
-/* --------------------------------------------------------------------- */
index c9c9a05a9b3c412d6f3c75b7cd6bf9951bc42a54..e8c3ae1a1df5339757795dedc64edd13ec58327e 100644 (file)
@@ -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"
index 25295fa6326dc9716dd51800a517a12920c80ecd..3957bc8abf679c8099e2d596587b468228241b4a 100644 (file)
@@ -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>
index fb3f3c332ffd52c9a33a7f1bcf5a3e410eb21982..23fdab9450ce85c8b6840e782c4ff4750480a21c 100644 (file)
@@ -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"
index e631ebec9cdb7e422c9e83ff905f53c1604b012c..0ba24c978b18f8654f1f586444c9f9dc605d7751 100644 (file)
@@ -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"
index 4e71d0b2e1c9fc612ef277de1818bb3b62fe7aad..b290884617202f9ceb397b728d01e00e6b113184 100644 (file)
@@ -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>
index 3614ee4ae172ac24856fb0c6ebe6d67b1629d062..59357a4ef1e50307272fc7c3ec957cb1bb6b441e 100644 (file)
@@ -184,12 +184,14 @@ static struct symbol_table misc_syms = {
 #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)
 {
index a3ce2e356cb9b2f77133fca7300c54da5e67c8b4..50c17d07b60101558c4e775c62e60b486df952d3 100644 (file)
@@ -1940,9 +1940,6 @@ int tty_init(void)
 #endif
 #ifdef CONFIG_RISCOM8
        riscom8_init();
-#endif
-#ifdef CONFIG_BAYCOM
-       baycom_init();
 #endif
        pty_init();
        vcs_init();
index 623190a73107de709a4ec463612dc844c89f1e4b..04415c79717e26299a7f35f6b1e29e63583b5815 100644 (file)
  *
  */
 
+#include <asm/uaccess.h>
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
@@ -915,7 +916,7 @@ void isdn_info_update(void)
        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 @@ static int isdn_read(struct inode *inode, struct file *file, char *buf, int coun
        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;
index ad4e4c827d13968c64883550b07c99bcb8d7edbc..99b3ae746b2a8a84a0f7612282ebe107d6907afb 100644 (file)
  *
  */
 
+#include <asm/uaccess.h>
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
index cfe209f6a7fe6d63a1885e28e939519728f83fcb..6d678ed516d55aba2d72beca28de943ffd3f594b 100644 (file)
@@ -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 @@ int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long a
        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 @@ int isdn_ppp_read(int min, struct file *file, char *buf, int count)
 {
        struct ippp_struct *is;
        struct ippp_buf_queue *b;
-       int r;
        unsigned long flags;
 
        is = file->private_data;
@@ -1445,7 +1445,6 @@ static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *de
 {
        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 @@ static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *de
 
 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;
index 28e47c11ec2b3bc6cf03645a6c1b01e2f4032dee..7a10b5ceeb4bdb4b30845c18c177d7cc9521537c 100644 (file)
  *
  */
 
+#include <asm/uaccess.h>
 #define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
index 660db962c9092484a316c1d5dd87f892f6c81af9..ead53cb36c66ef877a7bfb44f7012e75f8765d73 100644 (file)
@@ -47,6 +47,7 @@
  */
 #define __NO_VERSION__
 #include "teles.h"
+#include <asm/uaccess.h>
 
 extern struct IsdnCard cards[];
 extern int      nrcards;
index d508a3c612c77e8982cf8660ac46c037a5dec8cc..692f99f679e61696376f1eb75fa3dd45f48d3e82 100644 (file)
@@ -25,7 +25,8 @@ fi
 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
index 8083084b22a5f0787e53af33764e598116f620dd..a8b3e3c6b4f7482079936cafd1dd59d6146d3b78 100644 (file)
@@ -18,6 +18,8 @@ CONFIG_8390_BUILTIN :=
 CONFIG_8390_MODULE  :=
 CONFIG_SLHC_BUILTIN :=
 CONFIG_SLHC_MODULE  :=
+CONFIG_HDLCDRV_BUILTIN :=
+CONFIG_HDLCDRV_MODULE  :=
 
 ifeq ($(CONFIG_ISDN),y)
   ifeq ($(CONFIG_ISDN_PPP),y)
@@ -518,6 +520,37 @@ else
   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
 
 clean:
diff --git a/drivers/net/README.baycom b/drivers/net/README.baycom
new file mode 100644 (file)
index 0000000..cdb0a9a
--- /dev/null
@@ -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 --git a/drivers/net/README.soundmodem b/drivers/net/README.soundmodem
new file mode 100644 (file)
index 0000000..7132b48
--- /dev/null
@@ -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 --git a/drivers/net/baycom.c b/drivers/net/baycom.c
new file mode 100644 (file)
index 0000000..9ae769a
--- /dev/null
@@ -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 */
+/* --------------------------------------------------------------------- */
index 5d54454b0b1c0214f2826f6be86a1e93de3cd5f6..2e4de8a970dd0ba1a5332f826a043d893c531b48 100644 (file)
@@ -128,6 +128,7 @@ static const char *version =
 #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 --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c
new file mode 100644 (file)
index 0000000..c43808f
--- /dev/null
@@ -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 */
+/* --------------------------------------------------------------------- */
index 3bef372c2c215c91079c28618d9e205b07393f90..bfb82472d6326e45354249e7fe1e31ed90d95026 100644 (file)
@@ -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"
                  * 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!
 
 #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
 /* ----------------------------------------------------------------------- */
 
 #include <linux/module.h>
-#include <linux/config.h>
-
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #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 @@ scc_lock_dev(struct scc_channel *scc)
 static __inline__ void
 scc_unlock_dev(struct scc_channel *scc)
 {
-       scc->tx_next_buff = NULL;
        scc->dev->tbusy = 0;
 }
 
@@ -327,12 +326,9 @@ scc_discard_buffers(struct scc_channel *scc)
                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 @@ scc_txint(struct scc_channel *scc)
        
        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 @@ scc_txint(struct scc_channel *scc)
                        Outb(scc->ctrl, RES_Tx_P);
                        return;
                }
-               
-               scc_unlock_dev(scc);
 
                scc->stat.tx_state = TXS_ACTIVE;
 
@@ -1150,7 +1145,7 @@ t_dwait(unsigned long channel)
        
        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 @@ scc_net_open(struct device *dev)
 
        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 @@ scc_net_tx(struct sk_buff *skb, struct device *dev)
        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 @@ scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
        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_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
                                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 --git a/drivers/net/sm_tables.h b/drivers/net/sm_tables.h
new file mode 100644 (file)
index 0000000..76444f7
--- /dev/null
@@ -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 --git a/drivers/net/soundmodem.c b/drivers/net/soundmodem.c
new file mode 100644 (file)
index 0000000..c5e4388
--- /dev/null
@@ -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 */
+/* --------------------------------------------------------------------- */
index 822bf129e79fae8df374e8468749d0df8a889252..7eb8adbe83d03e2c86bdf56c3de8e16bd8a01515 100644 (file)
@@ -1133,6 +1133,25 @@ static void set_multicast_list(struct device *dev)
                } 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);
+               }
        }
 }
 
index 11de4bde37d1551f2a1cd2772ed2109930309387..0c5240e80b5b72c12dcae946ba1fd3e0ec48d498 100644 (file)
@@ -336,11 +336,11 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
        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 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     {
        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
index 0fa74718cf38c8d35623a0b0243c9672f6f0907c..a77fe820f8c71f47de260297cf8aafe92e62319f 100644 (file)
@@ -224,12 +224,6 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #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,
@@ -288,6 +282,12 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #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,
 #endif
index dc0d8df427c34c25a0ada7aec5b25f0eb3c9703d..b00a41c818fabdfb0f760b40884152449936ab38 100644 (file)
@@ -1799,7 +1799,8 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
        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",
index 32dd02225596fecb0eaaefdd9ec48a86e611ee0f..aca3e287ac9ef9bcad7d04f99d39e63bc6738438 100644 (file)
@@ -25,27 +25,18 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
                                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 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
 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 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
                        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 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 
                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
index d564bf31528bf36ec3d92ec018b92b6998121201..f76c2c6661fa614f438773b0acd729eead973877 100644 (file)
@@ -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>
index 3665858b575bcbfae9b20d1d58b3c02b0aeccdb4..443c2d17651656880575a81c305c47ba41265209 100644 (file)
@@ -146,6 +146,8 @@ void remove_vfsmnt(kdev_t dev)
                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));
index 360a30535bb5e9dee835b358664842d2cee092d0..f113dd53529c7709da7c75b7ceac19aa01e0539c 100644 (file)
 #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 @@ struct thread_struct {
        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 @@ struct thread_struct {
        0, 0, 0, \
        0, 0, 0, \
        0, \
-       { 0 } \
+       0 \
 }
 
 #define alloc_kernel_stack()    __get_free_page(GFP_KERNEL)
index 4bc459aa93560ce35f7785a84a6bcf793e845479..43caa22984738026e7ec2625715d5f29aca2a18a 100644 (file)
@@ -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
  * 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 @@ static inline unsigned long get_ds(void)
  *  - 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 @@ static inline unsigned long get_ds(void)
  * (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 @@ extern void __get_user_unknown(void);
        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,94 +212,148 @@ extern void __put_user_unknown(void);
 ({                                                             \
        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);
 
@@ -309,13 +362,10 @@ extern void __clear_user(void);
        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 @@ extern void __clear_user(void);
 /* 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 */
index 7d082b7dbf02c565c8561a2619e08cb180d70b28..4ec40a1d06c960c5eeb5cbf92de2bdc02eaa1acc 100644 (file)
@@ -40,16 +40,6 @@ extern int EISA_bus;
  */
 #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.
  */
@@ -120,7 +110,6 @@ struct thread_struct {
        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 @@ struct thread_struct {
        _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)
index 576c7bfa2b7a0d49c10aa0647f572f9b544ab8b5..d6b42b70c3079e77d15c1ca5407f2c0c3468ccfb 100644 (file)
@@ -5,7 +5,6 @@
  * User space memory access functions
  */
 #include <linux/sched.h>
-
 #include <asm/segment.h>
 
 #define VERIFY_READ 0
  * 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;
+}
+
 
 /*
- * Uh, these should become the main single-value transfer routines..
- * They automatically use the right size if we just have the right
- * pointer type..
+ * 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);
+
+
+/*
+ * 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 @@ __access_ok((type),(unsigned long)(addr),(size))
  * 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))))
+  __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+
+#define __get_user(x,ptr) \
+  __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
 #define __put_user(x,ptr) \
-__do_put_user_nocheck((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr))))
+  __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+
+
+extern long __put_user_bad(void);
+
+#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)
 
 struct __large_struct { unsigned long buf[100]; };
 #define __m(x) (*(struct __large_struct *)(x))
 
-#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;
-}
-
-#define __do_put_user_nocheck(x, ptr, size) \
-({ int retval; __put_user_size(x,ptr,size,retval); retval; })
+/*
+ * 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 get_user(x,ptr) \
-__do_get_user((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
 
-#define __get_user(x,ptr) \
-__do_get_user_nocheck((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
-
-#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;
-
-       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;
-}
+/*
+ * Copy To/From Userspace
+ */
 
-static inline unsigned long __generic_copy_to_user(void *to, const void *from, unsigned long n)
+/* 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)
 {
        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)))
 
-extern inline int verify_area(int type, const void * addr, unsigned long size)
+#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)))
+
+
+/*
+ * 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)
 {
-       return access_ok(type,addr,size)?0:-EFAULT;
+       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)
+{
+       long res = -EFAULT;
+       if (access_ok(VERIFY_READ, src, 1))
+               __strncpy_from_user(dst, src, count, res);
+       return res;
+}
+
+
 #endif /* __i386_UACCESS_H */
index 4a727e144d4070b764006e0dd531306f550572b5..b4108d1dfdcb31817a3fb9c9dee91589f969b0e8 100644 (file)
@@ -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'
-
-#define KISS_FEND   ((unsigned char)0300)
-#define KISS_FESC   ((unsigned char)0333)
-#define KISS_TFEND  ((unsigned char)0334)
-#define KISS_TFESC  ((unsigned char)0335)
+struct baycom_modem_type {
+       unsigned char modem_type;
+       unsigned int options;
+};
 
-#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 @@ struct baycom_params {
  */
 #define BAYCOM_OPTIONS_SOFTDCD  1
 
-
-/*
- * ioctl constants
- */
-#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
+ * ioctl values change for baycom_net
  */
-#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 --git a/include/linux/hdlcdrv.h b/include/linux/hdlcdrv.h
new file mode 100644 (file)
index 0000000..82a35d0
--- /dev/null
@@ -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 */
+
+/* -------------------------------------------------------------------- */
index d9b59b045b61e012357318ec6fdd615ed9670851..77cd07c8f672561b698c2a493900df85999b4325 100644 (file)
@@ -33,7 +33,6 @@ enum {
        DIGI_BH,
        SERIAL_BH,
        RISCOM8_BH,
-       BAYCOM_BH,
        NET_BH,
        IMMEDIATE_BH,
        KEYBOARD_BH,
index 5a5c9035934a5d7ce83ed20fe95716659375f002..2777a7191e69e76f3fdadfa0d1c47c47f4633757 100644 (file)
@@ -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>
index bda966f520ae13abf4a4ad6a16e18c5f6b913901..330143da4a67eeb66e94df42136c2cfe74fa5ebd 100644 (file)
@@ -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 */
 #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)
 #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 @@ struct scc_mem_config {
 };
 
 
+#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_channel {
         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 @@ struct scc_channel {
 };
 
 int scc_init(void);
-#endif
+#endif /* defined(__KERNEL__) */
+#endif /* defined(_SCC_H) */
diff --git a/include/linux/soundmodem.h b/include/linux/soundmodem.h
new file mode 100644 (file)
index 0000000..0bea35f
--- /dev/null
@@ -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 */
+
+/* --------------------------------------------------------------------- */
index 6c9fd350f5b778c15f894f63c2c7c9d7a9c5f8b5..2d6602982d6d4a99ed24bc5c4b5db574fa0f42da 100644 (file)
@@ -292,7 +292,6 @@ extern int cy_init(void);
 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);
index b81311860f882b43143988384272a49b870e2b42..733e28d0be3280c60c0e92532723c4570e5bbd08 100644 (file)
@@ -172,6 +172,9 @@ extern void riscom8_setup(char *str, int *ints);
 #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 @@ struct {
 #endif
 #ifdef CONFIG_BAYCOM
        { "baycom=", baycom_setup },
+#endif
+#ifdef CONFIG_SOUNDMODEM
+       { "soundmodem=", sm_setup },
 #endif
        { 0, 0 }
 };
index b0f341656298574c9156103a93c8191bd1fe1781..830551ebd5e1b8318e92db3c35b2e5ee98ca0db0 100644 (file)
@@ -68,9 +68,7 @@ static inline void wait_for_packet(struct sock * sk)
  
 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 @@ void skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to,
 
 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 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait)
                        }
                        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;
 }
index 8be8425724403f4ab9209896635d31800e2cb1b7..b79d4a1f813a9e907b0dce5130febf203b1d4f9e 100644 (file)
@@ -1369,6 +1369,9 @@ extern int pi_init(void);
 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 @@ int net_dev_init(void)
 #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