diff -u --recursive --new-file v2.1.6/linux/CREDITS linux/CREDITS --- v2.1.6/linux/CREDITS Tue Oct 29 19:58:01 1996 +++ linux/CREDITS Fri Nov 1 13:04:40 1996 @@ -668,7 +668,7 @@ S: USA N: Bernhard Kaindl -E: bartelt@computerhaus.at +E: edv@bartelt.via.at D: Author of a menu based configuration tool, kmenu, which D: is the predecessor of 'make menuconfig' and 'make xconfig'. S: Tallak 95 @@ -1161,7 +1161,8 @@ N: Thomas Sailer E: sailer@ife.ee.ethz.ch -D: Baycom radio modem driver +E: HB9JNX@HB9W.CHE.EU (packet radio) +D: Baycom and Soundcard radio modem driver S: Weinbergstrasse 76 S: 8408 Winterthur S: Switzerland diff -u --recursive --new-file v2.1.6/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.6/linux/Documentation/Configure.help Tue Oct 29 19:58:01 1996 +++ linux/Documentation/Configure.help Wed Oct 30 14:15:11 1996 @@ -1875,17 +1875,29 @@ running kernel whenever you want), say M here and read Documentation/modules.txt. -BAYCOM ser12 and par96 kiss emulation driver for AX.25 +BAYCOM ser12 and par96 driver for AX.25 CONFIG_BAYCOM This is an experimental driver for Baycom style simple amateur radio modems that connect to either a serial interface or a parallel interface. The driver supports the ser12 and par96 designs. To - configure the driver, use the setbaycom utility available from - http://www.ife.ee.ethz.ch/~sailer/ham/ham.html#lnxbay. For - information on the modems, see http://www.baycom.de and - drivers/char/README.baycom. If you want to compile this as a module - ( = code which can be inserted in and removed from the running - kernel whenever you want), say M here and read + configure the driver, use the sethdlc utility available + in the standard ax25 utilities package. For information on the modems, + see http://www.baycom.de and drivers/net/README.baycom. + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. This is recommended. + +Soundcard modem driver for AX.25 +CONFIG_SOUNDMODEM + This experimental driver allows a standard SoundBlaster or + WindowsSoundSystem compatible soundcard to be used as a packet radio + modem. To configure the driver, use the sethdlc, smdiag and smmixer + utilities available in the standard ax25 utilities package. For + informations on how to key the transmitter, see + http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html and + drivers/net/README.soundmodem. If you want to compile this as a + module ( = code which can be inserted in and removed from the + running kernel whenever you want), say M here and read Documentation/modules.txt. This is recommended. PLIP (parallel port) support diff -u --recursive --new-file v2.1.6/linux/Makefile linux/Makefile --- v2.1.6/linux/Makefile Tue Oct 29 19:58:01 1996 +++ linux/Makefile Tue Oct 29 19:27:13 1996 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 6 +SUBLEVEL = 7 ARCH = i386 diff -u --recursive --new-file v2.1.6/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v2.1.6/linux/arch/alpha/kernel/entry.S Wed Oct 16 10:48:06 1996 +++ linux/arch/alpha/kernel/entry.S Wed Oct 30 18:41:43 1996 @@ -14,7 +14,7 @@ #define osf_vfork sys_fork /* - * These offsets must match with "struct hae" in io.h: + * These offsets must match with "struct hae" in io.h: */ #define HAE_CACHE 0 #define HAE_REG 8 @@ -142,9 +142,29 @@ .ent entMM entMM: SAVE_ALL - lda $27,do_page_fault - lda $26,ret_from_sys_call - jsr $31,($27),do_page_fault +/* save $9 - $15 so the inline exception code can manipulate them. */ + subq $30,56,$30 + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + addq $30,56,$19 +/* handle the fault */ + jsr $26,do_page_fault +/* reload the registers after the exception code played. */ + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + addq $30,56,$30 +/* finish up the syscall as normal. */ + br ret_from_sys_call .end entMM .align 3 @@ -190,7 +210,7 @@ SAVE_ALL lda $27,sys_clone jsr $26,($27),sys_clone - stq $0,0($30) + stq $0,0($30) br $31,ret_from_sys_call .end kernel_clone @@ -424,7 +444,7 @@ /* * A fork is the same as clone(SIGCHLD, 0); -*/ + */ .align 3 .globl sys_fork .ent sys_fork @@ -508,7 +528,7 @@ ldq $4,0($2) addq $1,1,$1 and $3,$4,$2 - bne $2,handle_bottom_half + bne $2,handle_bottom_half ret_from_handle_bh: ldq $0,SP_OFF($30) and $0,8,$0 @@ -529,9 +549,9 @@ restore_all: RESTORE_ALL rti - - -/* PTRACE syscall handler */ + + +/* PTRACE syscall handler */ .align 3 strace: /* set up signal stack, call syscall_trace */ @@ -588,7 +608,7 @@ lda $27,syscall_trace jsr $26,($27),syscall_trace bis $9,$9,$19 - bis $10,$10,$20 + bis $10,$10,$20 bsr $1,undo_switch_stack bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */ diff -u --recursive --new-file v2.1.6/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v2.1.6/linux/arch/alpha/kernel/traps.c Tue Oct 29 19:58:02 1996 +++ linux/arch/alpha/kernel/traps.c Thu Oct 31 13:59:58 1996 @@ -178,63 +178,217 @@ unsigned long count, va, pc; } unaligned[2]; + +/* Macro for exception fixup code to access integer registers. */ +#define una_reg(r) (regs.regs[(r) >= 16 && (r) <= 18 ? (r)+19 : (r)]) + + asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg, unsigned long a3, unsigned long a4, unsigned long a5, struct allregs regs) { static int cnt = 0; static long last_time = 0; + long error, tmp1, tmp2, tmp3, tmp4; + unsigned long pc = regs.pc - 4; + unsigned fixup; if (cnt >= 5 && jiffies - last_time > 5*HZ) { cnt = 0; } if (++cnt < 5) { printk("kernel: unaligned trap at %016lx: %p %lx %ld\n", - regs.pc - 4, va, opcode, reg); + pc, va, opcode, reg); } last_time = jiffies; - ++unaligned[0].count; - unaligned[0].va = (unsigned long) va - 4; - unaligned[0].pc = regs.pc; - - /* $16-$18 are PAL-saved, and are offset by 19 entries */ - if (reg >= 16 && reg <= 18) - reg += 19; - - { - /* Set up an exception handler address just in case we are - handling an unaligned fixup within get_user(). Notice - that we do *not* change the exception count because we - only want to bounce possible exceptions on through. */ - - __label__ handle_ex; - register void *ex_vector __asm__("$28"); - __asm__ __volatile__ ("" : "=r"(ex_vector) : "0"(&&handle_ex)); - - switch (opcode) { - case 0x28: /* ldl */ - *(reg+regs.regs) = get_unaligned((int *)va); - return; - case 0x29: /* ldq */ - *(reg+regs.regs) = get_unaligned((long *)va); - return; - case 0x2c: /* stl */ - put_unaligned(*(reg+regs.regs), (int *)va); - return; - case 0x2d: /* stq */ - put_unaligned(*(reg+regs.regs), (long *)va); - return; - - /* We'll only get back here if we are handling a - valid exception. */ - handle_ex: - (®s)->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); + (®s)->pc = newpc; + return; + } + + /* Yikes! No one to forward the exception to. */ + printk("%s: unhandled unaligned exception at pc=%lx ra=%lx" + " (bad address = %p)\n", current->comm, + pc, una_reg(26), va); do_exit(SIGSEGV); } diff -u --recursive --new-file v2.1.6/linux/arch/alpha/lib/clear_user.S linux/arch/alpha/lib/clear_user.S --- v2.1.6/linux/arch/alpha/lib/clear_user.S Sat Oct 19 10:07:28 1996 +++ linux/arch/alpha/lib/clear_user.S Thu Oct 31 13:59:59 1996 @@ -25,6 +25,14 @@ * $1,$2,$3,$4,$5,$6 */ +/* Allow an exception for an insn; exit if we get one. */ +#define EX(x,y...) \ + 99: x,##y; \ + .section __ex_table,"a"; \ + .gprel32 99b; \ + lda $31, $exception-99b($31); \ + .text + .set noat .set noreorder .align 4 @@ -38,7 +46,7 @@ and $1, 3, $4 # e0 : beq $4, 1f # .. e1 : -0: stq_u $31, 0($6) # e0 : zero one word +0: EX( stq_u $31, 0($6) ) # e0 : zero one word subq $0, 8, $0 # .. e1 : subq $4, 1, $4 # e0 : addq $6, 8, $6 # .. e1 : @@ -48,13 +56,13 @@ 1: bic $1, 3, $1 # e0 : beq $1, $tail # .. e1 : -2: stq_u $31, 0($6) # e0 : zero four words +2: EX( stq_u $31, 0($6) ) # e0 : zero four words subq $0, 8, $0 # .. e1 : - stq_u $31, 8($6) # e0 : + EX( stq_u $31, 8($6) ) # e0 : subq $0, 8, $0 # .. e1 : - stq_u $31, 16($6) # e0 : + EX( stq_u $31, 16($6) ) # e0 : subq $0, 8, $0 # .. e1 : - stq_u $31, 24($6) # e0 : + EX( stq_u $31, 24($6) ) # e0 : subq $0, 8, $0 # .. e1 : subq $1, 4, $1 # e0 : addq $6, 32, $6 # .. e1 : @@ -62,35 +70,29 @@ $tail: bne $2, 1f # e1 : is there a tail to do? - - stq $3, 0($7) # e0 : decrement exception count ret $31, ($28), 1 # .. e1 : -1: ldq_u $5, 0($6) # e1 : - mskqh $5, $0, $5 # e0 : - stq_u $5, 0($6) # e0 : +1: EX( ldq_u $5, 0($6) ) # e0 : clr $0 # .. e1 : - stq $3, 0($7) # e0 : decrement exception count + nop # e1 : + mskqh $5, $0, $5 # e0 : + EX( stq_u $5, 0($6) ) # e0 : ret $31, ($28), 1 # .. e1 : __clear_user: - ldq $3, 0($7) # e0 : load exception count for increment - beq $0, $zerolength # .. e1 : and $6, 7, $4 # e0 : find dest misalignment - addq $0, $4, $1 # e1 : bias counter - addq $3, 1, $5 # e0 : - and $1, 7, $2 # .. e1 : number of bytes in tail + beq $0, $zerolength # .. e1 : + addq $0, $4, $1 # e0 : bias counter + and $1, 7, $2 # e1 : number of bytes in tail srl $1, 3, $1 # e0 : - unop # : - stq $5, 0($7) # e0 : increment exception count beq $4, $loop # .. e1 : - ldq_u $5, 0($6) # e0 : load dst word to mask back in + EX( ldq_u $5, 0($6) ) # e0 : load dst word to mask back in beq $1, $oneword # .. e1 : sub-word store? mskql $5, $6, $5 # e0 : take care of misaligned head addq $6, 8, $6 # .. e1 : - stq_u $5, -8($6) # e0 : + EX( stq_u $5, -8($6) ) # e0 : addq $0, $4, $0 # .. e1 : bytes left -= 8 - misalignment subq $1, 1, $1 # e0 : subq $0, 8, $0 # .. e1 : @@ -101,11 +103,11 @@ mskql $5, $6, $4 # e0 : mskqh $5, $2, $5 # e0 : or $5, $4, $5 # e1 : - stq_u $5, 0($6) # e0 : + EX( stq_u $5, 0($6) ) # e0 : clr $0 # .. e1 : - stq $3, 0($7) # e0 : decrement exception count $zerolength: +$exception: ret $31, ($28), 1 # .. e1 : .end __clear_user diff -u --recursive --new-file v2.1.6/linux/arch/alpha/lib/copy_user.S linux/arch/alpha/lib/copy_user.S --- v2.1.6/linux/arch/alpha/lib/copy_user.S Wed Oct 16 10:48:06 1996 +++ linux/arch/alpha/lib/copy_user.S Thu Oct 31 13:59:59 1996 @@ -18,7 +18,7 @@ * destination address in $6 * source address in $7 * exception pointer in $8 - * return address in $28 (exceptions expect it there) + * return address in $28 * * Outputs: * bytes left to copy in $0 @@ -27,28 +27,33 @@ * $1,$2,$3,$4,$5,$6,$7 */ +/* Allow an exception for an insn; exit if we get one. */ +#define EX(x,y...) \ + 99: x,##y; \ + .section __ex_table,"a"; \ + .gprel32 99b; \ + lda $31, $exit-99b($31); \ + .text + .set noat .align 3 .globl __copy_user .ent __copy_user __copy_user: - ldq $5,0($8) - beq $0,$35 and $6,7,$3 - addq $5,1,$1 - stq $1,0($8) + beq $0,$35 beq $3,$36 subq $3,8,$3 .align 5 $37: - ldq_u $1,0($7) - ldq_u $2,0($6) + EX( ldq_u $1,0($7) ) + EX( ldq_u $2,0($6) ) extbl $1,$7,$1 mskbl $2,$6,$2 insbl $1,$6,$1 addq $3,1,$3 bis $1,$2,$1 - stq_u $1,0($6) + EX( stq_u $1,0($6) ) subq $0,1,$0 addq $6,1,$6 addq $7,1,$7 @@ -59,10 +64,10 @@ bic $0,7,$4 beq $1,$43 beq $4,$48 - ldq_u $3,0($7) + EX( ldq_u $3,0($7) ) .align 5 $50: - ldq_u $2,8($7) + EX( ldq_u $2,8($7) ) subq $4,8,$4 extql $3,$7,$3 extqh $2,$7,$1 @@ -77,13 +82,13 @@ beq $0,$41 .align 5 $57: - ldq_u $1,0($7) - ldq_u $2,0($6) + EX( ldq_u $1,0($7) ) + EX( ldq_u $2,0($6) ) extbl $1,$7,$1 mskbl $2,$6,$2 insbl $1,$6,$1 bis $1,$2,$1 - stq_u $1,0($6) + EX( stq_u $1,0($6) ) subq $0,1,$0 addq $6,1,$6 addq $7,1,$7 @@ -93,9 +98,8 @@ $43: beq $4,$65 .align 5 - .align 5 $66: - ldq $1,0($7) + EX( ldq $1,0($7) ) subq $4,8,$4 stq $1,0($6) addq $7,8,$7 @@ -104,15 +108,15 @@ bne $4,$66 $65: beq $0,$41 - ldq $2,0($7) - ldq $1,0($6) + EX( ldq $2,0($7) ) + EX( ldq $1,0($6) ) mskql $2,$0,$2 mskqh $1,$0,$1 bis $2,$1,$2 - stq $2,0($6) + EX( stq $2,0($6) ) bis $31,$31,$0 $41: - stq $5,0($8) $35: +$exit: ret $31,($28),1 .end __copy_user diff -u --recursive --new-file v2.1.6/linux/arch/alpha/lib/strncpy_from_user.S linux/arch/alpha/lib/strncpy_from_user.S --- v2.1.6/linux/arch/alpha/lib/strncpy_from_user.S Sat Oct 19 10:07:28 1996 +++ linux/arch/alpha/lib/strncpy_from_user.S Thu Oct 31 13:59:59 1996 @@ -6,59 +6,334 @@ * * -EFAULT if an exception occurs before the terminator is copied. * N if the buffer filled. - * - * Otherwise the length of the string is returned. * - * Additionally, the fourth argument should be `¤t->tss.ex'. + * Otherwise the length of the string is returned. */ + #include +#include + + +/* Allow an exception for an insn; exit if we get one. */ +#define EX(x,y...) \ + 99: x,##y; \ + .section __ex_table,"a"; \ + .gprel32 99b; \ + lda $31, $exception-99b($0); \ + .text + .set noat .set noreorder - .text - .align 3 .globl __strncpy_from_user .ent __strncpy_from_user + .frame $30, 0, $26 + + .align 3 +$aligned: + /* On entry to this basic block: + t0 == the first destination word for masking back in + t1 == the first source word. */ + + /* Create the 1st output word and detect 0's in the 1st input word. */ + lda t2, -1 # e1 : build a mask against false zero + mskqh t2, a1, t2 # e0 : detection in the src word + mskqh t1, a1, t3 # e0 : + ornot t1, t2, t2 # .. e1 : + mskql t0, a1, t0 # e0 : assemble the first output word + cmpbge zero, t2, t7 # .. e1 : bits set iff null found + or t0, t3, t0 # e0 : + beq a2, $a_eoc # .. e1 : + bne t7, $a_eos # .. e1 : + + /* On entry to this basic block: + t0 == a source word not containing a null. */ + +$a_loop: + stq_u t0, 0(a0) # e0 : + addq a0, 8, a0 # .. e1 : + EX( ldq_u t0, 0(a1) ) # e0 : + addq a1, 8, a1 # .. e1 : + subq a2, 1, a2 # e0 : + cmpbge zero, t0, t7 # .. e1 (stall) + beq a2, $a_eoc # e1 : + beq t7, $a_loop # e1 : + + /* Take care of the final (partial) word store. At this point + the end-of-count bit is set in t7 iff it applies. + + On entry to this basic block we have: + t0 == the source word containing the null + t7 == the cmpbge mask that found it. */ + +$a_eos: + negq t7, t8 # e0 : find low bit set + and t7, t8, t8 # e1 (stall) + + /* For the sake of the cache, don't read a destination word + if we're not going to need it. */ + and t8, 0x80, t6 # e0 : + bne t6, 1f # .. e1 (zdb) + + /* We're doing a partial word store and so need to combine + our source and original destination words. */ + ldq_u t1, 0(a0) # e0 : + subq t8, 1, t6 # .. e1 : + or t8, t6, t7 # e0 : + unop # + zapnot t0, t7, t0 # e0 : clear src bytes > null + zap t1, t7, t1 # .. e1 : clear dst bytes <= null + or t0, t1, t0 # e1 : + +1: stq_u t0, 0(a0) + br $finish_up + + /* Add the end-of-count bit to the eos detection bitmask. */ +$a_eoc: + or t10, t7, t7 + br $a_eos + /*** The Function Entry Point ***/ + .align 3 __strncpy_from_user: - .frame $30, 0, $26 - .prologue 0 - ldq $20, 0($19) - beq $18, 9f - br $28, 1f # set up exception return address + mov a0, v0 # save the string start + beq a2, $zerolength - lda $0, -EFAULT - ret + /* Are source and destination co-aligned? */ + xor a0, a1, t1 # e0 : + and a0, 7, t0 # .. e1 : find dest misalignment + and t1, 7, t1 # e0 : + addq a2, t0, a2 # .. e1 : bias count by dest misalignment + subq a2, 1, a2 # e0 : + and a2, 7, t2 # e1 : + srl a2, 3, a2 # e0 : a2 = loop counter = (count - 1)/8 + addq zero, 1, t10 # .. e1 : + sll t10, t2, t10 # e0 : t10 = bitmask of last count byte + bne t1, $unaligned # .. e1 : + + /* We are co-aligned; take care of a partial first word. */ + + EX( ldq_u t1, 0(a1) ) # e0 : load first src word + addq a1, 8, a1 # .. e1 : + + beq t0, $aligned # avoid loading dest word if not needed + ldq_u t0, 0(a0) # e0 : + br $aligned # .. e1 : + + +/* The source and destination are not co-aligned. Align the destination + and cope. We have to be very careful about not reading too much and + causing a SEGV. */ + + .align 3 +$u_head: + /* We know just enough now to be able to assemble the first + full source word. We can still find a zero at the end of it + that prevents us from outputting the whole thing. + + On entry to this basic block: + t0 == the first dest word, unmasked + t1 == the shifted low bits of the first source word + t6 == bytemask that is -1 in dest word bytes */ + + EX( ldq_u t2, 8(a1) ) # e0 : load second src word + addq a1, 8, a1 # .. e1 : + mskql t0, a0, t0 # e0 : mask trailing garbage in dst + extqh t2, a1, t4 # e0 : + or t1, t4, t1 # e1 : first aligned src word complete + mskqh t1, a0, t1 # e0 : mask leading garbage in src + or t0, t1, t0 # e0 : first output word complete + or t0, t6, t6 # e1 : mask original data for zero test + cmpbge zero, t6, t7 # e0 : + beq a2, $u_eocfin # .. e1 : + bne t7, $u_final # e1 : + + lda t6, -1 # e1 : mask out the bits we have + mskql t6, a1, t6 # e0 : already seen + stq_u t0, 0(a0) # e0 : store first output word + or t6, t2, t2 # .. e1 : + cmpbge zero, t2, t7 # e0 : find nulls in second partial + addq a0, 8, a0 # .. e1 : + subq a2, 1, a2 # e0 : + bne t7, $u_late_head_exit # .. e1 : + + /* Finally, we've got all the stupid leading edge cases taken care + of and we can set up to enter the main loop. */ + + extql t2, a1, t1 # e0 : position hi-bits of lo word + EX( ldq_u t2, 8(a1) ) # .. e1 : read next high-order source word + addq a1, 8, a1 # e0 : + cmpbge zero, t2, t7 # e1 (stall) + beq a2, $u_eoc # e1 : + bne t7, $u_eos # e1 : + + /* Unaligned copy main loop. In order to avoid reading too much, + the loop is structured to detect zeros in aligned source words. + This has, unfortunately, effectively pulled half of a loop + iteration out into the head and half into the tail, but it does + prevent nastiness from accumulating in the very thing we want + to run as fast as possible. + + On entry to this basic block: + t1 == the shifted high-order bits from the previous source word + t2 == the unshifted current source word + + We further know that t2 does not contain a null terminator. */ + + .align 3 +$u_loop: + extqh t2, a1, t0 # e0 : extract high bits for current word + addq a1, 8, a1 # .. e1 : + extql t2, a1, t3 # e0 : extract low bits for next time + addq a0, 8, a0 # .. e1 : + or t0, t1, t0 # e0 : current dst word now complete + EX( ldq_u t2, 0(a1) ) # .. e1 : load high word for next time + stq_u t0, -8(a0) # e0 : save the current word + mov t3, t1 # .. e1 : + subq a2, 1, a2 # e0 : + cmpbge zero, t2, t7 # .. e1 : test new word for eos + beq a2, $u_eoc # e1 : + beq t7, $u_loop # e1 : + + /* We've found a zero somewhere in the source word we just read. + If it resides in the lower half, we have one (probably partial) + word to write out, and if it resides in the upper half, we + have one full and one partial word left to write out. + + On entry to this basic block: + t1 == the shifted high-order bits from the previous source word + t2 == the unshifted current source word. */ +$u_eos: + extqh t2, a1, t0 # e0 : + or t0, t1, t0 # e1 : first (partial) source word complete + + cmpbge zero, t0, t7 # e0 : is the null in this first bit? + bne t7, $u_final # .. e1 (zdb) + + stq_u t0, 0(a0) # e0 : the null was in the high-order bits + addq a0, 8, a0 # .. e1 : + subq a2, 1, a2 # e1 : + +$u_late_head_exit: + extql t2, a1, t0 # .. e0 : + cmpbge zero, t0, t7 # e0 : + or t7, t10, t6 # e1 : + cmoveq a2, t6, t7 # e0 : + nop # .. e1 : + + /* Take care of a final (probably partial) result word. + On entry to this basic block: + t0 == assembled source word + t7 == cmpbge mask that found the null. */ +$u_final: + negq t7, t6 # e0 : isolate low bit set + and t6, t7, t8 # e1 : + + and t8, 0x80, t6 # e0 : avoid dest word load if we can + bne t6, 1f # .. e1 (zdb) + + ldq_u t1, 0(a0) # e0 : + subq t8, 1, t6 # .. e1 : + or t6, t8, t7 # e0 : + zapnot t0, t7, t0 # .. e1 : kill source bytes > null + zap t1, t7, t1 # e0 : kill dest bytes <= null + or t0, t1, t0 # e1 : + +1: stq_u t0, 0(a0) # e0 : + br $finish_up + +$u_eoc: # end-of-count + extqh t2, a1, t0 + or t0, t1, t0 + cmpbge zero, t0, t7 + +$u_eocfin: # end-of-count, final word + or t10, t7, t7 + br $u_final + + /* Unaligned copy entry point. */ + .align 3 +$unaligned: + + EX( ldq_u t1, 0(a1) ) # e0 : load first source word + + and a0, 7, t4 # .. e1 : find dest misalignment + and a1, 7, t5 # e0 : find src misalignment -1: addq $20, 1, $21 - mov $16, $0 # save the string start - stq $21, 0($19) # increment exception count - bsr $23, __stxncpy # do the work of the copy - - zapnot $1, $22, $5 # was last byte written null? - stq $20, 0($19) # decrement exception count - cmovne $5, 1, $5 - - and $22, 0xf0, $4 # binary search for the address of the - and $22, 0xcc, $3 # last byte written - and $22, 0xaa, $2 - bic $16, 7, $1 - cmovne $4, 4, $4 - cmovne $3, 2, $3 - cmovne $2, 1, $2 - addq $1, $4, $1 - addq $2, $3, $2 - addq $1, $2, $1 - addq $1, $5, $1 # add one if we filled the buffer + /* Conditionally load the first destination word and a bytemask + with 0xff indicating that the destination byte is sacrosanct. */ + + mov zero, t0 # .. e1 : + mov zero, t6 # e0 : + beq t4, 1f # .. e1 : + ldq_u t0, 0(a0) # e0 : + lda t6, -1 # .. e1 : + mskql t6, a0, t6 # e0 : +1: + subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr + + /* If source misalignment is larger than dest misalignment, we need + extra startup checks to avoid SEGV. */ + + cmplt t4, t5, t8 # e1 : + extql t1, a1, t1 # .. e0 : shift src into place + lda t2, -1 # e0 : for creating masks later + beq t8, $u_head # e1 : + + mskqh t2, t5, t2 # e0 : begin src byte validity mask + cmpbge zero, t1, t7 # .. e1 : is there a zero? + extql t2, a1, t2 # e0 : + or t7, t10, t6 # .. e1 : test for end-of-count too + cmpbge zero, t2, t3 # e0 : + cmoveq a2, t6, t7 # .. e1 : + andnot t7, t3, t7 # e0 : + beq t7, $u_head # .. e1 (zdb) + + /* At this point we've found a zero in the first partial word of + the source. We need to isolate the valid source data and mask + it into the original destination data. (Incidentally, we know + that we'll need at least one byte of that original dest word.) */ + + ldq_u t0, 0(a0) # e0 : + negq t7, t6 # .. e1 : build bitmask of bytes <= zero + mskqh t1, t4, t1 # e0 : + and t6, t7, t8 # .. e1 : + subq t8, 1, t6 # e0 : + or t6, t8, t7 # e1 : + + zapnot t2, t7, t2 # e0 : prepare source word; mirror changes + zapnot t1, t7, t1 # .. e1 : to source validity mask + + andnot t0, t2, t0 # e0 : zero place for source to reside + or t0, t1, t0 # e1 : and put it there + stq_u t0, 0(a0) # e0 : + +$finish_up: + zapnot t0, t10, t4 # was last byte written null? + cmovne t4, 1, t4 + + and t10, 0xf0, t3 # binary search for the address of the + and t10, 0xcc, t2 # last byte written + and t10, 0xaa, t1 + bic a0, 7, t0 + cmovne t3, 4, t3 + cmovne t2, 2, t2 + cmovne t1, 1, t1 + addq t0, t3, t0 + addq t1, t2, t1 + addq t0, t1, t0 + addq t0, t4, t0 # add one if we filled the buffer - subq $1, $0, $0 # find string length + subq t0, v0, v0 # find string length ret -9: clr $0 +$zerolength: + clr v0 +$exception: ret .end __strncpy_from_user diff -u --recursive --new-file v2.1.6/linux/arch/alpha/mm/Makefile linux/arch/alpha/mm/Makefile --- v2.1.6/linux/arch/alpha/mm/Makefile Tue Aug 15 15:07:02 1995 +++ linux/arch/alpha/mm/Makefile Wed Oct 30 18:41:44 1996 @@ -7,7 +7,7 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -OBJS = init.o fault.o +OBJS = init.o fault.o extable.o mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) diff -u --recursive --new-file v2.1.6/linux/arch/alpha/mm/extable.c linux/arch/alpha/mm/extable.c --- v2.1.6/linux/arch/alpha/mm/extable.c Thu Jan 1 02:00:00 1970 +++ linux/arch/alpha/mm/extable.c Thu Oct 31 13:59:59 1996 @@ -0,0 +1,50 @@ +/* + * linux/arch/alpha/mm/extable.c + */ + +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + signed int value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup.unit; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned +search_exception_table(unsigned long addr) +{ + unsigned ret; + signed int reladdr; + + /* Search the kernel's table first. */ + { + register unsigned long gp __asm__("$29"); + reladdr = addr - gp; + } + ret = search_one_table(__start___ex_table, + __stop___ex_table-1, reladdr); + if (ret) + return ret; + + /* FIXME -- search the module's tables here */ + + return 0; +} diff -u --recursive --new-file v2.1.6/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c --- v2.1.6/linux/arch/alpha/mm/fault.c Tue Oct 29 19:58:02 1996 +++ linux/arch/alpha/mm/fault.c Thu Oct 31 13:59:59 1996 @@ -52,14 +52,24 @@ * -1 = instruction fetch * 0 = load * 1 = store + * + * Registers $9 through $15 are saved in a block just prior to `regs' and + * are saved and restored around the call to allow exception code to + * modify them. */ -asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) + +/* Macro for exception fixup code to access integer registers. */ +#define dpf_reg(r) \ + (((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-16 : \ + (r) <= 18 ? (r)+8 : (r)-10]) + +asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, + long cause, struct pt_regs *regs) { struct vm_area_struct * vma; struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; + unsigned fixup; down(&mm->mmap_sem); vma = find_vma(mm, address); @@ -97,18 +107,21 @@ */ bad_area: up(&mm->mmap_sem); - /* Did we have an exception handler installed? */ - if (current->tss.ex.count == 1) { - printk("Taking exception at %lx (%lx)\n", regs.pc, regs.r28); - current->tss.ex.count = 0; - /* return to the address in r28 */ - (®s)->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(®s)) { - printk("%s: memory violation at pc=%08lx rp=%08lx (bad address = %08lx)\n", - tsk->comm, regs.pc, regs.r26, address); - die_if_kernel("oops", ®s, cause); + + if (user_mode(regs)) { + printk("%s: memory violation at pc=%08lx ra=%08lx " + "(bad address = %08lx)\n", + tsk->comm, regs->pc, regs->r26, address); + die_if_kernel("oops", regs, cause); force_sig(SIGSEGV, tsk); return; } @@ -116,8 +129,8 @@ * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - printk(KERN_ALERT - "Unable to handle kernel paging request at virtual address %016lx\n", address); - die_if_kernel("Oops", ®s, cause); + printk(KERN_ALERT "Unable to handle kernel paging request at " + "virtual address %016lx\n", address); + die_if_kernel("Oops", regs, cause); do_exit(SIGKILL); } diff -u --recursive --new-file v2.1.6/linux/arch/i386/mm/Makefile linux/arch/i386/mm/Makefile --- v2.1.6/linux/arch/i386/mm/Makefile Wed Sep 25 11:46:53 1996 +++ linux/arch/i386/mm/Makefile Fri Nov 1 11:56:43 1996 @@ -8,6 +8,6 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -O_OBJS := init.o fault.o ioremap.o +O_OBJS := init.o fault.o ioremap.o extable.o include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.6/linux/arch/i386/mm/extable.c linux/arch/i386/mm/extable.c --- v2.1.6/linux/arch/i386/mm/extable.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/mm/extable.c Fri Nov 1 11:56:43 1996 @@ -0,0 +1,50 @@ +/* + * linux/arch/i386/mm/extable.c + */ + +#include + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + /* Some versions of the linker are buggy and do not align the + __start pointer along with the section, thus we may be low. */ + if ((long)first & 3) + (long)first = ((long)first | 3) + 1; + + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + + /* Search the kernel's table first. */ + ret = search_one_table(__start___ex_table, + __stop___ex_table-1, addr); + if (ret) + return ret; + + /* FIXME -- search the module's tables here */ + + return 0; +} diff -u --recursive --new-file v2.1.6/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.6/linux/arch/i386/mm/fault.c Tue Oct 29 19:58:02 1996 +++ linux/arch/i386/mm/fault.c Fri Nov 1 11:56:43 1996 @@ -93,6 +93,7 @@ struct vm_area_struct * vma; unsigned long address; unsigned long page; + unsigned long fixup; int write; /* get the address */ @@ -161,14 +162,14 @@ */ bad_area: up(&mm->mmap_sem); - /* is there valid exception data? Return to indicated handler if so */ - if (tsk->tss.ex.count == 0) { - printk("Exception at %lx (%lx)\n", regs->eip, regs->edx); - tsk->tss.ex.count--; - regs->eip = regs->edx; - regs->edx = -EFAULT; + + /* Are we prepared to handle this fault? */ + if ((fixup = search_exception_table(regs->eip)) != 0) { + printk("Exception at %lx (%lx)\n", regs->eip, fixup); + regs->eip = fixup; return; } + if (error_code & 4) { tsk->tss.cr2 = address; tsk->tss.error_code = error_code; diff -u --recursive --new-file v2.1.6/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.1.6/linux/drivers/block/md.c Thu Oct 10 19:10:54 1996 +++ linux/drivers/block/md.c Wed Oct 30 18:25:37 1996 @@ -40,6 +40,7 @@ #define MD_DRIVER #include +#include static struct hd_struct md_hd_struct[MAX_MD_DEV]; static int md_blocksizes[MAX_MD_DEV]; @@ -296,10 +297,9 @@ case BLKGETSIZE: /* Return device size */ if (!arg) return -EINVAL; - err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long)); + err = put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg); if (err) return err; - put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg); break; case BLKFLSBUF: @@ -315,10 +315,9 @@ case BLKRAGET: if (!arg) return -EINVAL; - err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long)); + err = put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg); if (err) return err; - put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg); break; /* We have a problem here : there is no easy way to give a CHS @@ -328,14 +327,19 @@ case HDIO_GETGEO: if (!loc) return -EINVAL; - err = verify_area(VERIFY_WRITE, loc, sizeof(*loc)); + err = put_user (2, (char *) &loc->heads); if (err) return err; - put_user (2, (char *) &loc->heads); - put_user (4, (char *) &loc->sectors); - put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders); - put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect, + err = put_user (4, (char *) &loc->sectors); + if (err) + return err; + err = put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders); + if (err) + return err; + err = put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect, (long *) &loc->start); + if (err) + return err; break; RO_IOCTLS(inode->i_rdev,arg); @@ -367,7 +371,7 @@ } -static int md_read (struct inode *inode, struct file *file, +static long md_read (struct inode *inode, struct file *file, char *buf, int count) { int minor=MINOR(inode->i_rdev); @@ -378,7 +382,7 @@ return block_read (inode, file, buf, count); } -static int md_write (struct inode *inode, struct file *file, +static long md_write (struct inode *inode, struct file *file, const char *buf, int count) { int minor=MINOR(inode->i_rdev); diff -u --recursive --new-file v2.1.6/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c --- v2.1.6/linux/drivers/cdrom/cdrom.c Wed Oct 16 10:48:08 1996 +++ linux/drivers/cdrom/cdrom.c Wed Oct 30 01:56:01 1996 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff -u --recursive --new-file v2.1.6/linux/drivers/char/README.baycom linux/drivers/char/README.baycom --- v2.1.6/linux/drivers/char/README.baycom Sat May 18 11:15:09 1996 +++ linux/drivers/char/README.baycom Thu Jan 1 02:00:00 1970 @@ -1,164 +0,0 @@ - LINUX DRIVER FOR BAYCOM MODEMS - - Thomas M. Sailer - -This document describes the Linux Kernel Driver for simple Baycom style -amateur radio modems. The driver supports the following modems: - -ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - of a modulator/demodulator chip, usually a TI TCM3105. The computer - is responsible for regenerating the receiver bit clock, as well as - for handling the HDLC protocol. The modem connects to a serial port, - hence the name. Since the serial port is not used as an async serial - port, the kernel driver for serial ports cannot be used, and this - driver only supports standard serial hardware (8250, 16450, 16550) - -par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - The modem does all the filtering and regenerates the receiver clock. - Data is transferred from and to the PC via a shift register. - The shift register is filled with 16 bits and an interrupt is signalled. - The PC then empties the shift register in a burst. This modem connects - to the parallel port, hence the name. The modem leaves the - implementation of the HDLC protocol and the scrambler polynomial to - the PC. - -par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem - is protocol compatible to par96, but uses only three low power ICs - and can therefore be fed from the parallel port and does not require - an additional power supply. - -All of the above modems only support half duplex communications. However, -the driver supports the KISS (see below) fullduplex command. It then simply -starts to send as soon as there's a packet to transmit and does not care -about DCD, i.e. it starts to send even if there's someone else on the channel. -This command is required by some implementations of the DAMA channel -access protocol. - - -The Interface of the driver - -The driver interfaces to the AX25 stack via a KISS interface. The driver -can be accessed as a character device with major 60. Major 60 is the first -number of the local/experimental range. I did no steps to coordinate a -major number for this driver, but I plan to do so in the near future. -The driver supports multiple modems (currently four, as defined with -NR_PORTS). It therefore responds to minor numbers 0 to 3. I recommend -to access the driver via the special device files /dev/bc[0-3], which -can be created with 'make bc'. - - -Compiling and installing the driver - -First unpack the source files into a directory. Then enter the following: (you -must be root to do it) - - make dep - make - -This will create the files baycom.o and setbaycom. baycom.o is as well copied -to /lib/modules/`uname -n`/misc. If you plan to use kerneld, do the following: - - depmod -a - -Do not forget to create the device special files if you install the driver the -first time. This can be done with: - - make bc - -You are now ready to use the driver. You can now activate the driver manually -by entering - - insmod baycom - -or leave this task to kerneld (if installed). Add the following line to -/etc/conf.modules - - alias char-major-60 baycom - - - -Configuring the driver - -Every time the driver is inserted into the kernel, it has to know which -modems it should access at which ports. This can be done with the setbaycom -utility. If you are only using one modem, you can also configure the -driver from the insmod command line (or by means of an option line in -/etc/conf.modules). - -Examples: - insmod baycom modem=1 iobase=0x3f8 irq=4 options=1 - setbaycom -i /dev/bc0 -p ser12 0x3f8 4 1 - -Both lines configure the first port to drive a ser12 modem at the first -serial port (COM1 under DOS). options=1 instructs the driver to use -the software DCD algorithm (see below). - - insmod baycom modem=2 iobase=0x378 irq=7 options=1 - setbaycom -i /dev/bc0 -p par96 0x378 7 1 - -Both lines configure the first port to drive a par96 or par97 modem at the -first parallel port (LPT1 under DOS). options=1 instructs the driver to use -the software DCD algorithm (see below). - -The channel access parameters must be set through KISS parameter frames. The -AX25 stack may be used to generate such frames. KA9Q NET derivatives such -as WAMPES or TNOS offer the 'param' command for this purpose. - - - -Hardware DCD versus Software DCD - -To avoid collisions on the air, the driver must know when the channel is -busy. This is the task of the DCD circuitry/software. The driver may either -utilise a software DCD algorithm (options=1) or use a DCD signal from -the hardware (options=0). - -ser12: if software DCD is utilised, the radio's squelch should always be - open. It is highly recommended to use the software DCD algorithm, - as it is much faster than most hardware squelch circuitry. The - disadvantage is a slightly higher load on the system. - -par96: the software DCD algorithm for this type of modem is rather poor. - The modem simply does not provide enough information to implement - a reasonable DCD algorithm in software. Therefore, if your radio - feeds the DCD input of the PAR96 modem, the use of the hardware - DCD circuitry is recommended. - -par97: as far as I know it is in this respect equivalent to par96. - - - -Compatibility with the rest of the Linux kernel - -The tty interface gave me some headaches. I did not find a reasonable -documentation of its interfaces, so I'm not particularly sure if I implemented -it the way I should. Perhaps someone with a more profound knowledge about -tty drivers could check the interface routines. -The driver produces a rather high interrupt load. par96/par97 generates 600 -interrupts per second, ser12 1200 while transmitting and 2400 if hardware -DCD is used, 3600 otherwise. If other device drivers disable interrupts -too long, the performance of the driver drops (the packet loss rate increases), -especially with the ser12 modem. -There were also reports that under rather high load situations the driver -drops frames. This might be either an interrupt problem, or an AX25 stack -running in user mode might not get enough CPU time to process the packets -before the drivers internal buffers overflow. There is no way to throttle -the other radio stations from this layer, throttling must be done in the -AX25 layer. - -The serial driver, the line printer (lp) driver and the baycom driver compete -for the same hardware resources. Of course only one driver can access a given -interface at a time. The serial driver grabs all interfaces it can find at -startup time. Therefore the baycom driver subsequently won't be able to -access a serial port. You might therefore find it necessary to release -a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where -# is the number of the interface. The baycom driver does not reserve any -port at startup, unless one is specified on the 'insmod' command line. Another -method to solve the problem is to compile all three drivers as modules and -leave it to kerneld to load the correct driver depending on the application. - - - -vy 73s de -Tom Sailer, hb9jnx@radio.amiv.ethz.ch -hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.6/linux/drivers/char/baycom.c linux/drivers/char/baycom.c --- v2.1.6/linux/drivers/char/baycom.c Tue Oct 29 19:58:05 1996 +++ linux/drivers/char/baycom.c Thu Jan 1 02:00:00 1970 @@ -1,2327 +0,0 @@ -/*****************************************************************************/ - -/* - * baycom.c -- baycom ser12 and par96 radio modem driver. - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550) - * - * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - * The modem does all the filtering and regenerates the receiver clock. - * Data is transferred from and to the PC via a shift register. - * The shift register is filled with 16 bits and an interrupt is - * signalled. The PC then empties the shift register in a burst. This - * modem connects to the parallel port, hence the name. The modem - * leaves the implementation of the HDLC protocol and the scrambler - * polynomial to the PC. - * - * par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The - * modem is protocol compatible to par96, but uses only three low - * power ICs and can therefore be fed from the parallel port and - * does not require an additional power supply. - * - * - * Command line options (insmod command line) - * - * major major number the driver should use; default 60 - * modem modem type of the first channel (minor 0); 1=ser12, - * 2=par96/par97, any other value invalid - * iobase base address of the port; common values are for ser12 0x3f8, - * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc - * irq interrupt line of the port; common values are for ser12 3,4 - * and for par96/par97 7 - * options 0=use hardware DCD, 1=use software DCD - * - * - * History: - * 0.1 03.05.96 Renamed from ser12 0.5 and added support for par96 - * Various resource allocation cleanups - * 0.2 12.05.96 Changed major to allocated 51. Integrated into kernel - * source tree - * 0.3 04.06.96 Major bug fixed (forgot to wake up after write) which - * interestingly manifested only with kernel ax25 - * (the slip line discipline) - * introduced bottom half and tq_baycom - * HDLC processing now done with interrupts on - */ - -/*****************************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_TYPE_NORMAL 0 /* not used */ -#define TTY_DRIVER_TYPE_BAYCOM 6 - -/* - * ser12 options: - * BAYCOM_OPTIONS_SOFTDCD: if undefined, you must use the transmitters - * hardware carrier detect circuitry, the driver will report DCD as soon as - * there are transitions on the input line. Advantage: lower interrupt load - * on the system. Disadvantage: slower, since hardware carrier detect - * circuitry is usually slow. - */ - -#define BUFLEN_RX 8192 -#define BUFLEN_TX 8192 - -#define NR_PORTS 4 - -#define KISS_VERBOSE - -#define BAYCOM_MAGIC 0x3105bac0 - -/* --------------------------------------------------------------------- */ - -/* - * user settable parameters (from the command line) - */ -#ifndef MODULE -static -#endif /* MODULE */ -int major = BAYCOM_MAJOR; - -/* --------------------------------------------------------------------- */ - -static struct tty_struct *baycom_table[NR_PORTS]; -static struct termios *baycom_termios[NR_PORTS]; -static struct termios *baycom_termios_locked[NR_PORTS]; - -static int baycom_refcount; - -static struct tty_driver baycom_driver; - -static struct { - int modem, iobase, irq, options; -} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, }; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -#define LPT_DATA(iobase) (iobase+0) -#define LPT_STATUS(iobase) (iobase+1) -#define LPT_CONTROL(iobase) (iobase+2) -#define LPT_IRQ_ENABLE 0x10 -#define PAR96_BURSTBITS 16 -#define PAR96_BURST 4 -#define PAR96_PTT 2 -#define PAR96_TXBIT 1 -#define PAR96_ACK 0x40 -#define PAR96_RXBIT 0x20 -#define PAR96_DCD 0x10 -#define PAR97_POWER 0xf8 - -#define PAR96_EXTENT 3 - -/* ---------------------------------------------------------------------- */ - -struct access_params { - int tx_delay; - int tx_tail; - int slottime; - int ppersist; - int fulldup; -}; - -struct hdlc_state_rx { - int rx_state; /* 0 = sync hunt, != 0 receiving */ - unsigned int bitstream; - unsigned int bitbuf; - int numbits; - unsigned int shreg1, shreg2; - - int len; - unsigned char *bp; - unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */ -}; - -struct hdlc_state_tx { - /* - * 0 = send flags - * 1 = send txtail (flags) - * 2 = send packet - */ - int tx_state; - int numflags; - unsigned int bitstream; - unsigned int current_byte; - unsigned char ptt; - - unsigned int bitbuf; - int numbits; - unsigned int shreg1, shreg2; - - int len; - unsigned char *bp; - unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */ -}; - -struct modem_state_ser12 { - unsigned char last_sample; - unsigned char interm_sample; - unsigned int bit_pll; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - unsigned char last_rxbit; - unsigned char tx_bit; -}; - -struct modem_state_par96 { - int dcd_count; - unsigned int dcd_shreg; - unsigned long descram; - unsigned long scram; -}; - -struct modem_state { - unsigned char dcd; - short arb_divider; - unsigned char flags; - struct modem_state_ser12 ser12; - struct modem_state_par96 par96; -}; - -struct packet_buffer { - unsigned int rd; - unsigned int wr; - - unsigned int buflen; - unsigned char *buffer; -}; - -struct packet_hdr { - unsigned int next; - unsigned int len; - /* packet following */ -}; - -#ifdef BAYCOM_DEBUG -struct bit_buffer { - unsigned int rd; - unsigned int wr; - unsigned int shreg; - unsigned char buffer[64]; -}; - -struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; -}; -#endif /* BAYCOM_DEBUG */ - -struct kiss_decode { - unsigned char dec_state; /* 0 = hunt FEND */ - unsigned char escaped; - unsigned char pkt_buf[BAYCOM_MAXFLEN+1]; - unsigned int wr; -}; - -/* ---------------------------------------------------------------------- */ - -struct baycom_state { - int magic; - - unsigned char modem_type; - - unsigned int iobase; - unsigned int irq; - unsigned int options; - - int opened; - struct tty_struct *tty; - -#ifdef BAYCOM_USE_BH - struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate; -#endif /* BAYCOM_USE_BH */ - - struct packet_buffer rx_buf; - struct packet_buffer tx_buf; - - struct access_params ch_params; - - struct hdlc_state_rx hdlc_rx; - struct hdlc_state_tx hdlc_tx; - - int calibrate; - - struct modem_state modem; - -#ifdef BAYCOM_DEBUG - struct bit_buffer bitbuf_channel; - struct bit_buffer bitbuf_hdlc; - - struct debug_vals debug_vals; -#endif /* BAYCOM_DEBUG */ - - struct kiss_decode kiss_decode; - - struct baycom_statistics stat; -}; - -/* --------------------------------------------------------------------- */ - -struct baycom_state baycom_state[NR_PORTS]; - -#ifdef BAYCOM_USE_BH -DECLARE_TASK_QUEUE(tq_baycom); -#endif /* BAYCOM_USE_BH */ - -/* --------------------------------------------------------------------- */ - -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - -static const unsigned short crc_ccitt_table[] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 -}; - -/*---------------------------------------------------------------------------*/ - -static inline void append_crc_ccitt(unsigned char *buffer, int len) -{ - unsigned int crc = 0xffff; - - for (;len>0;len--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; - crc ^= 0xffff; - *buffer++ = crc; - *buffer++ = crc >> 8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf,int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - return (crc & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -#if 0 -static int calc_crc_ccitt(const unsigned char *buf,int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - crc ^= 0xffff; - return (crc & 0xffff); -} -#endif - -/* ---------------------------------------------------------------------- */ - -static int store_packet(struct packet_buffer *buf, unsigned char *data, - char from_user, unsigned int len) -{ - unsigned int free; - struct packet_hdr *hdr; - unsigned int needed = sizeof(struct packet_hdr)+len; - - free = buf->rd-buf->wr; - if(buf->rd <= buf->wr) { - free = buf->buflen - buf->wr; - if((free < needed) && (buf->rd >= needed)) { - hdr = (struct packet_hdr *)(buf->buffer+buf->wr); - hdr->next = 0; - hdr->len = 0; - buf->wr = 0; - free = buf->rd; - } - } - if(free < needed) return 0; /* buffer overrun */ - hdr = (struct packet_hdr *)(buf->buffer+buf->wr); - if (from_user) - copy_from_user(hdr+1,data,len); - else - memcpy(hdr+1,data,len); - hdr->len = len; - hdr->next = buf->wr+needed; - if (hdr->next + sizeof(struct packet_hdr) >= buf->buflen) - hdr->next = 0; - buf->wr = hdr->next; - return 1; -} - -/* ---------------------------------------------------------------------- */ - -static void get_packet(struct packet_buffer *buf, unsigned char **data, - unsigned int *len) -{ - struct packet_hdr *hdr; - - *data = NULL; - *len = 0; - if (buf->rd == buf->wr) - return; - hdr = (struct packet_hdr *)(buf->buffer+buf->rd); - while (!(hdr->len)) { - buf->rd = hdr->next; - if (buf->rd == buf->wr) - return; - hdr = (struct packet_hdr *)(buf->buffer+buf->rd); - } - *data = (unsigned char *)(hdr+1); - *len = hdr->len; -} - -/* ---------------------------------------------------------------------- */ - -static void ack_packet(struct packet_buffer *buf) -{ - struct packet_hdr *hdr; - - if (buf->rd == buf->wr) - return; - hdr = (struct packet_hdr *)(buf->buffer+buf->rd); - buf->rd = hdr->next; -} - -/* ---------------------------------------------------------------------- */ - -static int store_kiss_packet(struct packet_buffer *buf, unsigned char *data, - unsigned int len) -{ - unsigned char *bp = data; - int ln = len; - /* - * variables of buf - */ - unsigned int rd; - unsigned int wr; - unsigned int buflen; - unsigned char *buffer; - - if (!len || !data || !buf) - return 0; - buflen = buf->buflen; - rd = buf->rd; - wr = buf->wr; - buffer = buf->buffer; - -#define ADD_CHAR(c) {\ - buffer[wr++] = c;\ - if (wr >= buflen) wr = 0;\ - if (wr == rd) return 0;\ - } -#define ADD_KISSCHAR(c) {\ - if (((c) & 0xff) == KISS_FEND) {\ - ADD_CHAR(KISS_FESC);\ - ADD_CHAR(KISS_TFEND);\ - } else if (((c) & 0xff) == KISS_FESC) {\ - ADD_CHAR(KISS_FESC);\ - ADD_CHAR(KISS_TFESC);\ - } else {\ - ADD_CHAR(c);\ - }\ - } - - ADD_CHAR(KISS_FEND); - ADD_KISSCHAR(KISS_CMD_DATA); - for(; ln > 0; ln--,bp++) { - ADD_KISSCHAR(*bp); - } - ADD_CHAR(KISS_FEND); - buf->wr = wr; -#undef ADD_CHAR -#undef ADD_KISSCHAR - return 1; -} - -/* ---------------------------------------------------------------------- */ - -#ifdef BAYCOM_DEBUG -static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit) -{ - unsigned char new; - - if (!buf) return; - new = buf->shreg & 1; - buf->shreg >>= 1; - if (bit) - buf->shreg |= 0x80; - if (new) { - buf->buffer[buf->wr] = buf->shreg; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->shreg = 0x80; - } -} - -static inline void add_bitbuffer_word(struct bit_buffer * buf, - unsigned int bits) -{ - buf->buffer[buf->wr] = bits & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->buffer[buf->wr] = (bits >> 8) & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - -} -#endif /* BAYCOM_DEBUG */ - -/* ---------------------------------------------------------------------- */ - -static inline unsigned int tenms_to_2flags(struct baycom_state *bc, - unsigned int tenms) -{ - switch (bc->modem_type) { - case BAYCOM_MODEM_SER12: - return tenms * 3 / 4; - case BAYCOM_MODEM_PAR96: - return tenms * 6; - default: - return 0; - } -} - -/* ---------------------------------------------------------------------- */ -/* - * The HDLC routines - */ - -static inline int hdlc_rx_add_bytes(struct baycom_state *bc, - unsigned int bits, int num) -{ - int added = 0; - while (bc->hdlc_rx.rx_state && num >= 8) { - if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) { - bc->hdlc_rx.rx_state = 0; - return 0; - } - *bc->hdlc_rx.bp++ = bits >> (32-num); - bc->hdlc_rx.len++; - num -= 8; - added += 8; - } - return added; -} - -static inline void hdlc_rx_flag(struct baycom_state *bc) -{ - if (bc->hdlc_rx.len < 4) - return; - if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len)) - return; - bc->stat.rx_packets++; - if (!store_kiss_packet(&bc->rx_buf, - bc->hdlc_rx.buffer, - bc->hdlc_rx.len-2)) - bc->stat.rx_bufferoverrun++; -} - -static void hdlc_rx_word(struct baycom_state *bc, unsigned int word) -{ - int i; - unsigned int mask1, mask2, mask3, mask4, mask5, mask6; - - if (!bc) return; - - word &= 0xffff; -#ifdef BAYCOM_DEBUG - add_bitbuffer_word(&bc->bitbuf_hdlc, word); -#endif /* BAYCOM_DEBUG */ - bc->hdlc_rx.bitstream >>= 16; - bc->hdlc_rx.bitstream |= word << 16; - bc->hdlc_rx.bitbuf >>= 16; - bc->hdlc_rx.bitbuf |= word << 16; - bc->hdlc_rx.numbits += 16; - for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00, - mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; - i >= 0; - i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, - mask5 <<= 1, mask6 = (mask6 << 1) | 1) { - if ((bc->hdlc_rx.bitstream & mask1) == mask1) - bc->hdlc_rx.rx_state = 0; /* abort received */ - else if ((bc->hdlc_rx.bitstream & mask2) == mask3) { - /* flag received */ - if (bc->hdlc_rx.rx_state) { - hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf << - (8 + i), bc->hdlc_rx.numbits - - 8 - i); - hdlc_rx_flag(bc); - } - bc->hdlc_rx.len = 0; - bc->hdlc_rx.bp = bc->hdlc_rx.buffer; - bc->hdlc_rx.rx_state = 1; - bc->hdlc_rx.numbits = i; - } else if ((bc->hdlc_rx.bitstream & mask4) == mask5) { - /* stuffed bit */ - bc->hdlc_rx.numbits--; - bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) | - ((bc->hdlc_rx.bitbuf & mask6) << 1); - } - } - bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf, - bc->hdlc_rx.numbits); -} - -/* ---------------------------------------------------------------------- */ - -static unsigned int hdlc_tx_word(struct baycom_state *bc) -{ - unsigned int mask1, mask2, mask3; - int i; - - if (!bc || !bc->hdlc_tx.ptt) - return 0; - for (;;) { - if (bc->hdlc_tx.numbits >= 16) { - unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff; - bc->hdlc_tx.bitbuf >>= 16; - bc->hdlc_tx.numbits -= 16; - return ret; - } - switch (bc->hdlc_tx.tx_state) { - default: - bc->hdlc_tx.ptt = 0; - bc->hdlc_tx.tx_state = 0; - return 0; - case 0: - case 1: - if (bc->hdlc_tx.numflags) { - bc->hdlc_tx.numflags--; - bc->hdlc_tx.bitbuf |= - 0x7e7e << bc->hdlc_tx.numbits; - bc->hdlc_tx.numbits += 16; - break; - } - if (bc->hdlc_tx.tx_state == 1) { - bc->hdlc_tx.ptt = 0; - return 0; - } - get_packet(&bc->tx_buf, &bc->hdlc_tx.bp, - &bc->hdlc_tx.len); - if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) { - bc->hdlc_tx.tx_state = 1; - bc->hdlc_tx.numflags = tenms_to_2flags - (bc, bc->ch_params.tx_tail); - break; - } - if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) { - bc->hdlc_tx.tx_state = 0; - bc->hdlc_tx.numflags = 1; - ack_packet(&bc->tx_buf); - break; - } - memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp, - bc->hdlc_tx.len); - ack_packet(&bc->tx_buf); - bc->hdlc_tx.bp = bc->hdlc_tx.buffer; - append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len); - /* the appended CRC */ - bc->hdlc_tx.len += 2; - bc->hdlc_tx.tx_state = 2; - bc->hdlc_tx.bitstream = 0; - bc->stat.tx_packets++; - break; - case 2: - if (!bc->hdlc_tx.len) { - bc->hdlc_tx.tx_state = 0; - bc->hdlc_tx.numflags = 1; - break; - } - bc->hdlc_tx.len--; - bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp << - bc->hdlc_tx.numbits; - bc->hdlc_tx.bitstream >>= 8; - bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16; - mask1 = 0x1f000; - mask2 = 0x10000; - mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits); - bc->hdlc_tx.numbits += 8; - for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, - mask3 = (mask3 << 1) | 1) { - if ((bc->hdlc_tx.bitstream & mask1) != mask1) - continue; - bc->hdlc_tx.bitstream &= ~mask2; - bc->hdlc_tx.bitbuf = - (bc->hdlc_tx.bitbuf & mask3) | - ((bc->hdlc_tx.bitbuf & - (~mask3)) << 1); - bc->hdlc_tx.numbits++; - mask3 = (mask3 << 1) | 1; - } - break; - } - } -} - -/* ---------------------------------------------------------------------- */ - -static unsigned short random_seed; - -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} - -/* ---------------------------------------------------------------------- */ - -static inline void tx_arbitrate(struct baycom_state *bc) -{ - unsigned char *bp; - unsigned int len; - - if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd) - return; - get_packet(&bc->tx_buf, &bp, &len); - if (!bp || !len) - return; - - if (!bc->ch_params.fulldup) { - if ((random_num() % 256) > bc->ch_params.ppersist) - return; - } - bc->hdlc_tx.tx_state = 0; - bc->hdlc_tx.numflags = tenms_to_2flags(bc, bc->ch_params.tx_delay); - bc->hdlc_tx.numbits = bc->hdlc_tx.bitbuf = bc->hdlc_tx.bitstream = 0; - bc->hdlc_tx.ptt = 1; - bc->stat.ptt_keyed++; -} - -/* --------------------------------------------------------------------- */ - -#ifdef BAYCOM_DEBUG -static void inline baycom_int_freq(struct baycom_state *bc) -{ - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -} -#endif /* BAYCOM_DEBUG */ - -/* --------------------------------------------------------------------- */ - -static inline void rx_chars_to_flip(struct baycom_state *bc) -{ - int flip_free; - unsigned int cnt; - unsigned int new_rd; - unsigned long flags; - - if ((!bc) || (!bc->tty) || (bc->tty->flip.count >= TTY_FLIPBUF_SIZE) || - (bc->rx_buf.rd == bc->rx_buf.wr) || - (!bc->tty->flip.char_buf_ptr) || - (!bc->tty->flip.flag_buf_ptr)) - return; - for(;;) { - flip_free = TTY_FLIPBUF_SIZE - bc->tty->flip.count; - if (bc->rx_buf.rd <= bc->rx_buf.wr) - cnt = bc->rx_buf.wr - bc->rx_buf.rd; - else - cnt = bc->rx_buf.buflen - bc->rx_buf.rd; - if ((flip_free <= 0) || (!cnt)) { - tty_schedule_flip(bc->tty); - return; - } - if (cnt > flip_free) - cnt = flip_free; - save_flags(flags); cli(); - memcpy(bc->tty->flip.char_buf_ptr, bc->rx_buf.buffer+bc->rx_buf.rd, cnt); - memset(bc->tty->flip.flag_buf_ptr, TTY_NORMAL, cnt); - bc->tty->flip.count += cnt; - bc->tty->flip.char_buf_ptr += cnt; - bc->tty->flip.flag_buf_ptr += cnt; - restore_flags(flags); - new_rd = bc->rx_buf.rd+cnt; - if (new_rd >= bc->rx_buf.buflen) - new_rd -= bc->rx_buf.buflen; - bc->rx_buf.rd = new_rd; - } -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -static void inline ser12_set_divisor(struct baycom_state *bc, - unsigned char divisor) -{ - outb(0x81, LCR(bc->iobase)); /* DLAB = 1 */ - outb(divisor, DLL(bc->iobase)); - outb(0, DLM(bc->iobase)); - outb(0x01, LCR(bc->iobase)); /* word length = 6 */ -} - -/* --------------------------------------------------------------------- */ - -/* - * must call the TX arbitrator every 10ms - */ -#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ - 36 : 24) -#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ - 240 : 12) - -static void baycom_ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct baycom_state *bc = (struct baycom_state *)dev_id; - unsigned char cur_s; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(bc->iobase)); - rx_chars_to_flip(bc); -#ifdef BAYCOM_DEBUG - baycom_int_freq(bc); -#endif /* BAYCOM_DEBUG */ - /* - * check if transmitter active - */ - if (bc->hdlc_tx.ptt || bc->calibrate > 0) { - ser12_set_divisor(bc, 12); /* one interrupt per channel bit */ - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase)); - if (bc->hdlc_tx.shreg1 <= 1) { - if (bc->calibrate > 0) { - bc->hdlc_tx.shreg1 = 0x10000; - bc->calibrate--; - } else { -#ifdef BAYCOM_USE_BH - bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2; - bc->hdlc_tx.shreg2 = 0; - queue_task_irq_off(&bc->tq_transmitter, - &tq_baycom); - mark_bh(BAYCOM_BH); -#ifdef HDLC_LOOPBACK - bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1; - queue_task_irq_off(&bc->tq_receiver, - &tq_baycom); -#endif /* HDLC_LOOPBACK */ -#else /* BAYCOM_USE_BH */ - bc->hdlc_tx.shreg1 = hdlc_tx_word(bc) - | 0x10000; -#ifdef HDLC_LOOPBACK - hdlc_rx_word(bc, bc->hdlc_tx.shreg1); -#endif /* HDLC_LOOPBACK */ -#endif /* BAYCOM_USE_BH */ - } - } - if (!(bc->hdlc_tx.shreg1 & 1)) - bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit; - bc->hdlc_tx.shreg1 >>= 1; - return; - } - /* - * do demodulator - */ - outb(0x0d, MCR(bc->iobase)); /* transmitter off */ - cur_s = inb(MSR(bc->iobase)) & 0x10; /* the CTS line */ -#ifdef BAYCOM_DEBUG - add_bitbuffer(&bc->bitbuf_channel, cur_s); -#endif /* BAYCOM_DEBUG */ - bc->modem.ser12.dcd_shreg <<= 1; - if(cur_s != bc->modem.ser12.last_sample) { - bc->modem.ser12.dcd_shreg |= 1; - - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - dcdspos += 2; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } else - bc->modem.ser12.dcd_sum0--; - } - bc->modem.ser12.last_sample = cur_s; - if(!bc->modem.ser12.dcd_time) { - bc->modem.dcd = (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0; - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - /* - * PLL code for the improved software DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(bc, 4); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 7) { - case 1: /* transition too late */ - ser12_set_divisor(bc, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 4: /* transition too early */ - ser12_set_divisor(bc, 3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(bc, 4); - break; - } - bc->hdlc_rx.shreg1 >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->hdlc_rx.shreg1 |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - if (++bc->modem.ser12.interm_sample >= 3) - bc->modem.ser12.interm_sample = 0; - } else { - /* - * PLL algorithm for the hardware squelch DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(bc, 6); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 3) { - case 1: /* transition too late */ - ser12_set_divisor(bc, 7); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 2: /* transition too early */ - ser12_set_divisor(bc, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(bc, 6); - break; - } - bc->hdlc_rx.shreg1 >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->hdlc_rx.shreg1 |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; - } - if (bc->hdlc_rx.shreg1 & 1) { -#ifdef BAYCOM_USE_BH - bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000; - queue_task_irq_off(&bc->tq_receiver, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1); -#endif /* BAYCOM_USE_BH */ - bc->hdlc_rx.shreg1 = 0x10000; - } - if (--bc->modem.arb_divider <= 0) { -#ifdef BAYCOM_USE_BH - queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - tx_arbitrate(bc); -#endif /* BAYCOM_USE_BH */ - bc->modem.arb_divider = bc->ch_params.slottime * - SER12_ARB_DIVIDER(bc); - } -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = - { "unknown", "8250", "16450", "16550", "16550A" }; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_allocate_resources(unsigned int iobase, unsigned int irq, - unsigned int options) -{ - enum uart u; - - if (!iobase || iobase > 0xfff || irq < 2 || irq > 15) - return -ENXIO; - if (check_region(iobase, SER12_EXTENT)) - return -EACCES; - if ((u = ser12_check_uart(iobase)) == c_uart_unknown) - return -EIO; - request_region(iobase, SER12_EXTENT, "baycom_ser12"); - outb(0, FCR(iobase)); /* disable FIFOs */ - outb(0x0d, MCR(iobase)); - printk(KERN_INFO "baycom: ser12 at iobase 0x%x irq %u options 0x%x " - "uart %s\n", iobase, irq, options, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void ser12_deallocate_resources(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_SER12) - return; - /* - * disable interrupts - */ - outb(0, IER(bc->iobase)); - outb(1, MCR(bc->iobase)); - /* - * this should prevent kernel: Trying to free IRQx - * messages - */ - if (bc->opened > 0) - free_irq(bc->irq, bc); - release_region(bc->iobase, SER12_EXTENT); - bc->modem_type = BAYCOM_MODEM_INVALID; - printk(KERN_INFO "baycom: release ser12 at iobase 0x%x irq %u\n", - bc->iobase, bc->irq); - bc->iobase = bc->irq = bc->options = 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_on_open(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_SER12) - return -ENXIO; - /* - * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that - * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, - * depending on the usage of the software DCD routine - */ - ser12_set_divisor(bc, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6); - outb(0x0d, MCR(bc->iobase)); - outb(0, IER(bc->iobase)); - if (request_irq(bc->irq, baycom_ser12_interrupt, SA_INTERRUPT, - "baycom_ser12", bc)) - return -EBUSY; - /* - * enable transmitter empty interrupt - */ - outb(2, IER(bc->iobase)); - /* - * the value here serves to power the modem - */ - outb(0x00, THR(bc->iobase)); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void ser12_on_close(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_SER12) - return; - /* - * disable interrupts - */ - outb(0, IER(bc->iobase)); - outb(1, MCR(bc->iobase)); - free_irq(bc->irq, bc); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== PAR96 specific routines ========================= - */ - -#define PAR96_DESCRAM_TAP1 0x20000 -#define PAR96_DESCRAM_TAP2 0x01000 -#define PAR96_DESCRAM_TAP3 0x00001 - -#define PAR96_DESCRAM_TAPSH1 17 -#define PAR96_DESCRAM_TAPSH2 12 -#define PAR96_DESCRAM_TAPSH3 0 - -#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ -#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static void baycom_par96_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - register struct baycom_state *bc = (struct baycom_state *)dev_id; - int i; - unsigned int data, descx, mask, mask2; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - - rx_chars_to_flip(bc); -#ifdef BAYCOM_DEBUG - baycom_int_freq(bc); -#endif /* BAYCOM_DEBUG */ - /* - * check if transmitter active - */ - if (bc->hdlc_tx.ptt || bc->calibrate > 0) { - /* - * first output the last 16 bits (!) then call HDLC - * transmitter, since this may take quite long - * do the differential encoder and the scrambler on the fly - */ - data = bc->hdlc_tx.shreg1; - for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { - unsigned char val = PAR97_POWER; - bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | - (bc->modem.par96.scram & 1)); - if (!(data & 1)) - bc->modem.par96.scram ^= 1; - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) - bc->modem.par96.scram ^= - (PAR96_SCRAM_TAPN << 1); - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) - val |= PAR96_TXBIT; - outb(val, LPT_DATA(bc->iobase)); - outb(val | PAR96_BURST, LPT_DATA(bc->iobase)); - } - if (bc->calibrate > 0) { - bc->hdlc_tx.shreg1 = 0x10000; - bc->calibrate--; - } else { -#ifdef BAYCOM_USE_BH - bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2; - bc->hdlc_tx.shreg2 = 0; - queue_task_irq_off(&bc->tq_transmitter, &tq_baycom); - mark_bh(BAYCOM_BH); -#ifdef HDLC_LOOPBACK - bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1; - queue_task_irq_off(&bc->tq_receiver, &tq_baycom); -#endif /* HDLC_LOOPBACK */ -#else /* BAYCOM_USE_BH */ - bc->hdlc_tx.shreg1 = hdlc_tx_word(bc); -#ifdef HDLC_LOOPBACK - hdlc_rx_word(bc, bc->hdlc_tx.shreg1); -#endif /* HDLC_LOOPBACK */ -#endif /* BAYCOM_USE_BH */ - } - return; - } - /* - * do receiver; differential decode and descramble on the fly - */ - for(data = i = 0; i < PAR96_BURSTBITS; i++) { - bc->modem.par96.descram = (bc->modem.par96.descram << 1); - if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT) - bc->modem.par96.descram |= 1; - descx = bc->modem.par96.descram ^ - (bc->modem.par96.descram >> 1); - /* now the diff decoded data is inverted in descram */ - outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase)); - descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ - (descx >> PAR96_DESCRAM_TAPSH2)); - data >>= 1; - if (!(descx & 1)) - data |= 0x8000; - outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, - LPT_DATA(bc->iobase)); - } -#ifdef BAYCOM_USE_BH - bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1; - bc->hdlc_rx.shreg1 = data | 0x10000; - queue_task_irq_off(&bc->tq_receiver, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - hdlc_rx_word(bc, data); -#endif /* BAYCOM_USE_BH */ - /* - * do DCD algorithm - */ - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) - | (data << 16); - /* search for flags and set the dcd counter appropriately */ - for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if ((bc->modem.par96.dcd_shreg & mask) == mask2) - bc->modem.par96.dcd_count = BAYCOM_MAXFLEN+4; - /* check for abort/noise sequences */ - for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if (((bc->modem.par96.dcd_shreg & mask) == mask2) && - (bc->modem.par96.dcd_count >= 0)) - bc->modem.par96.dcd_count -= BAYCOM_MAXFLEN-10; - /* decrement and set the dcd variable */ - if (bc->modem.par96.dcd_count >= 0) - bc->modem.par96.dcd_count -= 2; - bc->modem.dcd = bc->modem.par96.dcd_count > 0; - } else { - bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase)) - & PAR96_DCD); - } - if (--bc->modem.arb_divider <= 0) { -#ifdef BAYCOM_USE_BH - queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom); - mark_bh(BAYCOM_BH); -#else /* BAYCOM_USE_BH */ - tx_arbitrate(bc); -#endif /* BAYCOM_USE_BH */ - bc->modem.arb_divider = bc->ch_params.slottime * 6; - } -} - -/* --------------------------------------------------------------------- */ - -static int par96_check_lpt(unsigned int iobase) -{ - unsigned char b1,b2; - int i; - - b1 = inb(LPT_DATA(iobase)); - b2 = inb(LPT_CONTROL(iobase)); - outb(0xaa, LPT_DATA(iobase)); - i = inb(LPT_DATA(iobase)) == 0xaa; - outb(0x55, LPT_DATA(iobase)); - i &= inb(LPT_DATA(iobase)) == 0x55; - outb(0x0a, LPT_CONTROL(iobase)); - i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; - outb(0x05, LPT_CONTROL(iobase)); - i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; - outb(b1, LPT_DATA(iobase)); - outb(b2, LPT_CONTROL(iobase)); - return !i; -} - -/* --------------------------------------------------------------------- */ - -static int par96_allocate_resources(unsigned int iobase, unsigned int irq, - unsigned int options) -{ - if (!iobase || iobase > 0xfff || irq < 2 || irq > 15) - return -ENXIO; - if (check_region(iobase, PAR96_EXTENT)) - return -EACCES; - if (par96_check_lpt(iobase)) - return -EIO; - request_region(iobase, PAR96_EXTENT, "baycom_par96"); - outb(0, LPT_CONTROL(iobase)); /* disable interrupt */ - outb(PAR96_PTT | PAR97_POWER, LPT_DATA(iobase)); /* switch off PTT */ - printk(KERN_INFO "baycom: par96 at iobase 0x%x irq %u options 0x%x\n", - iobase, irq, options); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void par96_deallocate_resources(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96) - return; - outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */ - outb(PAR96_PTT, LPT_DATA(bc->iobase)); /* switch off PTT */ - /* - * this should prevent kernel: Trying to free IRQx - * messages - */ - if (bc->opened > 0) - free_irq(bc->irq, bc); - release_region(bc->iobase, PAR96_EXTENT); - bc->modem_type = BAYCOM_MODEM_INVALID; - printk(KERN_INFO "baycom: release par96 at iobase 0x%x irq %u\n", - bc->iobase, bc->irq); - bc->iobase = bc->irq = bc->options = 0; -} - -/* --------------------------------------------------------------------- */ - -static int par96_on_open(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96) - return -ENXIO; - outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */ - /* switch off PTT */ - outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase)); - if (request_irq(bc->irq, baycom_par96_interrupt, SA_INTERRUPT, - "baycom_par96", bc)) - return -EBUSY; - outb(LPT_IRQ_ENABLE, LPT_CONTROL(bc->iobase)); /* enable interrupt */ - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void par96_on_close(struct baycom_state *bc) -{ - if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96) - return; - outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */ - /* switch off PTT */ - outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase)); - free_irq(bc->irq, bc); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== Bottom half (soft interrupt) ==================== - */ - -#ifdef BAYCOM_USE_BH -static void bh_receiver(void *private) -{ - struct baycom_state *bc = (struct baycom_state *)private; - unsigned int temp; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - if (!bc->hdlc_rx.shreg2) - return; - temp = bc->hdlc_rx.shreg2; - bc->hdlc_rx.shreg2 = 0; - hdlc_rx_word(bc, temp); -} - -/* --------------------------------------------------------------------- */ - -static void bh_transmitter(void *private) -{ - struct baycom_state *bc = (struct baycom_state *)private; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - if (bc->hdlc_tx.shreg2) - return; - bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000; -} - -/* --------------------------------------------------------------------- */ - -static void bh_arbitrate(void *private) -{ - struct baycom_state *bc = (struct baycom_state *)private; - - if (!bc || bc->magic != BAYCOM_MAGIC) - return; - tx_arbitrate(bc); -} - -/* --------------------------------------------------------------------- */ - -static void baycom_bottom_half(void) -{ - run_task_queue(&tq_baycom); -} -#endif /* BAYCOM_USE_BH */ - -/* --------------------------------------------------------------------- */ -/* - * ===================== TTY interface routines ========================== - */ - -static inline int baycom_paranoia_check(struct baycom_state *bc, - const char *routine) -{ - if (!bc || bc->magic != BAYCOM_MAGIC) { - printk(KERN_ERR "baycom: bad magic number for baycom struct " - "in routine %s\n", routine); - return 1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * Here the tty driver code starts - */ - -static void baycom_put_fend(struct baycom_state *bc) -{ - if (bc->kiss_decode.wr <= 0 || - (bc->kiss_decode.pkt_buf[0] & 0xf0) != 0) - return; - - switch (bc->kiss_decode.pkt_buf[0] & 0xf) { - case KISS_CMD_DATA: - if (bc->kiss_decode.wr <= 8) - break; - if (!store_packet(&bc->tx_buf, bc->kiss_decode.pkt_buf+1, 0, - bc->kiss_decode.wr-1)) - bc->stat.tx_bufferoverrun++; - break; - - case KISS_CMD_TXDELAY: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.tx_delay = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: TX delay = %ums\n", - bc->ch_params.tx_delay * 10); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_PPERSIST: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: p-persistence = %u\n", - bc->ch_params.ppersist); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_SLOTTIME: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: slottime = %ums\n", - bc->ch_params.slottime * 10); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_TXTAIL: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.tx_tail = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: TX tail = %ums\n", - bc->ch_params.tx_tail * 10); -#endif /* KISS_VERBOSE */ - break; - - case KISS_CMD_FULLDUP: - if (bc->kiss_decode.wr < 2) - break; - bc->ch_params.fulldup = bc->kiss_decode.pkt_buf[1]; -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: %s duplex\n", - bc->ch_params.fulldup ? "full" : "half"); -#endif /* KISS_VERBOSE */ - break; - - default: -#ifdef KISS_VERBOSE - printk(KERN_INFO "baycom: unhandled KISS packet code %u\n", - bc->kiss_decode.pkt_buf[0] & 0xf); -#endif /* KISS_VERBOSE */ - break; - } -} - -/* --------------------------------------------------------------------- */ - -static void baycom_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct baycom_state *bc; - - if (!tty) - return; - if (baycom_paranoia_check(bc = tty->driver_data, "put_char")) - return; - - if (ch == KISS_FEND) { - baycom_put_fend(bc); - bc->kiss_decode.wr = 0; - bc->kiss_decode.escaped = 0; - bc->kiss_decode.dec_state = 1; - return; - } - if (!bc->kiss_decode.dec_state) - return; - if (ch == KISS_FESC) { - bc->kiss_decode.escaped = 1; - return; - } - if (bc->kiss_decode.wr >= sizeof(bc->kiss_decode.pkt_buf)) { - bc->kiss_decode.wr = 0; - bc->kiss_decode.dec_state = 0; - return; - } - if (bc->kiss_decode.escaped) { - if (ch == KISS_TFEND) - bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = - KISS_FEND; - else if (ch == KISS_TFESC) - bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = - KISS_FESC; - else { - bc->kiss_decode.wr = 0; - bc->kiss_decode.dec_state = 0; - } - bc->kiss_decode.escaped = 0; - return; - } - bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = ch; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_write(struct tty_struct * tty, int from_user, - const unsigned char *buf, int count) -{ - int c; - const unsigned char *bp; - struct baycom_state *bc; - - if (!tty || !buf || count <= 0) - return count; - - if (baycom_paranoia_check(bc = tty->driver_data, "write")) - return count; - - if (from_user) { - for(c = count, bp = buf; c > 0; c--,bp++) - baycom_put_char(tty, get_user(bp)); - } else { - for(c = count, bp = buf; c > 0; c--,bp++) - baycom_put_char(tty, *bp); - } - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - return count; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_write_room(struct tty_struct *tty) -{ - int free; - struct baycom_state *bc; - - if (!tty) - return 0; - if (baycom_paranoia_check(bc = tty->driver_data, "write_room")) - return 0; - - free = bc->tx_buf.rd - bc->tx_buf.wr; - if (free <= 0) { - free = bc->tx_buf.buflen - bc->tx_buf.wr; - if (free < bc->tx_buf.rd) - free = bc->tx_buf.rd; /* we may fold */ - } - - return free / 2; /* a rather pessimistic estimate */ -} - -/* --------------------------------------------------------------------- */ - -static int baycom_chars_in_buffer(struct tty_struct *tty) -{ - int cnt; - struct baycom_state *bc; - - if (!tty) - return 0; - if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer")) - return 0; - - cnt = bc->tx_buf.wr - bc->tx_buf.rd; - if (cnt < 0) - cnt += bc->tx_buf.buflen; - - return cnt; -} - -/* --------------------------------------------------------------------- */ - -static void baycom_flush_buffer(struct tty_struct *tty) -{ - struct baycom_state *bc; - - if (!tty) - return; - if (baycom_paranoia_check(bc = tty->driver_data, "flush_buffer")) - return; - - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} - -/* --------------------------------------------------------------------- */ - -static inline void baycom_dealloc_hw(struct baycom_state *bc) -{ - if (!bc || bc->magic != BAYCOM_MAGIC || - bc->modem_type == BAYCOM_MODEM_INVALID) - return; - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - ser12_deallocate_resources(bc); - break; - case BAYCOM_MODEM_PAR96: - par96_deallocate_resources(bc); - break; - } -} - -/* --------------------------------------------------------------------- */ - -static int baycom_set_hardware(struct baycom_state *bc, - unsigned int modem_type, unsigned int iobase, - unsigned int irq, unsigned int options) -{ - int i; - - if (!bc) - return -EINVAL; - - if (modem_type == BAYCOM_MODEM_SER12) { - i = ser12_allocate_resources(iobase, irq, options); - if (i < 0) - return i; - } else if (modem_type == BAYCOM_MODEM_PAR96) { - i = par96_allocate_resources(iobase, irq, options); - if (i < 0) - return i; - } else if (modem_type == BAYCOM_MODEM_INVALID) { - iobase = irq = options = 0; - } else { - return -ENXIO; - } - baycom_dealloc_hw(bc); - bc->modem_type = modem_type; - bc->iobase = iobase; - bc->irq = irq; - bc->options = options; - i = 0; - if (bc->opened > 0) { - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - i = ser12_on_open(bc); - break; - case BAYCOM_MODEM_PAR96: - i = par96_on_open(bc); - break; - } - } - return i; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int i; - struct baycom_state *bc; - struct baycom_params par; - - if (!tty) - return -EINVAL; - if (baycom_paranoia_check(bc = tty->driver_data, "ioctl")) - return -EINVAL; - - switch (cmd) { - default: - return -ENOIOCTLCMD; - - case TIOCMGET: - i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); - if (i) - return i; - i = (bc->modem.dcd ? TIOCM_CAR : 0) | - (bc->hdlc_tx.ptt ? TIOCM_RTS : 0); - put_user(i, (int *) arg); - return 0; - - case BAYCOMCTL_GETDCD: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) - put_user(bc->modem.dcd, (unsigned char *) arg); - return i; - - case BAYCOMCTL_GETPTT: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) - put_user(bc->hdlc_tx.ptt, (unsigned char *) arg); - return i; - - case BAYCOMCTL_PARAM_TXDELAY: - if (arg > 255) - return -EINVAL; - bc->ch_params.tx_delay = arg; - return 0; - - case BAYCOMCTL_PARAM_PPERSIST: - if (arg > 255) - return -EINVAL; - bc->ch_params.ppersist = arg; - return 0; - - case BAYCOMCTL_PARAM_SLOTTIME: - if (arg > 255) - return -EINVAL; - bc->ch_params.slottime = arg; - return 0; - - case BAYCOMCTL_PARAM_TXTAIL: - if (arg > 255) - return -EINVAL; - bc->ch_params.tx_tail = arg; - return 0; - - case BAYCOMCTL_PARAM_FULLDUP: - bc->ch_params.fulldup = arg ? 1 : 0; - return 0; - - case BAYCOMCTL_CALIBRATE: - bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ? - 600 : 75); - return 0; - - case BAYCOMCTL_GETPARAMS: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(par)); - if (i) - return i; - par.modem_type = bc->modem_type; - par.iobase = bc->iobase; - par.irq = bc->irq; - par.options = bc->options; - par.tx_delay = bc->ch_params.tx_delay; - par.tx_tail = bc->ch_params.tx_tail; - par.slottime = bc->ch_params.slottime; - par.ppersist = bc->ch_params.ppersist; - par.fulldup = bc->ch_params.fulldup; - copy_to_user((void *)arg, &par, sizeof(par)); - return 0; - - case BAYCOMCTL_SETPARAMS: - if (!suser()) - return -EPERM; - i = verify_area(VERIFY_READ, (void *) arg, - sizeof(par)); - if (i) - return i; - copy_from_user(&par, (void *)arg, sizeof(par)); - printk(KERN_INFO "baycom: changing hardware type: modem %u " - "iobase 0x%x irq %u options 0x%x\n", par.modem_type, - par.iobase, par.irq, par.options); - i = baycom_set_hardware(bc, par.modem_type, par.iobase, - par.irq, par.options); - if (i) - return i; - bc->ch_params.tx_delay = par.tx_delay; - bc->ch_params.tx_tail = par.tx_tail; - bc->ch_params.slottime = par.slottime; - bc->ch_params.ppersist = par.ppersist; - bc->ch_params.fulldup = par.fulldup; - return 0; - - case BAYCOMCTL_GETSTAT: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct baycom_statistics)); - if (i) - return i; - copy_to_user((void *)arg, &bc->stat, - sizeof(struct baycom_statistics)); - return 0; - - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETSAMPLES: - if (bc->bitbuf_channel.rd == bc->bitbuf_channel.wr) - return -EAGAIN; - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) { - put_user(bc->bitbuf_channel.buffer - [bc->bitbuf_channel.rd], - (unsigned char *) arg); - bc->bitbuf_channel.rd = (bc->bitbuf_channel.rd+1) % - sizeof(bc->bitbuf_channel.buffer); - } - return i; - - case BAYCOMCTL_GETBITS: - if (bc->bitbuf_hdlc.rd == bc->bitbuf_hdlc.wr) - return -EAGAIN; - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned char)); - if (!i) { - put_user(bc->bitbuf_hdlc.buffer[bc->bitbuf_hdlc.rd], - (unsigned char *) arg); - bc->bitbuf_hdlc.rd = (bc->bitbuf_hdlc.rd+1) % - sizeof(bc->bitbuf_hdlc.buffer); - } - return i; - - case BAYCOMCTL_DEBUG1: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned long)); - if (!i) - put_user((bc->rx_buf.wr-bc->rx_buf.rd) % - bc->rx_buf.buflen, (unsigned long *)arg); - return i; - - case BAYCOMCTL_DEBUG2: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned long)); - if (!i) - put_user(bc->debug_vals.last_intcnt, - (unsigned long *)arg); - return i; - - case BAYCOMCTL_DEBUG3: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned long)); - if (!i) - put_user((long)bc->debug_vals.last_pllcorr, - (long *)arg); - return i; -#endif /* BAYCOM_DEBUG */ - } -} - -/* --------------------------------------------------------------------- */ - -int baycom_open(struct tty_struct *tty, struct file * filp) -{ - int line; - struct baycom_state *bc; - int i; - - if(!tty) - return -ENODEV; - - line = MINOR(tty->device) - tty->driver.minor_start; - if (line < 0 || line >= NR_PORTS) - return -ENODEV; - bc = baycom_state+line; - - if (bc->opened > 0) { - bc->opened++; - MOD_INC_USE_COUNT; - return 0; - } - /* - * initialise some variables - */ - bc->calibrate = 0; - - /* - * allocate the buffer space - */ - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buflen = BUFLEN_RX; - bc->tx_buf.buflen = BUFLEN_TX; - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->tx_buf.rd = bc->tx_buf.wr = 0; - bc->rx_buf.buffer = kmalloc(bc->rx_buf.buflen, GFP_KERNEL); - bc->tx_buf.buffer = kmalloc(bc->tx_buf.buflen, GFP_KERNEL); - if (!bc->rx_buf.buffer || !bc->tx_buf.buffer) { - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buffer = bc->tx_buf.buffer = NULL; - bc->rx_buf.buflen = bc->tx_buf.buflen = 0; - return -ENOMEM; - } - /* - * check if the modem type has been set - */ - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - i = ser12_on_open(bc); - break; - case BAYCOM_MODEM_PAR96: - i = par96_on_open(bc); - break; - case BAYCOM_MODEM_INVALID: - /* - * may open even if no hardware specified, in order to - * subsequently allow the BAYCOMCTL_SETPARAMS ioctl - */ - i = 0; - break; - default: - return -ENODEV; - } - if (i) - return i; - - bc->opened++; - MOD_INC_USE_COUNT; - - tty->driver_data = bc; - bc->tty = tty; - - return 0; -} - - -/* --------------------------------------------------------------------- */ - -static void baycom_close(struct tty_struct *tty, struct file * filp) -{ - struct baycom_state *bc; - - if(!tty) return; - if (baycom_paranoia_check(bc = tty->driver_data, "close")) - return; - - MOD_DEC_USE_COUNT; - bc->opened--; - if (bc->opened <= 0) { - switch(bc->modem_type) { - case BAYCOM_MODEM_SER12: - ser12_on_close(bc); - break; - case BAYCOM_MODEM_PAR96: - par96_on_close(bc); - break; - } - tty->driver_data = NULL; - bc->tty = NULL; - bc->opened = 0; - /* - * free the buffers - */ - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->tx_buf.rd = bc->tx_buf.wr = 0; - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buffer = bc->tx_buf.buffer = NULL; - bc->rx_buf.buflen = bc->tx_buf.buflen = 0; - } -} - -/* --------------------------------------------------------------------- */ -/* - * And now the modules code and kernel interface. - */ - -static void init_channel(struct baycom_state *bc) -{ - struct access_params dflt_ch_params = { 20, 2, 10, 40, 0 }; - - if (!bc) - return; - - bc->hdlc_rx.rx_state = 0; - - bc->hdlc_tx.tx_state = bc->hdlc_tx.numflags = 0; - bc->hdlc_tx.bitstream = 0; - bc->hdlc_tx.current_byte = bc->hdlc_tx.ptt = 0; - - memset(&bc->modem, 0, sizeof(bc->modem)); - -#ifdef BAYCOM_DEBUG - bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0; - bc->bitbuf_channel.shreg = 0x80; - - bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0; - bc->bitbuf_hdlc.shreg = 0x80; -#endif /* BAYCOM_DEBUG */ - - bc->kiss_decode.dec_state = bc->kiss_decode.escaped = - bc->kiss_decode.wr = 0; - - bc->ch_params = dflt_ch_params; - -#ifdef BAYCOM_USE_BH - bc->tq_receiver.next = bc->tq_transmitter.next = - bc->tq_arbitrate.next = NULL; - bc->tq_receiver.sync = bc->tq_transmitter.sync = - bc->tq_arbitrate.sync = 0; - bc->tq_receiver.data = bc->tq_transmitter.data = - bc->tq_arbitrate.data = bc; - bc->tq_receiver.routine = bh_receiver; - bc->tq_transmitter.routine = bh_transmitter; - bc->tq_arbitrate.routine = bh_arbitrate; -#endif /* BAYCOM_USE_BH */ -} - -static void init_datastructs(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct baycom_state *bc = baycom_state+i; - - bc->magic = BAYCOM_MAGIC; - bc->modem_type = BAYCOM_MODEM_INVALID; - bc->iobase = bc->irq = bc->options = bc->opened = 0; - bc->tty = NULL; - - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->rx_buf.buflen = 0; - bc->rx_buf.buffer = NULL; - - bc->tx_buf.rd = bc->tx_buf.wr = 0; - bc->tx_buf.buflen = 0; - bc->tx_buf.buffer = NULL; - - memset(&bc->stat, 0, sizeof(bc->stat)); - - init_channel(bc); - } -} - -int baycom_init(void) { - int i, j; - - /* - * initialize the data structures - */ - init_datastructs(); - /* - * initialize bottom half handler - */ -#ifdef BAYCOM_USE_BH - init_bh(BAYCOM_BH, baycom_bottom_half); -#endif /* BAYCOM_USE_BH */ - /* - * register the driver as tty driver - */ - memset(&baycom_driver, 0, sizeof(struct tty_driver)); - baycom_driver.magic = TTY_DRIVER_MAGIC; - baycom_driver.name = "baycom"; - baycom_driver.major = major; - baycom_driver.minor_start = 0; - baycom_driver.num = NR_PORTS; - baycom_driver.type = TTY_DRIVER_TYPE_BAYCOM; - baycom_driver.subtype = BAYCOM_TYPE_NORMAL; - baycom_driver.init_termios.c_iflag = 0; - baycom_driver.init_termios.c_oflag = 0; - baycom_driver.init_termios.c_cflag = CS8 | B1200 | CREAD | CLOCAL; - baycom_driver.init_termios.c_lflag = 0; - baycom_driver.flags = TTY_DRIVER_REAL_RAW; - baycom_driver.refcount = &baycom_refcount; - baycom_driver.table = baycom_table; - baycom_driver.termios = baycom_termios; - baycom_driver.termios_locked = baycom_termios_locked; - /* - * the functions - */ - baycom_driver.open = baycom_open; - baycom_driver.close = baycom_close; - baycom_driver.write = baycom_write; - baycom_driver.put_char = baycom_put_char; - baycom_driver.flush_chars = NULL; - baycom_driver.write_room = baycom_write_room; - baycom_driver.chars_in_buffer = baycom_chars_in_buffer; - baycom_driver.flush_buffer = baycom_flush_buffer; - baycom_driver.ioctl = baycom_ioctl; - /* - * cannot throttle the transmitter on this layer - */ - baycom_driver.throttle = NULL; - baycom_driver.unthrottle = NULL; - /* - * no special actions on termio changes - */ - baycom_driver.set_termios = NULL; - /* - * no XON/XOFF and no hangup on the radio port - */ - baycom_driver.stop = NULL; - baycom_driver.start = NULL; - baycom_driver.hangup = NULL; - baycom_driver.set_ldisc = NULL; - - if (tty_register_driver(&baycom_driver)) { - printk(KERN_WARNING "baycom: tty_register_driver failed\n"); - return -EIO; - } - - for (i = 0; i < NR_PORTS && - baycom_ports[i].modem != BAYCOM_MODEM_INVALID; i++) { - j = baycom_set_hardware(baycom_state+i, - baycom_ports[i].modem, - baycom_ports[i].iobase, - baycom_ports[i].irq, - baycom_ports[i].options); - if (j < 0) { - const char *s; - switch (-j) { - case ENXIO: - s = "invalid iobase and/or irq"; - break; - case EACCES: - s = "io region already used"; - break; - case EIO: - s = "no uart/lpt port at iobase"; - break; - case EBUSY: - s = "interface already in use"; - break; - case EINVAL: - s = "internal error"; - break; - default: - s = "unknown error"; - break; - } - printk(KERN_WARNING "baycom: modem %u iobase 0x%x " - "irq %u: (%i) %s\n", baycom_ports[i].modem, - baycom_ports[i].iobase, baycom_ports[i].irq, - j, s); - } - } - - return 0; -} - -/* --------------------------------------------------------------------- */ - -#ifdef MODULE - -int modem = BAYCOM_MODEM_INVALID; -int iobase = 0x3f8; -int irq = 4; -int options = BAYCOM_OPTIONS_SOFTDCD; - -int init_module(void) -{ - int i; - - printk(KERN_INFO "baycom: init_module called\n"); - - baycom_ports[0].modem = modem; - baycom_ports[0].iobase = iobase; - baycom_ports[0].irq = irq; - baycom_ports[0].options = options; - baycom_ports[1].modem = BAYCOM_MODEM_INVALID; - - i = baycom_init(); - if (i) - return i; - - printk(KERN_INFO "baycom: version 0.3; " - "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n"); - - return 0; -} - -/* --------------------------------------------------------------------- */ - -void cleanup_module(void) -{ - int i; - - printk(KERN_INFO "baycom: cleanup_module called\n"); - - disable_bh(BAYCOM_BH); - if (tty_unregister_driver(&baycom_driver)) - printk(KERN_WARNING "baycom: failed to unregister tty " - "driver\n"); - for(i = 0; i < NR_PORTS; i++) { - struct baycom_state *bc = baycom_state+i; - - if (bc->magic != BAYCOM_MAGIC) - printk(KERN_ERR "baycom: invalid magic in " - "cleanup_module\n"); - else { - baycom_dealloc_hw(bc); - /* - * free the buffers - */ - bc->rx_buf.rd = bc->rx_buf.wr = 0; - bc->tx_buf.rd = bc->tx_buf.wr = 0; - if (bc->rx_buf.buffer) - kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen); - if (bc->tx_buf.buffer) - kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen); - bc->rx_buf.buffer = bc->tx_buf.buffer = NULL; - bc->rx_buf.buflen = bc->tx_buf.buflen = 0; - } - } -} - -#else /* MODULE */ -/* --------------------------------------------------------------------- */ -/* - * format: baycom=modem,io,irq,options[,modem,io,irq,options] - * modem=1: ser12, modem=2: par96 - * options=0: hardware DCD, options=1: software DCD - */ - -void baycom_setup(char *str, int *ints) -{ - int i; - - for (i = 0; i < NR_PORTS; i++) - if (ints[0] >= 4*i+4) { - baycom_ports[i].modem = ints[4*i+1]; - baycom_ports[i].iobase = ints[4*i+2]; - baycom_ports[i].irq = ints[4*i+3]; - baycom_ports[i].options = ints[4*i+4]; - } else - baycom_ports[i].modem = BAYCOM_MODEM_INVALID; - -} - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-ctl.c linux/drivers/char/ftape/ftape-ctl.c --- v2.1.6/linux/drivers/char/ftape/ftape-ctl.c Sat Oct 19 10:07:30 1996 +++ linux/drivers/char/ftape/ftape-ctl.c Wed Oct 30 01:40:36 1996 @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "tracing.h" #include "ftape-eof.h" diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-io.c linux/drivers/char/ftape/ftape-io.c --- v2.1.6/linux/drivers/char/ftape/ftape-io.c Sat Apr 13 11:23:24 1996 +++ linux/drivers/char/ftape/ftape-io.c Wed Oct 30 01:40:36 1996 @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-read.c linux/drivers/char/ftape/ftape-read.c --- v2.1.6/linux/drivers/char/ftape/ftape-read.c Sat Oct 19 10:07:30 1996 +++ linux/drivers/char/ftape/ftape-read.c Wed Oct 30 01:40:36 1996 @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "tracing.h" #include "ftape-read.h" diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/ftape-write.c linux/drivers/char/ftape/ftape-write.c --- v2.1.6/linux/drivers/char/ftape/ftape-write.c Sat Oct 19 10:07:30 1996 +++ linux/drivers/char/ftape/ftape-write.c Wed Oct 30 01:40:36 1996 @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "tracing.h" #include "ftape-write.h" diff -u --recursive --new-file v2.1.6/linux/drivers/char/ftape/kernel-interface.c linux/drivers/char/ftape/kernel-interface.c --- v2.1.6/linux/drivers/char/ftape/kernel-interface.c Mon Sep 30 10:43:38 1996 +++ linux/drivers/char/ftape/kernel-interface.c Wed Oct 30 01:40:36 1996 @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff -u --recursive --new-file v2.1.6/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.1.6/linux/drivers/char/misc.c Thu Oct 10 19:10:54 1996 +++ linux/drivers/char/misc.c Tue Oct 29 20:13:02 1996 @@ -184,12 +184,14 @@ #include }; +#if defined(CONFIG_PROC_FS) && !defined(MODULE) static struct proc_dir_entry proc_misc = { 0, 4, "misc", S_IFREG | S_IRUGO, 1, 0, 0, 0, NULL /* ops -- default to array */, &proc_misc_read /* get_info */, }; +#endif int misc_init(void) { diff -u --recursive --new-file v2.1.6/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.1.6/linux/drivers/char/tty_io.c Tue Oct 29 19:58:10 1996 +++ linux/drivers/char/tty_io.c Thu Oct 31 11:54:25 1996 @@ -1941,9 +1941,6 @@ #ifdef CONFIG_RISCOM8 riscom8_init(); #endif -#ifdef CONFIG_BAYCOM - baycom_init(); -#endif pty_init(); vcs_init(); return 0; diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- v2.1.6/linux/drivers/isdn/isdn_common.c Tue Oct 29 19:58:10 1996 +++ linux/drivers/isdn/isdn_common.c Wed Oct 30 14:27:57 1996 @@ -117,6 +117,7 @@ * */ +#include #include #include #include @@ -915,7 +916,7 @@ wake_up_interruptible(&(dev->info_waitq)); } -static int isdn_read(struct inode *inode, struct file *file, char *buf, int count) +static long isdn_read(struct inode *inode, struct file *file, char *buf, unsigned long count) { uint minor = MINOR(inode->i_rdev); int len = 0; @@ -988,12 +989,13 @@ return -ENODEV; } -static int isdn_lseek(struct inode *inode, struct file *file, off_t offset, int orig) +static long long isdn_lseek(struct inode *inode, struct file *file, long long offset, int orig) { return -ESPIPE; } -static int isdn_write(struct inode *inode, struct file *file, const char *buf, int count) +static long isdn_write(struct inode *inode, struct file *file, + const char *buf, unsigned long count) { uint minor = MINOR(inode->i_rdev); int drvidx; diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c --- v2.1.6/linux/drivers/isdn/isdn_net.c Tue Oct 29 19:58:10 1996 +++ linux/drivers/isdn/isdn_net.c Wed Oct 30 14:26:48 1996 @@ -106,6 +106,7 @@ * */ +#include #include #define __NO_VERSION__ #include diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- v2.1.6/linux/drivers/isdn/isdn_ppp.c Tue Oct 29 19:58:10 1996 +++ linux/drivers/isdn/isdn_ppp.c Wed Oct 30 14:26:48 1996 @@ -73,6 +73,7 @@ /* TODO: right tbusy handling when using MP */ +#include #include #define __NO_VERSION__ #include @@ -391,7 +392,7 @@ switch (cmd) { case PPPIOCBUNDLE: #ifdef CONFIG_ISDN_MPP - if ((r = get_user(val, arg))) + if ((r = get_user(val, argp))) return r; printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", (int) min, (int) is->unit, (int) val); @@ -588,7 +589,6 @@ { struct ippp_struct *is; struct ippp_buf_queue *b; - int r; unsigned long flags; is = file->private_data; @@ -1445,7 +1445,6 @@ { struct ppp_stats *res, t; isdn_net_local *lp = (isdn_net_local *) dev->priv; - int err; res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; @@ -1476,7 +1475,7 @@ int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { - int error; + int error = 0; char *r; int len; isdn_net_local *lp = (isdn_net_local *) dev->priv; diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c --- v2.1.6/linux/drivers/isdn/isdn_tty.c Tue Oct 29 19:58:11 1996 +++ linux/drivers/isdn/isdn_tty.c Wed Oct 30 14:26:48 1996 @@ -109,6 +109,7 @@ * */ +#include #define __NO_VERSION__ #include #include diff -u --recursive --new-file v2.1.6/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c --- v2.1.6/linux/drivers/isdn/teles/callc.c Tue Oct 29 19:58:11 1996 +++ linux/drivers/isdn/teles/callc.c Wed Oct 30 14:26:48 1996 @@ -47,6 +47,7 @@ */ #define __NO_VERSION__ #include "teles.h" +#include extern struct IsdnCard cards[]; extern int nrcards; diff -u --recursive --new-file v2.1.6/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.1.6/linux/drivers/net/Config.in Thu Oct 10 19:10:55 1996 +++ linux/drivers/net/Config.in Wed Oct 30 14:15:12 1996 @@ -25,7 +25,8 @@ bool 'Radio network interfaces' CONFIG_NET_RADIO if [ "$CONFIG_NET_RADIO" != "n" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'BAYCOM ser12 and par96 kiss emulation driver for AX.25' CONFIG_BAYCOM + tristate 'BAYCOM ser12 and par96 driver for AX.25' CONFIG_BAYCOM + tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM fi if [ "$CONFIG_AX25" = "y" ]; then bool 'Gracilis PackeTwin support' CONFIG_PT diff -u --recursive --new-file v2.1.6/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.1.6/linux/drivers/net/Makefile Tue Oct 29 19:58:11 1996 +++ linux/drivers/net/Makefile Wed Oct 30 14:15:12 1996 @@ -18,6 +18,8 @@ CONFIG_8390_MODULE := CONFIG_SLHC_BUILTIN := CONFIG_SLHC_MODULE := +CONFIG_HDLCDRV_BUILTIN := +CONFIG_HDLCDRV_MODULE := ifeq ($(CONFIG_ISDN),y) ifeq ($(CONFIG_ISDN_PPP),y) @@ -517,6 +519,37 @@ M_OBJS += dgrs.o endif endif + +ifeq ($(CONFIG_BAYCOM),y) +L_OBJS += baycom.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_BAYCOM),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += baycom.o + endif +endif + +ifeq ($(CONFIG_SOUNDMODEM),y) +L_OBJS += soundmodem.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_SOUNDMODEM),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += soundmodem.o + endif +endif + +# If anything built-in uses the hdlcdrv, then build it into the kernel also. +# If not, but a module uses it, build as a module. +ifdef CONFIG_HDLCDRV_BUILTIN +LX_OBJS += hdlcdrv.o +else + ifdef CONFIG_HDLCDRV_MODULE + MX_OBJS += hdlcdrv.o + endif +endif + include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.6/linux/drivers/net/README.baycom linux/drivers/net/README.baycom --- v2.1.6/linux/drivers/net/README.baycom Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/README.baycom Wed Oct 30 14:15:12 1996 @@ -0,0 +1,118 @@ + LINUX DRIVER FOR BAYCOM MODEMS + + Thomas M. Sailer, HB9JNX/AE4WA, + +This document describes the Linux Kernel Driver for simple Baycom style +amateur radio modems. The driver supports the following modems: + +ser12: This is a very simple 1200 baud AFSK modem. The modem consists only + of a modulator/demodulator chip, usually a TI TCM3105. The computer + is responsible for regenerating the receiver bit clock, as well as + for handling the HDLC protocol. The modem connects to a serial port, + hence the name. Since the serial port is not used as an async serial + port, the kernel driver for serial ports cannot be used, and this + driver only supports standard serial hardware (8250, 16450, 16550) + +par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. + The modem does all the filtering and regenerates the receiver clock. + Data is transferred from and to the PC via a shift register. + The shift register is filled with 16 bits and an interrupt is signalled. + The PC then empties the shift register in a burst. This modem connects + to the parallel port, hence the name. The modem leaves the + implementation of the HDLC protocol and the scrambler polynomial to + the PC. + +picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem + is protocol compatible to par96, but uses only three low power ICs + and can therefore be fed from the parallel port and does not require + an additional power supply. Furthermore, it incorporates a carrier + detect circuitry. + +All of the above modems only support half duplex communications. However, +the driver supports the KISS (see below) fullduplex command. It then simply +starts to send as soon as there's a packet to transmit and does not care +about DCD, i.e. it starts to send even if there's someone else on the channel. +This command is required by some implementations of the DAMA channel +access protocol. + + +The Interface of the driver + +Unlike previous drivers, the driver is no longer a character device, +but it is now a true kernel network interface. Installation is therefore +simple. Once installed, four interfaces named bc[0-3] are available. +sethdlc from the ax25 utilities may be used to set driver states etc. +Users of userland AX.25 stacks may use the net2kiss utility (also available +in the ax25 utilities package) to converts packets of a network interface +to a KISS stream on a pseudo tty. There's also a patch available from +me for WAMPES which allows attaching a kernel network interface directly. + + +Configuring the driver + +Every time the driver is inserted into the kernel, it has to know which +modems it should access at which ports. This can be done with the setbaycom +utility. If you are only using one modem, you can also configure the +driver from the insmod command line (or by means of an option line in +/etc/conf.modules). + +Examples: + insmod baycom modem=1 iobase=0x3f8 irq=4 options=1 + sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1 + +Both lines configure the first port to drive a ser12 modem at the first +serial port (COM1 under DOS). options=1 instructs the driver to use +the software DCD algorithm (see below). + + insmod baycom modem=2 iobase=0x378 irq=7 options=1 + sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1 + +Both lines configure the first port to drive a par96 or par97 modem at the +first parallel port (LPT1 under DOS). options=1 instructs the driver to use +the software DCD algorithm (see below). + +The channel access parameters can be set with sethdlc -a or kissparms. +Note that both utilities interpret the values slightly different. + + +Hardware DCD versus Software DCD + +To avoid collisions on the air, the driver must know when the channel is +busy. This is the task of the DCD circuitry/software. The driver may either +utilise a software DCD algorithm (options=1) or use a DCD signal from +the hardware (options=0). + +ser12: if software DCD is utilised, the radio's squelch should always be + open. It is highly recommended to use the software DCD algorithm, + as it is much faster than most hardware squelch circuitry. The + disadvantage is a slightly higher load on the system. + +par96: the software DCD algorithm for this type of modem is rather poor. + The modem simply does not provide enough information to implement + a reasonable DCD algorithm in software. Therefore, if your radio + feeds the DCD input of the PAR96 modem, the use of the hardware + DCD circuitry is recommended. + +picpar: the picpar modem features a builtin DCD hardware, which is highly + recommended. + + + +Compatibility with the rest of the Linux kernel + +The serial driver, the line printer (lp) driver and the baycom driver compete +for the same hardware resources. Of course only one driver can access a given +interface at a time. The serial driver grabs all interfaces it can find at +startup time. Therefore the baycom driver subsequently won't be able to +access a serial port. You might therefore find it necessary to release +a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where +# is the number of the interface. The baycom driver does not reserve any +port at startup, unless one is specified on the 'insmod' command line. Another +method to solve the problem is to compile all three drivers as modules and +leave it to kerneld to load the correct driver depending on the application. + + + +vy 73s de +Tom Sailer, sailer@ife.ee.ethz.ch +hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.6/linux/drivers/net/README.soundmodem linux/drivers/net/README.soundmodem --- v2.1.6/linux/drivers/net/README.soundmodem Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/README.soundmodem Wed Oct 30 14:15:12 1996 @@ -0,0 +1,88 @@ + LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS + + Thomas M. Sailer, HB9JNX/AE4WA, + +This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss) +compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK +AX.25 packet radio modems. Only half duplex operation is supported; an +attempt to include full duplex support failed because the hardware did +not support it (it appeared that the card only provides one DMA channel, +although the Codec chip would support two channels). The driver needs +some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise +interactive performance of the computer may become sluggish. + + +The Interface of the driver + +The driver provides a kernel network drivers named sm[0-3]. sethdlc +from the ax25 utilities may be used to set driver states etc. Users +of userland AX.25 stacks may use the net2kiss utility (also available +in the ax25 utilities package) to converts packets of a network interface +to a KISS stream on a pseudo tty. There's also a patch available from +me for WAMPES which allows attaching a kernel network interface directly. + + +Configuring the driver + +Some sound cards need to be initialized before they operate in either +SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this; +you may use the standard linux sound driver to initialize the soundcard; +compile it as a module, and do + insmod sound + rmmod sound +The soundcard should then be initialized correctly. If this does not help, +you'll have to write your own initialization utility. + +Every time the driver is inserted into the kernel, it has to know which +modems it should access at which ports. This can be done with the setbaycom +utility. If you are only using one modem, you can also configure the +driver from the insmod command line (or by means of an option line in +/etc/conf.modules). + +Examples: + insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1 + sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1 + +Both lines configure the first port to drive a soundblaster card +in 1200 baud AFSK mode. + +The channel access parameters can be set with sethdlc -a or kissparms. +Note that both utilities interpret the values slightly different. + + +Input and output levels + +It is important that the input and output levels are adjusted properly. +There are two utilities, available in the ax25 utilities distribution, +to facilitate this: smmixer and smdiag. smdiag allows you to display +the input signal in an oscilloscope like display or an eye diagram. +smmixer allows you to adjust input/output levels. See the respective +man pages. + + +Transmitter keying + +Since soundcards do not have a DC coupled output; PTT keying options include +the following: +* VOX circuitry +* Serial port pin +* Parallel port pin +* MPU401 MIDI output via a retriggerable monoflop. +Circuit schematics may be found at +http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html. + + +Compatibility with the rest of the Linux kernel + +The sound driver and the soundcard modem driver compete for the same +hardware resources. Of course only one driver can access a given +interface at a time. Worse yet, the sound driver grabs the soundcard +at startup time. Therefore the soundcard modem driver subsequently won't +be able to access the soundcard. You might therefore find it necessary to +unload the sound driver before using the soundcard modem driver. + + + +vy 73s de +Tom Sailer, sailer@ife.ee.ethz.ch +hb9jnx @ hb9w.ampr.org diff -u --recursive --new-file v2.1.6/linux/drivers/net/baycom.c linux/drivers/net/baycom.c --- v2.1.6/linux/drivers/net/baycom.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/baycom.c Wed Oct 30 14:15:12 1996 @@ -0,0 +1,969 @@ +/*****************************************************************************/ + +/* + * baycom.c -- baycom ser12 and par96 radio modem driver. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + * + * Supported modems + * + * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only + * of a modulator/demodulator chip, usually a TI TCM3105. The computer + * is responsible for regenerating the receiver bit clock, as well as + * for handling the HDLC protocol. The modem connects to a serial port, + * hence the name. Since the serial port is not used as an async serial + * port, the kernel driver for serial ports cannot be used, and this + * driver only supports standard serial hardware (8250, 16450, 16550) + * + * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. + * The modem does all the filtering and regenerates the receiver clock. + * Data is transferred from and to the PC via a shift register. + * The shift register is filled with 16 bits and an interrupt is + * signalled. The PC then empties the shift register in a burst. This + * modem connects to the parallel port, hence the name. The modem + * leaves the implementation of the HDLC protocol and the scrambler + * polynomial to the PC. This modem is no longer available (at least + * from Baycom) and has been replaced by the PICPAR modem (see below). + * You may however still build one from the schematics published in + * cq-DL :-). + * + * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The + * modem is protocol compatible to par96, but uses only three low + * power ICs and can therefore be fed from the parallel port and + * does not require an additional power supply. + * + * + * Command line options (insmod command line) + * + * modem modem type of the first channel; 1=ser12, + * 2=par96/par97, any other value invalid + * iobase base address of the port; common values are for ser12 0x3f8, + * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc + * irq interrupt line of the port; common values are for ser12 3,4 + * and for par96/par97 7 + * options 0=use hardware DCD, 1=use software DCD + * + * + * History: + * 0.1 26.06.96 Adapted from baycom.c and made network driver interface + * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + */ + +/*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#define BAYCOM_DEBUG + +/* --------------------------------------------------------------------- */ + +#define NR_PORTS 4 + +static struct device baycom_device[NR_PORTS]; + +static struct { + int modem, iobase, irq, options; +} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, }; + +/* --------------------------------------------------------------------- */ + +#define RBR(iobase) (iobase+0) +#define THR(iobase) (iobase+0) +#define IER(iobase) (iobase+1) +#define IIR(iobase) (iobase+2) +#define FCR(iobase) (iobase+2) +#define LCR(iobase) (iobase+3) +#define MCR(iobase) (iobase+4) +#define LSR(iobase) (iobase+5) +#define MSR(iobase) (iobase+6) +#define SCR(iobase) (iobase+7) +#define DLL(iobase) (iobase+0) +#define DLM(iobase) (iobase+1) + +#define SER12_EXTENT 8 + +#define LPT_DATA(iobase) (iobase+0) +#define LPT_STATUS(iobase) (iobase+1) +#define LPT_CONTROL(iobase) (iobase+2) +#define LPT_IRQ_ENABLE 0x10 +#define PAR96_BURSTBITS 16 +#define PAR96_BURST 4 +#define PAR96_PTT 2 +#define PAR96_TXBIT 1 +#define PAR96_ACK 0x40 +#define PAR96_RXBIT 0x20 +#define PAR96_DCD 0x10 +#define PAR97_POWER 0xf8 + +#define PAR96_EXTENT 3 + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct baycom_state { + struct hdlcdrv_state hdrv; + + unsigned int options; + + struct modem_state { + short arb_divider; + unsigned char flags; + unsigned int shreg; + struct modem_state_ser12 { + unsigned char last_sample; + unsigned char interm_sample; + unsigned int bit_pll; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; + unsigned char tx_bit; + } ser12; + struct modem_state_par96 { + int dcd_count; + unsigned int dcd_shreg; + unsigned long descram; + unsigned long scram; + } par96; + } modem; + +#ifdef BAYCOM_DEBUG + struct debug_vals { + unsigned long last_jiffies; + unsigned cur_intcnt; + unsigned last_intcnt; + int cur_pllcorr; + int last_pllcorr; + } debug_vals; +#endif /* BAYCOM_DEBUG */ +}; + +/* --------------------------------------------------------------------- */ + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* --------------------------------------------------------------------- */ +/* --------------------------------------------------------------------- */ +/* --------------------------------------------------------------------- */ + +static void inline baycom_int_freq(struct baycom_state *bc) +{ +#ifdef BAYCOM_DEBUG + unsigned long cur_jiffies = jiffies; + /* + * measure the interrupt frequency + */ + bc->debug_vals.cur_intcnt++; + if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) { + bc->debug_vals.last_jiffies = cur_jiffies; + bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; + bc->debug_vals.cur_intcnt = 0; + bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; + bc->debug_vals.cur_pllcorr = 0; + } +#endif /* BAYCOM_DEBUG */ +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== SER12 specific routines ========================= + */ + +static void inline ser12_set_divisor(struct device *dev, + unsigned char divisor) +{ + outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ + outb(divisor, DLL(dev->base_addr)); + outb(0, DLM(dev->base_addr)); + outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ + /* + * make sure the next interrupt is generated; + * 0 must be used to power the modem; the modem draws its + * power from the TxD line + */ + outb(0x00, THR(dev->base_addr)); + /* + * it is important not to set the divider while transmitting; + * this reportedly makes some UARTs generating interrupts + * in the hundredthousands per second region + * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) + */ +} + +/* --------------------------------------------------------------------- */ + +/* + * must call the TX arbitrator every 10ms + */ +#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ + 36 : 24) +#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \ + 240 : 12) + +static inline void ser12_tx(struct device *dev, struct baycom_state *bc) +{ + /* one interrupt per channel bit */ + ser12_set_divisor(dev, 12); + /* + * first output the last bit (!) then call HDLC transmitter, + * since this may take quite long + */ + outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); + if (bc->modem.shreg <= 1) + bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); + bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ + (bc->modem.shreg & 1)); + bc->modem.shreg >>= 1; +} + +/* --------------------------------------------------------------------- */ + +static inline void ser12_rx(struct device *dev, struct baycom_state *bc) +{ + unsigned char cur_s; + /* + * do demodulator + */ + cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */ + hdlcdrv_channelbit(&bc->hdrv, cur_s); + bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) | + (cur_s != bc->modem.ser12.last_sample); + bc->modem.ser12.last_sample = cur_s; + if(bc->modem.ser12.dcd_shreg & 1) { + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { + unsigned int dcdspos, dcdsneg; + + dcdspos = dcdsneg = 0; + dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); + if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) + dcdspos += 2; + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); + + bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; + } else + bc->modem.ser12.dcd_sum0--; + } + if(!bc->modem.ser12.dcd_time) { + hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + + bc->modem.ser12.dcd_sum1 + + bc->modem.ser12.dcd_sum2) < 0); + bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; + bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; + /* offset to ensure DCD off on silent input */ + bc->modem.ser12.dcd_sum0 = 2; + bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); + } + bc->modem.ser12.dcd_time--; + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { + /* + * PLL code for the improved software DCD algorithm + */ + if (bc->modem.ser12.interm_sample) { + /* + * intermediate sample; set timing correction to normal + */ + ser12_set_divisor(dev, 4); + } else { + /* + * do PLL correction and call HDLC receiver + */ + switch (bc->modem.ser12.dcd_shreg & 7) { + case 1: /* transition too late */ + ser12_set_divisor(dev, 5); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr++; +#endif /* BAYCOM_DEBUG */ + break; + case 4: /* transition too early */ + ser12_set_divisor(dev, 3); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr--; +#endif /* BAYCOM_DEBUG */ + break; + default: + ser12_set_divisor(dev, 4); + break; + } + bc->modem.shreg >>= 1; + if (bc->modem.ser12.last_sample == + bc->modem.ser12.last_rxbit) + bc->modem.shreg |= 0x10000; + bc->modem.ser12.last_rxbit = + bc->modem.ser12.last_sample; + } + if (++bc->modem.ser12.interm_sample >= 3) + bc->modem.ser12.interm_sample = 0; + /* + * DCD stuff + */ + if (bc->modem.ser12.dcd_shreg & 1) { + unsigned int dcdspos, dcdsneg; + + dcdspos = dcdsneg = 0; + dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); + dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) + << 1; + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); + dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); + + bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; + } + } else { + /* + * PLL algorithm for the hardware squelch DCD algorithm + */ + if (bc->modem.ser12.interm_sample) { + /* + * intermediate sample; set timing correction to normal + */ + ser12_set_divisor(dev, 6); + } else { + /* + * do PLL correction and call HDLC receiver + */ + switch (bc->modem.ser12.dcd_shreg & 3) { + case 1: /* transition too late */ + ser12_set_divisor(dev, 7); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr++; +#endif /* BAYCOM_DEBUG */ + break; + case 2: /* transition too early */ + ser12_set_divisor(dev, 5); +#ifdef BAYCOM_DEBUG + bc->debug_vals.cur_pllcorr--; +#endif /* BAYCOM_DEBUG */ + break; + default: + ser12_set_divisor(dev, 6); + break; + } + bc->modem.shreg >>= 1; + if (bc->modem.ser12.last_sample == + bc->modem.ser12.last_rxbit) + bc->modem.shreg |= 0x10000; + bc->modem.ser12.last_rxbit = + bc->modem.ser12.last_sample; + } + bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; + /* + * DCD stuff + */ + bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1); + } + outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ + if (bc->modem.shreg & 1) { + hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); + bc->modem.shreg = 0x10000; + } + if(!bc->modem.ser12.dcd_time) { + hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + + bc->modem.ser12.dcd_sum1 + + bc->modem.ser12.dcd_sum2) < 0); + bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; + bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; + /* offset to ensure DCD off on silent input */ + bc->modem.ser12.dcd_sum0 = 2; + bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); + } + bc->modem.ser12.dcd_time--; +} + +/* --------------------------------------------------------------------- */ + +static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; + + baycom_int_freq(bc); + /* + * check if transmitter active + */ + if (hdlcdrv_ptt(&bc->hdrv)) + ser12_tx(dev, bc); + else { + ser12_rx(dev, bc); + if (--bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = SER12_ARB_DIVIDER(bc); + sti(); + hdlcdrv_arbitrate(dev, &bc->hdrv); + } + } + sti(); + hdlcdrv_transmitter(dev, &bc->hdrv); + hdlcdrv_receiver(dev, &bc->hdrv); +} + +/* --------------------------------------------------------------------- */ + +enum uart { c_uart_unknown, c_uart_8250, + c_uart_16450, c_uart_16550, c_uart_16550A}; +static const char *uart_str[] = + { "unknown", "8250", "16450", "16550", "16550A" }; + +static enum uart ser12_check_uart(unsigned int iobase) +{ + unsigned char b1,b2,b3; + enum uart u; + enum uart uart_tab[] = + { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; + + b1 = inb(MCR(iobase)); + outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ + b2 = inb(MSR(iobase)); + outb(0x1a, MCR(iobase)); + b3 = inb(MSR(iobase)) & 0xf0; + outb(b1, MCR(iobase)); /* restore old values */ + outb(b2, MSR(iobase)); + if (b3 != 0x90) + return c_uart_unknown; + inb(RBR(iobase)); + inb(RBR(iobase)); + outb(0x01, FCR(iobase)); /* enable FIFOs */ + u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; + if (u == c_uart_16450) { + outb(0x5a, SCR(iobase)); + b1 = inb(SCR(iobase)); + outb(0xa5, SCR(iobase)); + b2 = inb(SCR(iobase)); + if ((b1 != 0x5a) || (b2 != 0xa5)) + u = c_uart_8250; + } + return u; +} + +/* --------------------------------------------------------------------- */ + +static int ser12_open(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + enum uart u; + + if (!dev || !bc) + return -ENXIO; + if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || + dev->irq < 2 || dev->irq > 15) + return -ENXIO; + if (check_region(dev->base_addr, SER12_EXTENT)) + return -EACCES; + memset(&bc->modem, 0, sizeof(bc->modem)); + if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) + return -EIO; + outb(0, FCR(dev->base_addr)); /* disable FIFOs */ + outb(0x0d, MCR(dev->base_addr)); + outb(0x0d, MCR(dev->base_addr)); + outb(0, IER(dev->base_addr)); + if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT, + "baycom_ser12", dev)) + return -EBUSY; + request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"); + /* + * enable transmitter empty interrupt + */ + outb(2, IER(dev->base_addr)); + /* + * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that + * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, + * depending on the usage of the software DCD routine + */ + ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6); + printk(KERN_INFO "baycom: ser12 at iobase 0x%lx irq %u options " + "0x%x uart %s\n", dev->base_addr, dev->irq, bc->options, + uart_str[u]); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int ser12_close(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc) + return -EINVAL; + /* + * disable interrupts + */ + outb(0, IER(dev->base_addr)); + outb(1, MCR(dev->base_addr)); + free_irq(dev->irq, dev); + release_region(dev->base_addr, SER12_EXTENT); + printk(KERN_INFO "baycom: close ser12 at iobase 0x%lx irq %u\n", + dev->base_addr, dev->irq); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== PAR96 specific routines ========================= + */ + +#define PAR96_DESCRAM_TAP1 0x20000 +#define PAR96_DESCRAM_TAP2 0x01000 +#define PAR96_DESCRAM_TAP3 0x00001 + +#define PAR96_DESCRAM_TAPSH1 17 +#define PAR96_DESCRAM_TAPSH2 12 +#define PAR96_DESCRAM_TAPSH3 0 + +#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ +#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ + +/* --------------------------------------------------------------------- */ + +static inline void par96_tx(struct device *dev, struct baycom_state *bc) +{ + int i; + unsigned int data = hdlcdrv_getbits(&bc->hdrv); + + for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { + unsigned char val = PAR97_POWER; + bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | + (bc->modem.par96.scram & 1)); + if (!(data & 1)) + bc->modem.par96.scram ^= 1; + if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) + bc->modem.par96.scram ^= + (PAR96_SCRAM_TAPN << 1); + if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) + val |= PAR96_TXBIT; + outb(val, LPT_DATA(dev->base_addr)); + outb(val | PAR96_BURST, LPT_DATA(dev->base_addr)); + } +} + +/* --------------------------------------------------------------------- */ + +static inline void par96_rx(struct device *dev, struct baycom_state *bc) +{ + int i; + unsigned int data, mask, mask2, descx; + + /* + * do receiver; differential decode and descramble on the fly + */ + for(data = i = 0; i < PAR96_BURSTBITS; i++) { + bc->modem.par96.descram = (bc->modem.par96.descram << 1); + if (inb(LPT_STATUS(dev->base_addr)) & PAR96_RXBIT) + bc->modem.par96.descram |= 1; + descx = bc->modem.par96.descram ^ + (bc->modem.par96.descram >> 1); + /* now the diff decoded data is inverted in descram */ + outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev->base_addr)); + descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ + (descx >> PAR96_DESCRAM_TAPSH2)); + data >>= 1; + if (!(descx & 1)) + data |= 0x8000; + outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, + LPT_DATA(dev->base_addr)); + } + hdlcdrv_putbits(&bc->hdrv, data); + /* + * do DCD algorithm + */ + if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { + bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) + | (data << 16); + /* search for flags and set the dcd counter appropriately */ + for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; + i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) + if ((bc->modem.par96.dcd_shreg & mask) == mask2) + bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4; + /* check for abort/noise sequences */ + for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; + i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) + if (((bc->modem.par96.dcd_shreg & mask) == mask2) && + (bc->modem.par96.dcd_count >= 0)) + bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10; + /* decrement and set the dcd variable */ + if (bc->modem.par96.dcd_count >= 0) + bc->modem.par96.dcd_count -= 2; + hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0); + } else { + hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev->base_addr)) + & PAR96_DCD)); + } +} + +/* --------------------------------------------------------------------- */ + +static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; + + baycom_int_freq(bc); + /* + * check if transmitter active + */ + if (hdlcdrv_ptt(&bc->hdrv)) + par96_tx(dev, bc); + else { + par96_rx(dev, bc); + if (--bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = 6; + sti(); + hdlcdrv_arbitrate(dev, &bc->hdrv); + } + } + sti(); + hdlcdrv_transmitter(dev, &bc->hdrv); + hdlcdrv_receiver(dev, &bc->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int par96_check_lpt(unsigned int iobase) +{ + unsigned char b1,b2; + int i; + + b1 = inb(LPT_DATA(iobase)); + b2 = inb(LPT_CONTROL(iobase)); + outb(0xaa, LPT_DATA(iobase)); + i = inb(LPT_DATA(iobase)) == 0xaa; + outb(0x55, LPT_DATA(iobase)); + i &= inb(LPT_DATA(iobase)) == 0x55; + outb(0x0a, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; + outb(0x05, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; + outb(b1, LPT_DATA(iobase)); + outb(b2, LPT_CONTROL(iobase)); + return !i; +} + +/* --------------------------------------------------------------------- */ + +static int par96_open(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc) + return -ENXIO; + if (!dev->base_addr || dev->base_addr > 0x1000-PAR96_EXTENT || + dev->irq < 2 || dev->irq > 15) + return -ENXIO; + if (check_region(dev->base_addr, PAR96_EXTENT)) + return -EACCES; + memset(&bc->modem, 0, sizeof(bc->modem)); + if (par96_check_lpt(dev->base_addr)) + return -EIO; + /* disable interrupt */ + outb(0, LPT_CONTROL(dev->base_addr)); + /* switch off PTT */ + outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr)); + printk(KERN_INFO "baycom: par96 at iobase 0x%lx irq %u " + "options 0x%x\n", dev->base_addr, dev->irq, bc->options); + if (request_irq(dev->irq, par96_interrupt, SA_INTERRUPT, + "baycom_par96", dev)) + return -EBUSY; + request_region(dev->base_addr, PAR96_EXTENT, "baycom_par96"); + /* enable interrupt */ + outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev->base_addr)); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int par96_close(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (!dev || !bc) + return -EINVAL; + /* disable interrupt */ + outb(0, LPT_CONTROL(dev->base_addr)); + /* switch off PTT */ + outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr)); + free_irq(dev->irq, dev); + release_region(dev->base_addr, PAR96_EXTENT); + printk(KERN_INFO "baycom: close par96 at iobase 0x%lx irq %u\n", + dev->base_addr, dev->irq); + MOD_DEC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== hdlcdrv driver interface ========================= + */ + +/* --------------------------------------------------------------------- */ + +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd); + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops ser12_ops = { + 1200, + ser12_open, + ser12_close, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops par96_ops = { + 9600, + par96_open, + par96_close, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops dummy_ops = { + 0, + NULL, + NULL, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static struct hdlcdrv_ops *ops_tab[3] = { + &dummy_ops, &ser12_ops, &par96_ops +}; + +/* --------------------------------------------------------------------- */ + +static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct baycom_state *bc; + struct baycom_ioctl bi; + unsigned long flags; + + if (!dev || !dev->priv || + ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "bc_ioctl: invalid device struct\n"); + return -EINVAL; + } + bc = (struct baycom_state *)dev->priv; + + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + + switch (bi.cmd) { + default: + return -ENOIOCTLCMD; + + case BAYCOMCTL_GETMODEMTYPE: + bi.data.mt.modem_type = BAYCOM_MODEM_INVALID; + if (bc->hdrv.ops == &ser12_ops) + bi.data.mt.modem_type = BAYCOM_MODEM_SER12; + else if (bc->hdrv.ops == &par96_ops) + bi.data.mt.modem_type = BAYCOM_MODEM_PAR96; + else if (bc->hdrv.ops != &dummy_ops) { + printk(KERN_ERR "baycom: BAYCOMCTL_GETMODEMTYPE: " + "modem ops invalid\n"); + } + bi.data.mt.options = bc->options; + break; + + case BAYCOMCTL_SETMODEMTYPE: + if (!suser() || dev->start) + return -EACCES; + if (bi.data.mt.modem_type < BAYCOM_MODEM_SER12 || + bi.data.mt.modem_type > BAYCOM_MODEM_PAR96) + return -EINVAL; + save_flags(flags); + cli(); + bc->hdrv.ops = ops_tab[bi.data.mt.modem_type]; + bc->options = bi.data.mt.options; + restore_flags(flags); + return 0; + +#ifdef BAYCOM_DEBUG + case BAYCOMCTL_GETDEBUG: + bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; + bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; + bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; + break; +#endif /* BAYCOM_DEBUG */ + + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +static +#endif /* MODULE */ +int baycom_init(void) +{ + int i, j, found = 0; + char set_hw = 1; + struct baycom_state *bc; + char ifname[HDLCDRV_IFNAMELEN]; + + + printk(KERN_INFO "baycom: compiled %s %s\n", __TIME__, __DATE__); + /* + * register net devices + */ + for (i = 0; i < NR_PORTS; i++) { + struct device *dev = baycom_device+i; + sprintf(ifname, "bc%d", i); + + if (baycom_ports[i].modem < BAYCOM_MODEM_SER12 || + baycom_ports[i].modem > BAYCOM_MODEM_PAR96) + set_hw = 0; + if (set_hw) { + j = hdlcdrv_register_hdlcdrv(dev, ops_tab[baycom_ports[i].modem], + sizeof(struct baycom_state), + ifname, baycom_ports[i].iobase, + baycom_ports[i].irq, 0); + if (!j) { + bc = (struct baycom_state *)dev->priv; + bc->options = baycom_ports[i].options; + } + } else + j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops, + sizeof(struct baycom_state), + ifname, 0, 0, 0); + if (j) { + printk(KERN_WARNING "baycom: cannot register net " + "device\n"); + } else + found++; + } + if (!found) + return -ENXIO; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +/* + * command line settable parameters + */ +int modem = BAYCOM_MODEM_INVALID; +int iobase = 0x3f8; +int irq = 4; +int options = BAYCOM_OPTIONS_SOFTDCD; + +int init_module(void) +{ + printk(KERN_INFO "baycom: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n"); + + baycom_ports[0].modem = modem; + baycom_ports[0].iobase = iobase; + baycom_ports[0].irq = irq; + baycom_ports[0].options = options; + baycom_ports[1].modem = BAYCOM_MODEM_INVALID; + + return baycom_init(); +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + int i; + + printk(KERN_INFO "baycom: cleanup_module called\n"); + + for(i = 0; i < NR_PORTS; i++) { + struct device *dev = baycom_device+i; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + if (bc) { + if (bc->hdrv.magic != HDLCDRV_MAGIC) + printk(KERN_ERR "baycom: invalid magic in " + "cleanup_module\n"); + else + hdlcdrv_unregister_hdlcdrv(dev); + } + } +} + +#else /* MODULE */ +/* --------------------------------------------------------------------- */ +/* + * format: baycom=modem,io,irq,options[,modem,io,irq,options] + * modem=1: ser12, modem=2: par96 + * options=0: hardware DCD, options=1: software DCD + */ + +void baycom_setup(char *str, int *ints) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + if (ints[0] >= 4*i+4) { + baycom_ports[i].modem = ints[4*i+1]; + baycom_ports[i].iobase = ints[4*i+2]; + baycom_ports[i].irq = ints[4*i+3]; + baycom_ports[i].options = ints[4*i+4]; + } else + baycom_ports[i].modem = BAYCOM_MODEM_INVALID; + +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.6/linux/drivers/net/eql.c linux/drivers/net/eql.c --- v2.1.6/linux/drivers/net/eql.c Wed Oct 16 10:48:17 1996 +++ linux/drivers/net/eql.c Wed Oct 30 18:41:44 1996 @@ -128,6 +128,7 @@ #include #include #include +#include #include #include diff -u --recursive --new-file v2.1.6/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c --- v2.1.6/linux/drivers/net/hdlcdrv.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/hdlcdrv.c Wed Oct 30 14:15:12 1996 @@ -0,0 +1,934 @@ +/*****************************************************************************/ + +/* + * hdlcdrv.c -- HDLC packet radio network driver. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + * The driver was derived from Donald Beckers skeleton.c + * Written 1993-94 by Donald Becker. + * + * History: + * 0.1 21.09.96 Started + * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + */ + +/*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* make genksyms happy */ +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * 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 + X(hdlcdrv_receiver), + X(hdlcdrv_transmitter), + X(hdlcdrv_arbitrate), + X(hdlcdrv_register_hdlcdrv), + X(hdlcdrv_unregister_hdlcdrv), +#include +}; + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +int init_module(void) +{ + printk(KERN_INFO "hdlcdrv: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n"); + printk(KERN_INFO "hdlcdrv: compiled %s %s\n", __TIME__, __DATE__); + + register_symtab(&hdlcdrv_syms); + return 0; +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + printk(KERN_INFO "hdlcdrv: cleanup\n"); +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.6/linux/drivers/net/scc.c linux/drivers/net/scc.c --- v2.1.6/linux/drivers/net/scc.c Tue Oct 29 19:58:12 1996 +++ linux/drivers/net/scc.c Thu Oct 31 09:52:32 1996 @@ -1,4 +1,4 @@ -#define RCS_ID "$Id: scc.c,v 1.63 1996/10/09 16:45:47 jreuter Exp jreuter $" +#define RCS_ID "$Id: scc.c,v 1.64 1996/10/30 18:58:26 jreuter Exp jreuter $" #define VERSION "3.0" #define BANNER "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n" @@ -78,13 +78,12 @@ * Source moved to drivers/net/ * Includes Z8530 defines from drivers/net/z8530.h * Uses sk_buffer memory management - * Doesn't have own queues anymore * Reduced overhead of /proc/net/z8530drv output * Streamlined quite a lot things * Invents brand new bugs... ;-) The move to version number 3.0 reflects theses changes. - You can use version 2.4 if you need a KISS TNC emulator. + You can use version 2.4a if you need a KISS TNC emulator. Thanks to all who contributed to this driver with ideas and bug reports! @@ -120,6 +119,7 @@ #define MAXSCC 4 /* number of max. supported chips */ #define BUFSIZE 384 /* must not exceed 4096 */ +#define MAXQUEUE 8 /* number of buffers we queue ourself */ #undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */ /* enable_irq()/disable_irq() */ #undef SCC_DEBUG @@ -129,8 +129,6 @@ /* ----------------------------------------------------------------------- */ #include -#include - #include #include #include @@ -150,6 +148,8 @@ #include #include +#include /* for CONFIG_PROC_FS */ + #include #include "z8530.h" @@ -309,7 +309,6 @@ static __inline__ void scc_unlock_dev(struct scc_channel *scc) { - scc->tx_next_buff = NULL; scc->dev->tbusy = 0; } @@ -327,12 +326,9 @@ scc->tx_buff = NULL; } - if (scc->tx_next_buff != NULL) - { - dev_kfree_skb(scc->tx_next_buff, FREE_WRITE); - scc->tx_next_buff = NULL; - } - + while (skb_queue_len(&scc->tx_queue)) + dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE); + restore_flags(flags); } @@ -399,8 +395,9 @@ if (skb == NULL) { - skb = scc->tx_next_buff; + skb = skb_dequeue(&scc->tx_queue); scc->tx_buff = skb; + scc_unlock_dev(scc); if (skb == NULL) { @@ -417,8 +414,6 @@ Outb(scc->ctrl, RES_Tx_P); return; } - - scc_unlock_dev(scc); scc->stat.tx_state = TXS_ACTIVE; @@ -1150,7 +1145,7 @@ if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ { - if (scc->tx_next_buff == NULL) /* nothing to send */ + if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */ { scc->stat.tx_state = TXS_IDLE; scc_unlock_dev(scc); /* t_maxkeyup locked it. */ @@ -1620,7 +1615,8 @@ MOD_INC_USE_COUNT; - scc->tx_buff = scc->tx_next_buff = NULL; + scc->tx_buff = NULL; + skb_queue_head_init(&scc->tx_queue); init_channel(scc); @@ -1746,19 +1742,12 @@ save_flags(flags); cli(); - scc_lock_dev(scc); + __skb_queue_tail(&scc->tx_queue, skb); - if (scc->tx_next_buff != NULL) - { - printk(KERN_ERR "z8530drv: race condition, discarding frame\n"); - dev_kfree_skb(skb, FREE_WRITE); + if (skb_queue_len(&scc->tx_queue) == MAXQUEUE) scc_lock_dev(scc); - restore_flags(flags); - return 0; - } - + dev->trans_start = jiffies; - scc->tx_next_buff = skb; /* * Start transmission if the trx state is idle or @@ -1798,10 +1787,10 @@ struct scc_kiss_cmd kiss_cmd; struct scc_mem_config memcfg; struct scc_hw_config hwcfg; - unsigned char device_name[10]; - struct scc_channel *scc; int chan; + unsigned char device_name[10]; void *arg; + struct scc_channel *scc; scc = (struct scc_channel *) dev->priv; if (scc == NULL || scc->magic != SCC_MAGIC) @@ -1964,7 +1953,8 @@ scc->kiss.softdcd = 0; /* hardware dcd */ } - scc->tx_buff = scc->tx_next_buff = NULL; + scc->tx_buff = NULL; + skb_queue_head_init(&scc->tx_queue); scc->init = 1; return 0; diff -u --recursive --new-file v2.1.6/linux/drivers/net/sm_tables.h linux/drivers/net/sm_tables.h --- v2.1.6/linux/drivers/net/sm_tables.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/sm_tables.h Wed Oct 30 14:15:12 1996 @@ -0,0 +1,320 @@ +#ifdef ENABLE_AFSK1200 +static const char tx_lo_i[] = { 127, 89, 0, -89, -127, -89, 0, 89 }; + +static const char tx_lo_q[] = { 0, 89, 127, 89, 0, -89, -127, -89 }; + +static const char tx_hi_i[] = { 127, 16, -122, -48, 109, 77, -89, -100 }; + +static const char tx_hi_q[] = { 0, 125, 32, -117, -63, 100, 89, -77 }; + +static const char sinetab[] = { + 128, 140, 152, 164, 176, 187, 198, 208, + 217, 226, 233, 240, 245, 249, 252, 254, + 255, 254, 252, 249, 245, 240, 233, 226, + 217, 208, 198, 187, 176, 164, 152, 140, + 128, 116, 104, 92, 80, 69, 58, 48, + 39, 30, 23, 16, 11, 7, 4, 2, + 1, 2, 4, 7, 11, 16, 23, 30, + 39, 48, 58, 69, 80, 92, 104, 116 +}; +#endif ENABLE_AFSK1200 +#ifdef ENABLE_FSK9600 +#ifdef ENABLE_SBC +static unsigned char tx_filter_9k6_4[] = { + 65, 66, 63, 63, 76, 76, 73, 73, + 54, 54, 51, 51, 64, 65, 61, 62, + 214, 214, 211, 211, 224, 225, 221, 222, + 202, 202, 199, 200, 213, 213, 210, 210, + 38, 38, 35, 35, 48, 49, 45, 46, + 26, 27, 23, 24, 37, 37, 34, 34, + 186, 187, 183, 184, 197, 197, 194, 194, + 175, 175, 172, 172, 185, 185, 182, 182, + 73, 74, 70, 71, 84, 84, 81, 81, + 62, 62, 59, 59, 72, 72, 69, 69, + 221, 222, 218, 219, 232, 232, 229, 229, + 210, 210, 207, 207, 220, 221, 217, 218, + 46, 46, 43, 43, 56, 56, 53, 53, + 34, 34, 31, 31, 44, 45, 41, 42, + 194, 194, 191, 191, 204, 205, 201, 202, + 182, 183, 179, 180, 193, 193, 190, 190, + 65, 65, 62, 62, 75, 76, 72, 73, + 53, 54, 50, 51, 64, 64, 61, 61, + 213, 214, 210, 211, 224, 224, 221, 221, + 202, 202, 199, 199, 212, 212, 209, 209, + 37, 38, 34, 35, 48, 48, 45, 45, + 26, 26, 23, 23, 36, 37, 33, 34, + 186, 186, 183, 183, 196, 196, 193, 193, + 174, 174, 171, 171, 184, 185, 181, 182, + 73, 73, 70, 70, 83, 83, 80, 80, + 61, 61, 58, 58, 71, 72, 68, 69, + 221, 221, 218, 218, 231, 232, 228, 229, + 209, 210, 206, 207, 220, 220, 217, 217, + 45, 45, 42, 42, 55, 56, 53, 53, + 33, 34, 30, 31, 44, 44, 41, 41, + 193, 194, 190, 191, 204, 204, 201, 201, + 182, 182, 179, 179, 192, 192, 189, 190, + 65, 66, 62, 63, 67, 68, 64, 64, + 103, 104, 100, 101, 105, 106, 101, 102, + 168, 169, 164, 165, 169, 170, 166, 167, + 205, 206, 202, 203, 207, 208, 204, 205, + 49, 50, 46, 46, 51, 51, 47, 48, + 87, 88, 83, 84, 88, 89, 85, 86, + 151, 152, 148, 149, 153, 154, 149, 150, + 189, 190, 186, 187, 191, 192, 187, 188, + 66, 67, 63, 64, 68, 69, 64, 65, + 104, 105, 100, 101, 105, 106, 102, 103, + 168, 169, 165, 166, 170, 171, 167, 167, + 206, 207, 203, 204, 208, 209, 204, 205, + 50, 51, 46, 47, 51, 52, 48, 49, + 87, 88, 84, 85, 89, 90, 85, 86, + 152, 153, 149, 149, 154, 154, 150, 151, + 190, 191, 186, 187, 191, 192, 188, 189, + 66, 67, 63, 64, 68, 69, 64, 65, + 104, 105, 101, 101, 106, 106, 102, 103, + 169, 170, 165, 166, 170, 171, 167, 168, + 206, 207, 203, 204, 208, 209, 204, 205, + 50, 51, 46, 47, 51, 52, 48, 49, + 88, 88, 84, 85, 89, 90, 86, 87, + 152, 153, 149, 150, 154, 155, 150, 151, + 190, 191, 186, 187, 191, 192, 188, 189, + 67, 68, 63, 64, 68, 69, 65, 66, + 105, 106, 101, 102, 106, 107, 103, 104, + 169, 170, 166, 167, 171, 172, 167, 168, + 207, 208, 204, 204, 209, 209, 205, 206, + 50, 51, 47, 48, 52, 53, 49, 50, + 88, 89, 85, 86, 90, 91, 86, 87, + 153, 154, 149, 150, 154, 155, 151, 152, + 191, 191, 187, 188, 192, 193, 189, 190, + 65, 66, 66, 67, 49, 50, 50, 50, + 168, 169, 168, 169, 151, 152, 152, 153, + 103, 104, 104, 105, 87, 88, 87, 88, + 205, 206, 206, 207, 189, 190, 190, 191, + 67, 68, 68, 68, 51, 51, 51, 52, + 169, 170, 170, 171, 153, 154, 154, 154, + 105, 106, 105, 106, 88, 89, 89, 90, + 207, 208, 208, 209, 191, 191, 191, 192, + 62, 63, 63, 63, 46, 46, 46, 47, + 164, 165, 165, 166, 148, 149, 149, 149, + 100, 101, 100, 101, 83, 84, 84, 85, + 202, 203, 203, 204, 186, 186, 186, 187, + 64, 64, 64, 65, 47, 48, 48, 49, + 166, 167, 167, 167, 149, 150, 150, 151, + 101, 102, 102, 103, 85, 86, 85, 86, + 204, 204, 204, 205, 187, 188, 188, 189, + 66, 67, 67, 68, 50, 51, 51, 51, + 169, 170, 169, 170, 152, 153, 153, 154, + 104, 105, 105, 106, 88, 88, 88, 89, + 206, 207, 207, 208, 190, 191, 191, 191, + 68, 69, 69, 69, 51, 52, 52, 53, + 170, 171, 171, 172, 154, 155, 154, 155, + 106, 106, 106, 107, 89, 90, 90, 91, + 208, 209, 209, 209, 192, 192, 192, 193, + 63, 64, 64, 64, 46, 47, 47, 48, + 165, 166, 166, 167, 149, 150, 149, 150, + 101, 101, 101, 102, 84, 85, 85, 86, + 203, 204, 204, 204, 187, 187, 187, 188, + 64, 65, 65, 66, 48, 49, 49, 50, + 167, 168, 167, 168, 150, 151, 151, 152, + 102, 103, 103, 104, 86, 87, 86, 87, + 205, 205, 205, 206, 188, 189, 189, 190, + 65, 65, 73, 73, 38, 37, 46, 45, + 214, 213, 221, 221, 186, 186, 194, 193, + 54, 53, 62, 61, 26, 26, 34, 33, + 202, 202, 210, 209, 175, 174, 182, 182, + 76, 75, 84, 83, 48, 48, 56, 55, + 224, 224, 232, 231, 197, 196, 204, 204, + 64, 64, 72, 71, 37, 36, 44, 44, + 213, 212, 220, 220, 185, 184, 193, 192, + 63, 62, 70, 70, 35, 34, 43, 42, + 211, 210, 218, 218, 183, 183, 191, 190, + 51, 50, 59, 58, 23, 23, 31, 30, + 199, 199, 207, 206, 172, 171, 179, 179, + 73, 72, 81, 80, 45, 45, 53, 53, + 221, 221, 229, 228, 194, 193, 201, 201, + 61, 61, 69, 68, 34, 33, 41, 41, + 210, 209, 217, 217, 182, 181, 190, 189, + 66, 65, 74, 73, 38, 38, 46, 45, + 214, 214, 222, 221, 187, 186, 194, 194, + 54, 54, 62, 61, 27, 26, 34, 34, + 202, 202, 210, 210, 175, 174, 183, 182, + 76, 76, 84, 83, 49, 48, 56, 56, + 225, 224, 232, 232, 197, 196, 205, 204, + 65, 64, 72, 72, 37, 37, 45, 44, + 213, 212, 221, 220, 185, 185, 193, 192, + 63, 62, 71, 70, 35, 35, 43, 42, + 211, 211, 219, 218, 184, 183, 191, 191, + 51, 51, 59, 58, 24, 23, 31, 31, + 200, 199, 207, 207, 172, 171, 180, 179, + 73, 73, 81, 80, 46, 45, 53, 53, + 222, 221, 229, 229, 194, 193, 202, 201, + 62, 61, 69, 69, 34, 34, 42, 41, + 210, 209, 218, 217, 182, 182, 190, 190 +}; + +#endif /* ENABLE_SBC */ +#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) +static unsigned char tx_filter_9k6_5[] = { + 78, 78, 75, 76, 85, 85, 82, 83, + 71, 72, 69, 69, 78, 78, 76, 76, + 193, 193, 190, 191, 199, 200, 197, 198, + 186, 187, 184, 184, 193, 193, 190, 191, + 58, 59, 56, 57, 65, 66, 63, 63, + 52, 52, 49, 50, 58, 59, 56, 57, + 173, 174, 171, 171, 180, 180, 177, 178, + 166, 167, 164, 165, 173, 174, 171, 171, + 85, 86, 83, 83, 92, 92, 89, 90, + 79, 79, 76, 77, 85, 86, 83, 83, + 200, 200, 198, 198, 207, 207, 204, 205, + 193, 194, 191, 191, 200, 201, 198, 198, + 66, 66, 63, 64, 72, 73, 70, 70, + 59, 60, 57, 57, 66, 66, 63, 64, + 180, 181, 178, 179, 187, 188, 185, 185, + 174, 174, 171, 172, 180, 181, 178, 179, + 76, 77, 74, 75, 83, 84, 81, 81, + 70, 70, 67, 68, 76, 77, 74, 75, + 191, 192, 189, 189, 198, 198, 195, 196, + 185, 185, 182, 183, 191, 192, 189, 189, + 57, 57, 54, 55, 64, 64, 61, 62, + 50, 51, 48, 48, 57, 57, 55, 55, + 172, 172, 169, 170, 178, 179, 176, 177, + 165, 166, 163, 163, 172, 172, 169, 170, + 84, 84, 81, 82, 90, 91, 88, 89, + 77, 78, 75, 75, 84, 84, 81, 82, + 198, 199, 196, 197, 205, 206, 203, 203, + 192, 192, 189, 190, 198, 199, 196, 197, + 64, 65, 62, 62, 71, 71, 68, 69, + 57, 58, 55, 56, 64, 65, 62, 62, + 179, 179, 177, 177, 186, 186, 183, 184, + 172, 173, 170, 170, 179, 180, 177, 177, + 78, 79, 76, 77, 79, 79, 77, 77, + 99, 100, 97, 98, 100, 100, 98, 98, + 171, 172, 169, 170, 172, 173, 170, 171, + 192, 193, 190, 191, 193, 194, 191, 192, + 59, 60, 57, 58, 60, 61, 58, 59, + 81, 81, 79, 79, 81, 82, 79, 80, + 153, 153, 151, 151, 153, 154, 151, 152, + 174, 175, 172, 173, 175, 175, 173, 173, + 82, 83, 80, 81, 83, 83, 81, 81, + 103, 104, 101, 102, 104, 105, 102, 103, + 175, 176, 173, 174, 176, 177, 174, 175, + 197, 197, 195, 195, 197, 198, 195, 196, + 64, 64, 62, 62, 64, 65, 62, 63, + 85, 85, 83, 83, 85, 86, 83, 84, + 157, 158, 155, 156, 158, 158, 156, 156, + 178, 179, 176, 177, 179, 179, 177, 177, + 78, 78, 76, 76, 78, 79, 76, 77, + 99, 99, 97, 97, 99, 100, 97, 98, + 171, 172, 169, 170, 172, 172, 170, 170, + 192, 193, 190, 191, 193, 193, 191, 191, + 59, 60, 57, 58, 60, 60, 58, 58, + 80, 81, 78, 79, 81, 82, 79, 80, + 152, 153, 150, 151, 153, 154, 151, 152, + 174, 174, 172, 172, 174, 175, 172, 173, + 82, 82, 80, 80, 82, 83, 80, 81, + 103, 104, 101, 102, 104, 104, 102, 102, + 175, 176, 173, 174, 176, 176, 174, 174, + 196, 197, 194, 195, 197, 198, 195, 196, + 63, 64, 61, 62, 64, 65, 62, 63, + 84, 85, 82, 83, 85, 86, 83, 84, + 157, 157, 155, 155, 157, 158, 155, 156, + 178, 178, 176, 176, 178, 179, 176, 177, + 78, 78, 78, 79, 69, 69, 69, 69, + 136, 136, 136, 137, 127, 127, 127, 128, + 136, 136, 136, 137, 127, 127, 127, 128, + 194, 195, 195, 195, 185, 185, 185, 186, + 69, 69, 69, 69, 59, 60, 60, 60, + 127, 127, 127, 128, 117, 118, 118, 118, + 127, 127, 127, 128, 117, 118, 118, 118, + 185, 185, 185, 186, 175, 176, 176, 176, + 78, 79, 79, 79, 69, 69, 69, 70, + 136, 137, 137, 137, 127, 128, 127, 128, + 136, 137, 137, 137, 127, 128, 127, 128, + 195, 195, 195, 195, 185, 186, 186, 186, + 69, 69, 69, 70, 60, 60, 60, 60, + 127, 128, 127, 128, 118, 118, 118, 119, + 127, 128, 127, 128, 118, 118, 118, 119, + 185, 186, 186, 186, 176, 176, 176, 177, + 78, 79, 79, 79, 69, 69, 69, 70, + 136, 137, 137, 137, 127, 128, 128, 128, + 136, 137, 137, 137, 127, 128, 128, 128, + 195, 195, 195, 195, 185, 186, 186, 186, + 69, 69, 69, 70, 60, 60, 60, 60, + 127, 128, 128, 128, 118, 118, 118, 119, + 127, 128, 128, 128, 118, 118, 118, 119, + 185, 186, 186, 186, 176, 176, 176, 177, + 79, 79, 79, 80, 69, 70, 70, 70, + 137, 137, 137, 138, 128, 128, 128, 128, + 137, 137, 137, 138, 128, 128, 128, 128, + 195, 195, 195, 196, 186, 186, 186, 186, + 69, 70, 70, 70, 60, 60, 60, 61, + 128, 128, 128, 128, 118, 119, 119, 119, + 128, 128, 128, 128, 118, 119, 119, 119, + 186, 186, 186, 186, 176, 177, 177, 177, + 78, 78, 82, 82, 59, 59, 64, 63, + 171, 171, 175, 175, 153, 152, 157, 157, + 99, 99, 103, 103, 81, 80, 85, 84, + 192, 192, 197, 196, 174, 174, 178, 178, + 79, 78, 83, 82, 60, 60, 64, 64, + 172, 172, 176, 176, 153, 153, 158, 157, + 100, 99, 104, 104, 81, 81, 85, 85, + 193, 193, 197, 197, 175, 174, 179, 178, + 76, 76, 80, 80, 57, 57, 62, 61, + 169, 169, 173, 173, 151, 150, 155, 155, + 97, 97, 101, 101, 79, 78, 83, 82, + 190, 190, 195, 194, 172, 172, 176, 176, + 77, 76, 81, 80, 58, 58, 62, 62, + 170, 170, 174, 174, 151, 151, 156, 155, + 98, 97, 102, 102, 79, 79, 83, 83, + 191, 191, 195, 195, 173, 172, 177, 176, + 79, 78, 83, 82, 60, 60, 64, 64, + 172, 172, 176, 176, 153, 153, 158, 157, + 100, 99, 104, 104, 81, 81, 85, 85, + 193, 193, 197, 197, 175, 174, 179, 178, + 79, 79, 83, 83, 61, 60, 65, 65, + 173, 172, 177, 176, 154, 154, 158, 158, + 100, 100, 105, 104, 82, 82, 86, 86, + 194, 193, 198, 198, 175, 175, 179, 179, + 77, 76, 81, 80, 58, 58, 62, 62, + 170, 170, 174, 174, 151, 151, 156, 155, + 98, 97, 102, 102, 79, 79, 83, 83, + 191, 191, 195, 195, 173, 172, 177, 176, + 77, 77, 81, 81, 59, 58, 63, 63, + 171, 170, 175, 174, 152, 152, 156, 156, + 98, 98, 103, 102, 80, 80, 84, 84, + 192, 191, 196, 196, 173, 173, 177, 177, + 78, 76, 85, 84, 58, 57, 66, 64, + 193, 191, 200, 198, 173, 172, 180, 179, + 71, 70, 79, 77, 52, 50, 59, 57, + 186, 185, 193, 192, 166, 165, 174, 172, + 85, 83, 92, 90, 65, 64, 72, 71, + 199, 198, 207, 205, 180, 178, 187, 186, + 78, 76, 85, 84, 58, 57, 66, 64, + 193, 191, 200, 198, 173, 172, 180, 179, + 75, 74, 83, 81, 56, 54, 63, 62, + 190, 189, 198, 196, 171, 169, 178, 177, + 69, 67, 76, 75, 49, 48, 57, 55, + 184, 182, 191, 189, 164, 163, 171, 170, + 82, 81, 89, 88, 63, 61, 70, 68, + 197, 195, 204, 203, 177, 176, 185, 183, + 76, 74, 83, 81, 56, 55, 63, 62, + 190, 189, 198, 196, 171, 169, 178, 177, + 78, 77, 86, 84, 59, 57, 66, 65, + 193, 192, 200, 199, 174, 172, 181, 179, + 72, 70, 79, 78, 52, 51, 60, 58, + 187, 185, 194, 192, 167, 166, 174, 173, + 85, 84, 92, 91, 66, 64, 73, 71, + 200, 198, 207, 206, 180, 179, 188, 186, + 78, 77, 86, 84, 59, 57, 66, 65, + 193, 192, 201, 199, 174, 172, 181, 180, + 76, 75, 83, 82, 57, 55, 64, 62, + 191, 189, 198, 197, 171, 170, 179, 177, + 69, 68, 77, 75, 50, 48, 57, 56, + 184, 183, 191, 190, 165, 163, 172, 170, + 83, 81, 90, 89, 63, 62, 70, 69, + 198, 196, 205, 203, 178, 177, 185, 184, + 76, 75, 83, 82, 57, 55, 64, 62, + 191, 189, 198, 197, 171, 170, 179, 177 +}; + +#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */ +#endif /* ENABLE_FSK9600 */ diff -u --recursive --new-file v2.1.6/linux/drivers/net/soundmodem.c linux/drivers/net/soundmodem.c --- v2.1.6/linux/drivers/net/soundmodem.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/net/soundmodem.c Wed Oct 30 14:15:12 1996 @@ -0,0 +1,2120 @@ +/*****************************************************************************/ + +/* + * soundmodem.c -- soundcard radio modem driver. + * + * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please note that the GPL allows you to use the driver, NOT the radio. + * In order to use the radio, you need a license from the communications + * authority of your country. + * + * + * Command line options (insmod command line) + * + * hardware hardware type; 0=sbc, 1=wss, any other value invalid + * mode mode type; 0=1200 baud AFSK, 1=9600 baud FSK, any other + * value invalid + * iobase base address of the soundcard; common values are 0x220 for sbc, + * 0x530 for wss + * irq interrupt number; common values are 7 or 5 for sbc, 11 for wss + * dma dma number; common values are 0 or 1 + * + * + * History: + * 0.1 21.09.96 Started + * 18.10.96 Changed to new user space access routines (copy_{to,from}_user) + */ + +/*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#define NR_PORTS 4 + +#define SM_DEBUG + +#define ENABLE_SBC +#define ENABLE_WSS +#undef ENABLE_WSSFDX + +#define ENABLE_AFSK1200 +#define ENABLE_FSK9600 + +/* --------------------------------------------------------------------- */ + +#include "sm_tables.h" + +/* --------------------------------------------------------------------- */ + +static struct device sm_device[NR_PORTS]; + +static struct { + int hardware, mode, iobase, irq, dma, seriobase, pariobase, midiiobase; +} sm_ports[NR_PORTS] = { +{ SM_HARDWARE_INVALID, SM_MODE_INVALID, -1, 0, 0, -1, -1, -1 }, +}; + +/* --------------------------------------------------------------------- */ +/* + * the sbc converter's registers + */ +#define DSP_RESET(iobase) (iobase+0x6) +#define DSP_READ_DATA(iobase) (iobase+0xa) +#define DSP_WRITE_DATA(iobase) (iobase+0xc) +#define DSP_WRITE_STATUS(iobase) (iobase+0xc) +#define DSP_DATA_AVAIL(iobase) (iobase+0xe) +#define DSP_MIXER_ADDR(iobase) (iobase+0x4) +#define DSP_MIXER_DATA(iobase) (iobase+0x5) +#define SBC_EXTENT 16 + +/* --------------------------------------------------------------------- */ +/* + * SBC commands + */ +#define SBC_OUTPUT 0x14 +#define SBC_INPUT 0x24 +#define SBC_HISPEED 0x48 +#define SBC_HI_OUTPUT 0x91 +#define SBC_HI_INPUT 0x99 +#define SBC_LO_OUTPUT_AUTOINIT 0x1c +#define SBC_LO_INPUT_AUTOINIT 0x2c +#define SBC_HI_OUTPUT_AUTOINIT 0x90 +#define SBC_HI_INPUT_AUTOINIT 0x98 +#define SBC_IMMED_INT 0xf2 +#define SBC_GET_REVISION 0xe1 +#define ESS_GET_REVISION 0xe7 +#define SBC_SPEAKER_ON 0xd1 +#define SBC_SPEAKER_OFF 0xd3 +#define SBC_DMA_ON 0xd0 +#define SBC_DMA_OFF 0xd4 +#define SBC_SAMPLE_RATE 0x40 +#define SBC_MONO_8BIT 0xa0 +#define SBC_MONO_16BIT 0xa4 +#define SBC_STEREO_8BIT 0xa8 +#define SBC_STEREO_16BIT 0xac + +#define DMA_MODE_AUTOINIT 0x10 + +/* --------------------------------------------------------------------- */ + +#define WSS_CONFIG(iobase) (iobase+0) +#define WSS_STATUS(iobase) (iobase+3) +#define WSS_CODEC_IA(iobase) (iobase+4) +#define WSS_CODEC_ID(iobase) (iobase+5) +#define WSS_CODEC_STATUS(iobase) (iobase+6) +#define WSS_CODEC_DATA(iobase) (iobase+7) + +#define WSS_EXTENT 8 + +/* --------------------------------------------------------------------- */ + +#define UART_RBR(iobase) (iobase+0) +#define UART_THR(iobase) (iobase+0) +#define UART_IER(iobase) (iobase+1) +#define UART_IIR(iobase) (iobase+2) +#define UART_FCR(iobase) (iobase+2) +#define UART_LCR(iobase) (iobase+3) +#define UART_MCR(iobase) (iobase+4) +#define UART_LSR(iobase) (iobase+5) +#define UART_MSR(iobase) (iobase+6) +#define UART_SCR(iobase) (iobase+7) +#define UART_DLL(iobase) (iobase+0) +#define UART_DLM(iobase) (iobase+1) + +#define SER_EXTENT 8 + +#define LPT_DATA(iobase) (iobase+0) +#define LPT_STATUS(iobase) (iobase+1) +#define LPT_CONTROL(iobase) (iobase+2) +#define LPT_IRQ_ENABLE 0x10 + +#define LPT_EXTENT 3 + +#define MIDI_DATA(iobase) (iobase) +#define MIDI_STATUS(iobase) (iobase+1) +#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */ +#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */ + +#define MIDI_EXTENT 2 + +/* ---------------------------------------------------------------------- */ + +#define PARAM_TXDELAY 1 +#define PARAM_PERSIST 2 +#define PARAM_SLOTTIME 3 +#define PARAM_TXTAIL 4 +#define PARAM_FULLDUP 5 +#define PARAM_HARDWARE 6 +#define PARAM_RETURN 255 + +#define SP_SER 1 +#define SP_PAR 2 +#define SP_MIDI 4 + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct sm_state { + struct hdlcdrv_state hdrv; + + struct config { + int hardware; + int mode; + } config; + + struct modem_state { + unsigned char revhi, revlo; + + unsigned int shreg; + + unsigned char last_sample; + unsigned int bit_pll; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + unsigned char last_rxbit; + unsigned char tx_bit; + + signed char filt[9]; + + unsigned long descram; + unsigned long scram; + + unsigned char *dmabufr; + unsigned char *dmabufw; + unsigned char dmabufidx; + unsigned char oldptt; + } modem; + +#define DIAGDATALEN 64 + struct diag_data { + unsigned int mode; + unsigned int flags; + volatile int ptr; + short data[DIAGDATALEN]; + } diag; + +#ifdef SM_DEBUG + struct debug_vals { + unsigned long last_jiffies; + unsigned cur_intcnt; + unsigned last_intcnt; + int cur_pllcorr; + int last_pllcorr; + } debug_vals; +#endif /* SM_DEBUG */ +}; + +/* --------------------------------------------------------------------- */ + +struct modem_info { + struct hdlcdrv_ops hops; + unsigned int samplerate; + unsigned char sbcmix; + unsigned char sperbit; + char *mode_name; /* used for request_{region,irq,dma} */ + /* + * low level chip informations + */ + unsigned char data_fmt; + unsigned int dmabuflen; + void (*modulator)(struct sm_state *, unsigned char *, int); + void (*demodulator)(struct sm_state *, unsigned char *, int); +}; + +/* --------------------------------------------------------------------- */ + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* --------------------------------------------------------------------- */ + +static void inline sm_int_freq(struct sm_state *sm) +{ +#ifdef SM_DEBUG + unsigned long cur_jiffies = jiffies; + /* + * measure the interrupt frequency + */ + sm->debug_vals.cur_intcnt++; + if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) { + sm->debug_vals.last_jiffies = cur_jiffies; + sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt; + sm->debug_vals.cur_intcnt = 0; + } +#endif /* SM_DEBUG */ +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== port checking routines ======================== + */ + +static int inline check_lpt(unsigned int iobase) +{ + unsigned char b1,b2; + int i; + + if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT) + return 0; + if (check_region(iobase, LPT_EXTENT)) + return 0; + b1 = inb(LPT_DATA(iobase)); + b2 = inb(LPT_CONTROL(iobase)); + outb(0xaa, LPT_DATA(iobase)); + i = inb(LPT_DATA(iobase)) == 0xaa; + outb(0x55, LPT_DATA(iobase)); + i &= inb(LPT_DATA(iobase)) == 0x55; + outb(0x0a, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a; + outb(0x05, LPT_CONTROL(iobase)); + i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05; + outb(b1, LPT_DATA(iobase)); + outb(b2, LPT_CONTROL(iobase)); + return !i; +} + +/* --------------------------------------------------------------------- */ + +enum uart { c_uart_unknown, c_uart_8250, + c_uart_16450, c_uart_16550, c_uart_16550A}; +static const char *uart_str[] = + { "unknown", "8250", "16450", "16550", "16550A" }; + +static enum uart inline check_uart(unsigned int iobase) +{ + unsigned char b1,b2,b3; + enum uart u; + enum uart uart_tab[] = + { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; + + if (iobase <= 0 || iobase > 0x1000-SER_EXTENT) + return c_uart_unknown; + if (check_region(iobase, SER_EXTENT)) + return c_uart_unknown; + b1 = inb(UART_MCR(iobase)); + outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */ + b2 = inb(UART_MSR(iobase)); + outb(0x1a, UART_MCR(iobase)); + b3 = inb(UART_MSR(iobase)) & 0xf0; + outb(b1, UART_MCR(iobase)); /* restore old values */ + outb(b2, UART_MSR(iobase)); + if (b3 != 0x90) + return c_uart_unknown; + inb(UART_RBR(iobase)); + inb(UART_RBR(iobase)); + outb(0x01, UART_FCR(iobase)); /* enable FIFOs */ + u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3]; + if (u == c_uart_16450) { + outb(0x5a, UART_SCR(iobase)); + b1 = inb(UART_SCR(iobase)); + outb(0xa5, UART_SCR(iobase)); + b2 = inb(UART_SCR(iobase)); + if ((b1 != 0x5a) || (b2 != 0xa5)) + u = c_uart_8250; + } + return u; +} + +/* --------------------------------------------------------------------- */ + +static int inline check_midi(unsigned int iobase) +{ + unsigned long timeout; + unsigned long flags; + unsigned char b; + + if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT) + return 0; + if (check_region(iobase, MIDI_EXTENT)) + return 0; + timeout = jiffies + (HZ / 100); + while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) + if ((signed)(jiffies - timeout) > 0) + return 0; + save_flags(flags); + cli(); + outb(0xff, MIDI_DATA(iobase)); + b = inb(MIDI_STATUS(iobase)); + restore_flags(flags); + if (!(b & MIDI_WRITE_EMPTY)) + return 0; + while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) + if ((signed)(jiffies - timeout) > 0) + return 0; + return 1; +} + +/* --------------------------------------------------------------------- */ + +static void inline output_status(struct sm_state *sm) +{ + int invert_dcd = 0; + int invert_ptt = 0; + + int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt; + int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd; + + if (sm->hdrv.ptt_out.flags & SP_SER) { + outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase)); + outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase)); + } + if (sm->hdrv.ptt_out.flags & SP_PAR) { + outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase)); + } + if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) { + outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase)); + } +} + +/* --------------------------------------------------------------------- */ + +static void output_open(struct sm_state *sm) +{ + enum uart u = c_uart_unknown; + + sm->hdrv.ptt_out.flags = 0; + if (sm->hdrv.ptt_out.seriobase > 0 && + sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT && + ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) { + sm->hdrv.ptt_out.flags |= SP_SER; + request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt"); + outb(0, UART_IER(sm->hdrv.ptt_out.seriobase)); + /* 5 bits, 1 stop, no parity, no break, Div latch access */ + outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase)); + outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase)); + outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */ + /* LCR and MCR set by output_status */ + } + if (sm->hdrv.ptt_out.pariobase > 0 && + sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT && + check_lpt(sm->hdrv.ptt_out.pariobase)) { + sm->hdrv.ptt_out.flags |= SP_PAR; + request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt"); + } + if (sm->hdrv.ptt_out.midiiobase > 0 && + sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT && + check_midi(sm->hdrv.ptt_out.midiiobase)) { + sm->hdrv.ptt_out.flags |= SP_MIDI; + request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT, + "sm midi ptt"); + } + output_status(sm); + + printk(KERN_INFO "sm: ptt output:"); + if (sm->hdrv.ptt_out.flags & SP_SER) + printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase, + uart_str[u]); + if (sm->hdrv.ptt_out.flags & SP_PAR) + printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase); + if (sm->hdrv.ptt_out.flags & SP_MIDI) + printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase); + if (!sm->hdrv.ptt_out.flags) + printk(" none"); + printk("\n"); +} + +/* --------------------------------------------------------------------- */ + +static void output_close(struct sm_state *sm) +{ + /* release regions used for PTT output */ + sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0; + output_status(sm); + if (sm->hdrv.ptt_out.flags & SP_SER) + release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT); + if (sm->hdrv.ptt_out.flags & SP_PAR) + release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT); + if (sm->hdrv.ptt_out.flags & SP_MIDI) + release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT); + sm->hdrv.ptt_out.flags = 0; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== diagnostics stuff =============================== + */ + +static inline void diag_trigger(struct sm_state *sm) +{ + if (sm->diag.ptr < 0) + if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd) + sm->diag.ptr = 0; +} + +/* --------------------------------------------------------------------- */ + +static inline void diag_add(struct sm_state *sm, int valinp, int valdemod) +{ + int val; + + if ((sm->diag.mode != SM_DIAGMODE_INPUT && + sm->diag.mode != SM_DIAGMODE_DEMOD) || + sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0) + return; + val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp; + /* clip */ + if (val > SHRT_MAX) + val = SHRT_MAX; + if (val < SHRT_MIN) + val = SHRT_MIN; + sm->diag.data[sm->diag.ptr++] = val; +} + +/* --------------------------------------------------------------------- */ + +static inline void diag_add_one(struct sm_state *sm, int val) +{ + if ((sm->diag.mode != SM_DIAGMODE_INPUT && + sm->diag.mode != SM_DIAGMODE_DEMOD) || + sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0) + return; + /* clip */ + if (val > SHRT_MAX) + val = SHRT_MAX; + if (val < SHRT_MIN) + val = SHRT_MIN; + sm->diag.data[sm->diag.ptr++] = val; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== modem routines 1200 baud ========================= + */ + +static inline unsigned int hweight32(unsigned int w) + __attribute__ ((unused)); +static inline unsigned int hweight16(unsigned short w) + __attribute__ ((unused)); +static inline unsigned int hweight8(unsigned char w) + __attribute__ ((unused)); + +static inline unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +static inline unsigned int hweight16(unsigned short w) +{ + unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555); + res = (res & 0x3333) + ((res >> 2) & 0x3333); + res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); + return (res & 0x00FF) + ((res >> 8) & 0x00FF); +} + +static inline unsigned int hweight8(unsigned char w) +{ + unsigned short res = (w & 0x55) + ((w >> 1) & 0x55); + res = (res & 0x33) + ((res >> 2) & 0x33); + return (res & 0x0F) + ((res >> 4) & 0x0F); +} + +/* --------------------------------------------------------------------- */ + +#ifdef ENABLE_AFSK1200 + +static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen) +{ + static const int dds_inc[2] = { 8192, 15019 }; + int j, k; + + for (; buflen >= 8; buflen -= 8) { + if (sm->modem.shreg <= 1) + sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + sm->modem.tx_bit = (sm->modem.tx_bit ^ + (!(sm->modem.shreg & 1))) & 1; + sm->modem.shreg >>= 1; + k = dds_inc[sm->modem.tx_bit & 1]; + for (j = 0; j < 8; j++) { + *buf++ = sinetab[(sm->modem.bit_pll >> 10) & 0x3f]; + sm->modem.bit_pll += k; + } + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen) +{ + static const int pll_corr[2] = { -0x1000, 0x1000 }; + int j; + signed char *fp; + const signed char *coeffp; + int sum1, sum2; + unsigned char newsample; + + for (; buflen > 0; buflen--, buf++) { + sm->modem.filt[8] = (*buf - 128); + for (sum1 = j = 0, fp = sm->modem.filt+1, coeffp = tx_lo_i; + j < 8; j++, fp++, coeffp++) { + sum1 += (*coeffp) * (*fp); + fp[-1] = fp[0]; + } + sum1 >>= 7; + sum2 = sum1 * sum1; + for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_lo_q; + j < 8; j++, fp++, coeffp++) + sum1 += (*coeffp) * (*fp); + sum1 >>= 7; + sum2 += sum1 * sum1; + for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_i; + j < 8; j++, fp++, coeffp++) + sum1 += (*coeffp) * (*fp); + sum1 >>= 7; + sum2 -= sum1 * sum1; + for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_q; + j < 8; j++, fp++, coeffp++) + sum1 += (*coeffp) * (*fp); + sum1 >>= 7; + sum2 -= sum1 * sum1; + sm->modem.dcd_shreg <<= 1; + sm->modem.bit_pll += 0x2000; + newsample = (sum2 > 0); + if (sm->modem.last_sample ^ newsample) { + sm->modem.last_sample = newsample; + sm->modem.dcd_shreg |= 1; + sm->modem.bit_pll += pll_corr + [sm->modem.bit_pll < 0x9000]; + j = 4 * hweight8(sm->modem.dcd_shreg & 0x38) + - hweight16(sm->modem.dcd_shreg & 0x7c0); + sm->modem.dcd_sum0 += j; + } + hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample); + if ((--sm->modem.dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + + sm->modem.dcd_sum1 + + sm->modem.dcd_sum2) < 0); + sm->modem.dcd_sum2 = sm->modem.dcd_sum1; + sm->modem.dcd_sum1 = sm->modem.dcd_sum0; + sm->modem.dcd_sum0 = 2; /* slight bias */ + sm->modem.dcd_time = 120; + } + if (sm->modem.bit_pll >= 0x10000) { + sm->modem.bit_pll &= 0xffff; + sm->modem.shreg >>= 1; + sm->modem.shreg |= (!(sm->modem.last_rxbit ^ + sm->modem.last_sample)) << 16; + sm->modem.last_rxbit = sm->modem.last_sample; + diag_trigger(sm); + if (sm->modem.shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1); + sm->modem.shreg = 0x10000; + } + } + diag_add(sm, sm->modem.filt[7] << 8, sum2); + } +} + +#endif /* ENABLE_AFSK1200 */ + +/* --------------------------------------------------------------------- */ +/* + * ===================== modem routines 9600 baud ========================= + */ + +#ifdef ENABLE_FSK9600 + +#define DESCRAM_TAP1 0x20000 +#define DESCRAM_TAP2 0x01000 +#define DESCRAM_TAP3 0x00001 + +#define DESCRAM_TAPSH1 17 +#define DESCRAM_TAPSH2 12 +#define DESCRAM_TAPSH3 0 + +#define SCRAM_TAP1 0x20000 /* X^17 */ +#define SCRAM_TAPN 0x00021 /* X^0+X^5 */ + +/* --------------------------------------------------------------------- */ + +#ifdef ENABLE_SBC + +static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen) +{ + int j; + const unsigned char *cp; + + for (; buflen >= 4; buflen -= 4) { + if (sm->modem.shreg <= 1) + sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + sm->modem.scram = ((sm->modem.scram << 1) | + (sm->modem.scram & 1)); + sm->modem.scram ^= (!(sm->modem.shreg & 1)); + sm->modem.shreg >>= 1; + if (sm->modem.scram & (SCRAM_TAP1 << 1)) + sm->modem.scram ^= (SCRAM_TAPN << 1); + sm->modem.tx_bit = (sm->modem.tx_bit << 1) | + (!!(sm->modem.scram & (SCRAM_TAP1 << 2))); + cp = tx_filter_9k6_4 + (sm->modem.tx_bit & 0xff); + for (j = 0; j < 4; j++) { + *buf++ = *cp; + cp += 0x100; + } + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen) +{ + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + sm->modem.dcd_shreg <<= 1; + sm->modem.bit_pll += 0x4000; + curbit = (*buf >= 0x80); + if (sm->modem.last_sample ^ curbit) { + sm->modem.dcd_shreg |= 1; + sm->modem.bit_pll += pll_corr + [sm->modem.bit_pll < 0xa000]; + sm->modem.dcd_sum0 += 8 * + hweight8(sm->modem.dcd_shreg & 0x0c) - + !!(sm->modem.dcd_shreg & 0x10); + } + sm->modem.last_sample = curbit; + hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample); + if ((--sm->modem.dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + + sm->modem.dcd_sum1 + + sm->modem.dcd_sum2) < 0); + sm->modem.dcd_sum2 = sm->modem.dcd_sum1; + sm->modem.dcd_sum1 = sm->modem.dcd_sum0; + sm->modem.dcd_sum0 = 2; /* slight bias */ + sm->modem.dcd_time = 240; + } + if (sm->modem.bit_pll >= 0x10000) { + sm->modem.bit_pll &= 0xffff; + sm->modem.descram = (sm->modem.descram << 1) | curbit; + descx = sm->modem.descram ^ (sm->modem.descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + sm->modem.shreg >>= 1; + sm->modem.shreg |= (!(descx & 1)) << 16; + if (sm->modem.shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1); + sm->modem.shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, ((short)(*buf - 0x80)) << 8); + } +} + +#endif /* ENABLE_SBC */ + +/* --------------------------------------------------------------------- */ + +#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) + +static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen) +{ + int j; + const unsigned char *cp; + + for (; buflen >= 5; buflen -= 5) { + if (sm->modem.shreg <= 1) + sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + sm->modem.scram = ((sm->modem.scram << 1) | + (sm->modem.scram & 1)); + sm->modem.scram ^= (!(sm->modem.shreg & 1)); + sm->modem.shreg >>= 1; + if (sm->modem.scram & (SCRAM_TAP1 << 1)) + sm->modem.scram ^= (SCRAM_TAPN << 1); + sm->modem.tx_bit = (sm->modem.tx_bit << 1) | + (!!(sm->modem.scram & (SCRAM_TAP1 << 2))); + cp = tx_filter_9k6_5 + (sm->modem.tx_bit & 0xff); + for (j = 0; j < 5; j++) { + *buf++ = *cp; + cp += 0x100; + } + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen) +{ + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + sm->modem.dcd_shreg <<= 1; + sm->modem.bit_pll += 0x3333; + curbit = (*buf >= 0x80); + if (sm->modem.last_sample ^ curbit) { + sm->modem.dcd_shreg |= 1; + sm->modem.bit_pll += pll_corr + [sm->modem.bit_pll < 0x9999]; + sm->modem.dcd_sum0 += 16 * + hweight8(sm->modem.dcd_shreg & 0x0c) - + hweight8(sm->modem.dcd_shreg & 0x70); + } + sm->modem.last_sample = curbit; + hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample); + if ((--sm->modem.dcd_time) <= 0) { + hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 + + sm->modem.dcd_sum1 + + sm->modem.dcd_sum2) < 0); + sm->modem.dcd_sum2 = sm->modem.dcd_sum1; + sm->modem.dcd_sum1 = sm->modem.dcd_sum0; + sm->modem.dcd_sum0 = 2; /* slight bias */ + sm->modem.dcd_time = 240; + } + if (sm->modem.bit_pll >= 0x10000) { + sm->modem.bit_pll &= 0xffff; + sm->modem.descram = (sm->modem.descram << 1) | curbit; + descx = sm->modem.descram ^ (sm->modem.descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + sm->modem.shreg >>= 1; + sm->modem.shreg |= (!(descx & 1)) << 16; + if (sm->modem.shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1); + sm->modem.shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, ((short)(*buf - 0x80)) << 8); + } +} + +#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */ +#endif /* ENABLE_FSK9600 */ + +/* --------------------------------------------------------------------- */ +/* + * ===================== soundblaster specific routines =================== + */ + +#ifdef ENABLE_SBC + +static int inline reset_dsp(struct device *dev) +{ + int i; + + outb(1, DSP_RESET(dev->base_addr)); + for (i = 0; i < 0x100; i++) + SLOW_DOWN_IO; + outb(0, DSP_RESET(dev->base_addr)); + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) + if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa) + return 1; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void inline write_dsp(struct device *dev, unsigned char data) +{ + int i; + + for (i = 0; i < 0xffff; i++) + if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) { + outb(data, DSP_WRITE_DATA(dev->base_addr)); + return; + } +} + +/* --------------------------------------------------------------------- */ + +static int inline read_dsp(struct device *dev, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) { + *data = inb(DSP_READ_DATA(dev->base_addr)); + return 1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void inline sbc_int_ack(struct device *dev) +{ + inb(DSP_DATA_AVAIL(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static void setup_dma_dsp(struct device *dev, int send) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned long flags; + static const unsigned char sbcmode[2][2] = { + { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, + { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }}; + static const unsigned char dmamode[2] = + { DMA_MODE_READ | DMA_MODE_AUTOINIT, + DMA_MODE_WRITE | DMA_MODE_AUTOINIT}; + static const unsigned char sbcskr[2] = + { SBC_SPEAKER_OFF, SBC_SPEAKER_ON }; + unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr); + + send = !!send; + if (!reset_dsp(dev)) { + printk(KERN_ERR "sm: cannot reset sb dsp\n"); + return; + } + if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000) + panic("sm: DMA buffer violates DMA boundary!"); + save_flags(flags); + cli(); + sbc_int_ack(dev); + write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */ + write_dsp(dev, mi->data_fmt); + write_dsp(dev, sbcskr[send]); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, dmamode[send]); + set_dma_addr(dev->dma, dmabufaddr); + set_dma_count(dev->dma, mi->dmabuflen); + enable_dma(dev->dma); + sbc_int_ack(dev); + write_dsp(dev, SBC_HISPEED); + write_dsp(dev, ((mi->dmabuflen >> 1) - 1) & 0xff); + write_dsp(dev, ((mi->dmabuflen >> 1) - 1) >> 8); + write_dsp(dev, sbcmode[mi->samplerate >= 13000][send]); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +#if 0 +static int probe_int(struct device *dev, struct sm_state *sm) +{ + unsigned long irqs; + int irq; + + irqs = probe_irq_on(); + setup_dma_dsp(dev, virt_to_bus(sm->modem.dmabufr), 4, 256-77, 0); + udelay(2000); + irq = probe_irq_off(irqs); + disable_dma(dev->dma); + return irq; +} +#endif + +/* --------------------------------------------------------------------- */ + +static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned char new_ptt; + unsigned char *buf; + + if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) + return; + new_ptt = hdlcdrv_ptt(&sm->hdrv); + sbc_int_ack(dev); + buf = sm->modem.dmabufr; + if (sm->modem.dmabufidx) + buf += mi->dmabuflen/2; + sm->modem.dmabufidx = !sm->modem.dmabufidx; + if (sm->modem.oldptt != new_ptt) { + disable_dma(dev->dma); + sti(); + sm->modem.dmabufidx = 0; + if (!new_ptt) { + setup_dma_dsp(dev, 0); + goto endint; + } + mi->demodulator(sm, buf, mi->dmabuflen/2); + mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2); + setup_dma_dsp(dev, 1); + mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2, + mi->dmabuflen/2); + goto endint; + } + sm_int_freq(sm); + sti(); + /* + * check if transmitter active + */ + if (new_ptt) + mi->modulator(sm, buf, mi->dmabuflen/2); + else { + mi->demodulator(sm, buf, mi->dmabuflen/2); + hdlcdrv_arbitrate(dev, &sm->hdrv); + } + endint: + sm->modem.oldptt = new_ptt; + output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int sbc_open(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned char revreq = (mi->samplerate >= 13000) ? 3 : 2; + + if (!dev || !sm) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, SBC_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (!reset_dsp(dev)) + return -ENODEV; + write_dsp(dev, SBC_GET_REVISION); + if (!read_dsp(dev, &sm->modem.revhi) || + !read_dsp(dev, &sm->modem.revlo)) + return -ENODEV; + if (sm->modem.revhi < revreq) { + printk(KERN_ERR "sm: sbc io 0x%lx: DSP rev %d.%02d too " + "old, at least %d.00 required\n", dev->base_addr, + sm->modem.revhi, sm->modem.revlo, revreq); + return -ENODEV; + } + /* + * initialize some variables + */ + if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + sm->modem.shreg = sm->modem.last_sample = 0; + sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0; + sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0; + sm->modem.dmabufidx = sm->modem.oldptt = 0; + sm->modem.dmabufw = NULL; + sm->modem.dcd_time = 120; + sm->modem.dcd_sum0 = 2; +#if 0 + if (!dev->irq) { + int irq = probe_int(dev, sm); + if (irq < 0) { + printk(KERN_ERR "sm: irq autoprobe failed\n"); + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + return -EBUSY; + } + dev->irq = irq; + } +#endif + if (request_dma(dev->dma, mi->mode_name)) { + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + return -EBUSY; + } + if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, + mi->mode_name, dev)) { + free_dma(dev->dma); + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + return -EBUSY; + } + request_region(dev->base_addr, SBC_EXTENT, mi->mode_name); + setup_dma_dsp(dev, 0); + output_open(sm); + printk(KERN_INFO "sm: sbc at iobase 0x%lx irq %u dma " + "%u DSP revision %u.%02u\n", dev->base_addr, dev->irq, + dev->dma, (unsigned int)sm->modem.revhi, + (unsigned int)sm->modem.revlo); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int sbc_close(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + reset_dsp(dev); + free_irq(dev->irq, dev); + free_dma(dev->dma); + release_region(dev->base_addr, SBC_EXTENT); + kfree(sm->modem.dmabufr); + output_close(sm); + printk(KERN_INFO "sm: close sbc at iobase 0x%lx irq %u dma %u\n", + dev->base_addr, dev->irq, dev->dma); + MOD_DEC_USE_COUNT; + return 0; +} + +#endif /* ENABLE_SBC */ + +/* --------------------------------------------------------------------- */ +/* + * ===================== Windows Sound System specific routines ========== + */ + +#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) + +static void write_codec(struct device *dev, unsigned char idx, + unsigned char data) +{ + int timeout = 900000; + + /* wait until codec ready */ + while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) + timeout--; + outb(idx, WSS_CODEC_IA(dev->base_addr)); + outb(data, WSS_CODEC_ID(dev->base_addr)); +} + + +/* --------------------------------------------------------------------- */ + +static unsigned char read_codec(struct device *dev, unsigned char idx) +{ + int timeout = 900000; + + /* wait until codec ready */ + while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) + timeout--; + outb(idx & 0xf, WSS_CODEC_IA(dev->base_addr)); + return inb(WSS_CODEC_ID(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static void inline wss_ack_int(struct device *dev) +{ + outb(0, WSS_CODEC_STATUS(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static int wss_init_codec(struct device *dev, unsigned char sdc, + unsigned char src_l, unsigned char src_r, + int igain_l, int igain_r, + int ogain_l, int ogain_r) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + + unsigned char tmp, reg0, reg1, reg6, reg7; + static const signed char irqtab[16] = + { -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, + -1, -1 }; + static const signed char dmatab[4] = { 1, 2, -1, 3 }; + unsigned long time; + + tmp = inb(WSS_STATUS(dev->base_addr)); + if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && + (tmp & 0x3f) != 0x0f) { + printk(KERN_ERR "sm: WSS card not found, address 0x%lx, ID " + "register 0x%02x\n", dev->base_addr, (int)tmp); + return -1; + } + if ((tmp & 0x80) && ((dev->dma == 0) || ((dev->irq >= 8) && + (dev->irq != 9)))) { + printk(KERN_ERR "sm: WSS: DMA0 and/or IRQ8..IRQ15 (except " + "IRQ9) cannot be used on an 8bit card\n"); + return -1; + } + if (dev->irq > 15 || irqtab[dev->irq] == -1) { + printk(KERN_ERR "sm: WSS: invalid interrupt %d\n", + (int)dev->irq); + return -1; + } + if (dev->dma > 3 || dmatab[dev->dma] == -1) { + printk(KERN_ERR "sm: WSS: invalid dma channel %d\n", + (int)dev->dma); + return -1; + } + tmp = irqtab[dev->irq] | dmatab[dev->dma]; + outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); /* irq probe */ + if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) { + outb(0, WSS_CONFIG(dev->base_addr)); + printk(KERN_ERR "sm: WSS: IRQ%d is not free!\n", dev->irq); + } + outb(tmp, WSS_CONFIG(dev->base_addr)); + /* + * initialize the codec + */ + if (igain_l < 0) + igain_l = 0; + if (igain_r < 0) + igain_r = 0; + if (ogain_l > 0) + ogain_l = 0; + if (ogain_r > 0) + ogain_r = 0; + reg0 = (src_l << 6) & 0xc0; + reg1 = (src_r << 6) & 0xc0; + if (reg0 == 0x80 && igain_l >= 20) { + reg0 |= 0x20; + igain_l -= 20; + } + if (reg1 == 0x80 && igain_r >= 20) { + reg1 |= 0x20; + igain_r -= 20; + } + if (igain_l > 23) + igain_l = 23; + if (igain_r > 23) + igain_r = 23; + reg0 |= igain_l * 2 / 3; + reg1 |= igain_r * 2 / 3; + reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3); + reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3); +#if 1 + write_codec(dev, 9, 0); + write_codec(dev, 15, 0xaa); + write_codec(dev, 14, 0x55); + if ((read_codec(dev, 15) != 0xaa) || (read_codec(dev, 14) != 0x55)) + goto codec_err; +#endif + write_codec(dev, 0x48, mi->data_fmt); /* Clock and data format register */ + write_codec(dev, 0x49, sdc ? 0xc : 0x8); /* MCE and interface config reg */ + /* single DMA channel, disable both DMA */ + /* clear MCE and wait until ACI set */ + time = jiffies + HZ/4; + while (!(read_codec(dev, 0x0b) & 0x20) && + ((signed)(jiffies - time) < 0)); + /* wait until ACI cleared */ + while ((read_codec(dev, 0x0b) & 0x20) && + ((signed)(jiffies - time) < 0)); + if ((signed)(jiffies - time) >= 0) { + printk(KERN_WARNING "sm: ad1848 auto calibration timed out\n"); + goto codec_err; + } + write_codec(dev, 0, reg0); /* left input control */ + write_codec(dev, 1, reg1); /* right input control */ + write_codec(dev, 2, 0x80); /* left aux#1 input control */ + write_codec(dev, 3, 0x80); /* right aux#1 input control */ + write_codec(dev, 4, 0x80); /* left aux#2 input control */ + write_codec(dev, 5, 0x80); /* right aux#2 input control */ + write_codec(dev, 6, reg6); /* left dac control */ + write_codec(dev, 7, reg7); /* right dac control */ + write_codec(dev, 0xa, 0x2); /* pin control register */ + write_codec(dev, 0xd, 0x0); /* digital mix control */ + sm->modem.revhi = inb(WSS_STATUS(dev->base_addr)) & 0x3f; + sm->modem.revlo = read_codec(dev, 0xc) & 0xf; + /* + * print revisions + */ + printk(KERN_INFO "sm: WSS revision %d, CODEC revision %d\n", + (int)sm->modem.revhi, (int)sm->modem.revlo); + return 0; + codec_err: + outb(0, WSS_CONFIG(dev->base_addr)); + printk(KERN_ERR "sm: no WSS soundcard found at address 0x%lx\n", + dev->base_addr); + return -1; +} + +#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */ + +/* --------------------------------------------------------------------- */ + +#ifdef ENABLE_WSS + +static void setup_dma_wss(struct device *dev, int flg) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned long flags; + static const unsigned char codecmode[2] = { 0x0e, 0x0d }; + static const unsigned char dmamode[2] = + { DMA_MODE_READ | DMA_MODE_AUTOINIT, + DMA_MODE_WRITE | DMA_MODE_AUTOINIT}; + unsigned char oldcodecmode, codecdma; + long abrt; + unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr); + + if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000) + panic("sm: DMA buffer violates DMA boundary!"); + flg = !!flg; + save_flags(flags); + cli(); + /* + * perform the final DMA sequence to disable the codec request + */ + oldcodecmode = read_codec(dev, 9); + write_codec(dev, 9, 0xc); /* disable codec */ + wss_ack_int(dev); + if ((codecdma = read_codec(dev, 11)) & 0x10) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]); + set_dma_addr(dev->dma, dmabufaddr); + set_dma_count(dev->dma, mi->dmabuflen); + enable_dma(dev->dma); + abrt = 0; + while (((codecdma = read_codec(dev, 11)) & 0x10) || + ((++abrt) >= 0x10000)); + } + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, dmamode[flg]); + set_dma_addr(dev->dma, dmabufaddr); + set_dma_count(dev->dma, mi->dmabuflen); + enable_dma(dev->dma); + write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff); + write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8); + write_codec(dev, 9, codecmode[flg]); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned char new_ptt; + unsigned char *buf; + unsigned long flags; + int dmares; + + if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) + return; + new_ptt = hdlcdrv_ptt(&sm->hdrv); + save_flags(flags); + cli(); + wss_ack_int(dev); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dmares = get_dma_residue(dev->dma); + enable_dma(dev->dma); + if (dmares <= 0) + dmares = mi->dmabuflen; + buf = sm->modem.dmabufr; + if (dmares > mi->dmabuflen/2) + buf += mi->dmabuflen/2; + if (dmares > mi->dmabuflen/2) + dmares -= mi->dmabuflen/2; +#ifdef SM_DEBUG + if (!sm->debug_vals.last_pllcorr || + dmares < sm->debug_vals.last_pllcorr) + sm->debug_vals.last_pllcorr = dmares; +#endif /* SM_DEBUG */ + dmares--; + write_codec(dev, 15, dmares & 0xff); + write_codec(dev, 14, dmares >> 8); + restore_flags(flags); + if (sm->modem.oldptt != new_ptt) { + disable_dma(dev->dma); + sti(); + sm->modem.dmabufidx = 0; + if (!new_ptt) { + setup_dma_wss(dev, 0); + goto endint; + } + mi->demodulator(sm, buf, mi->dmabuflen/2); + mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2); + setup_dma_wss(dev, 1); + mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2, + mi->dmabuflen/2); + goto endint; + } + sm_int_freq(sm); + sti(); + /* + * check if transmitter active + */ + if (new_ptt) + mi->modulator(sm, buf, mi->dmabuflen/2); + else { + mi->demodulator(sm, buf, mi->dmabuflen/2); + hdlcdrv_arbitrate(dev, &sm->hdrv); + } + endint: + sm->modem.oldptt = new_ptt; + output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int wss_open(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + + if (!dev || !sm) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, WSS_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (wss_init_codec(dev, 1, 1, 1, 0, 0, -45, -45)) + return -ENODEV; + /* + * initialize some variables + */ + if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + sm->modem.shreg = sm->modem.last_sample = 0; + sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0; + sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0; + sm->modem.dmabufidx = sm->modem.oldptt = 0; + sm->modem.dmabufw = NULL; + sm->modem.dcd_time = 120; + sm->modem.dcd_sum0 = 2; + if (request_dma(dev->dma, mi->mode_name)) { + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + return -EBUSY; + } + if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, + mi->mode_name, dev)) { + free_dma(dev->dma); + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + return -EBUSY; + } + request_region(dev->base_addr, WSS_EXTENT, mi->mode_name); + setup_dma_wss(dev, 0); + output_open(sm); + printk(KERN_INFO "sm: wss at iobase 0x%lx irq %u dma " + "%u\n", dev->base_addr, dev->irq, dev->dma); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wss_close(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + write_codec(dev, 9, 0xc); /* disable codec */ + free_irq(dev->irq, dev); + free_dma(dev->dma); + release_region(dev->base_addr, WSS_EXTENT); + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + output_close(sm); + printk(KERN_INFO "sm: close wss at iobase 0x%lx irq %u" + " dma %u\n", dev->base_addr, dev->irq, dev->dma); + MOD_DEC_USE_COUNT; + return 0; +} + +#endif /* ENABLE_WSS */ + +/* --------------------------------------------------------------------- */ +/* + * =========== Windows Sound System Fullduplex specific routines ========== + */ + +/* + * This does _not_ work on my hardware + */ + +#ifdef ENABLE_WSSFDX + +static void setup_dma_wssfdx(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned long flags; + unsigned char codecdma; + long abrt; + unsigned long dmabufraddr = virt_to_bus(sm->modem.dmabufr); + unsigned long dmabufwaddr = virt_to_bus(sm->modem.dmabufw); + + if (((dmabufraddr & 0xffff) + mi->dmabuflen > 0x10000) || + ((dmabufwaddr & 0xffff) + mi->dmabuflen > 0x10000)) + panic("sm: DMA buffer violates DMA boundary!"); + save_flags(flags); + cli(); + /* + * perform the final DMA sequence to disable the codec request + */ + write_codec(dev, 9, 0x8); /* disable codec */ + wss_ack_int(dev); + if ((codecdma = read_codec(dev, 11)) & 0x10) { + disable_dma(dev->dma); + disable_dma(!dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dev->dma, dmabufwaddr); + set_dma_count(dev->dma, mi->dmabuflen); + set_dma_mode(!dev->dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(!dev->dma, dmabufraddr); + set_dma_count(!dev->dma, mi->dmabuflen); + enable_dma(dev->dma); + enable_dma(!dev->dma); + abrt = 0; + while (((codecdma = read_codec(dev, 11)) & 0x10) || + ((++abrt) >= 0x10000)); + } + disable_dma(dev->dma); + disable_dma(!dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dev->dma, dmabufwaddr); + set_dma_count(dev->dma, mi->dmabuflen); + set_dma_mode(!dev->dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(!dev->dma, dmabufraddr); + set_dma_count(!dev->dma, mi->dmabuflen); + enable_dma(dev->dma); + enable_dma(!dev->dma); + write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff); + write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8); + write_codec(dev, 9, 0x0b); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static int msgcnt = 10; + +static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + unsigned char new_ptt; + unsigned char *bufr; + unsigned char *bufw; + unsigned long flags; + int dmares, dmares2, i; + + if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) + return; + new_ptt = hdlcdrv_ptt(&sm->hdrv); + save_flags(flags); + cli(); + wss_ack_int(dev); + disable_dma(dev->dma); + disable_dma(!dev->dma); + clear_dma_ff(dev->dma); + dmares = get_dma_residue(dev->dma); + dmares2 = get_dma_residue(!dev->dma); + enable_dma(dev->dma); + enable_dma(!dev->dma); + if (dmares <= 0) + dmares = mi->dmabuflen; + if (dmares2 <= 0) + dmares2 = mi->dmabuflen; + bufw = sm->modem.dmabufw; + if (dmares > mi->dmabuflen/2) + bufw += mi->dmabuflen/2; + bufr = sm->modem.dmabufr; + if (dmares2 > mi->dmabuflen/2) + bufr += mi->dmabuflen/2; + if ((i = dmares) > mi->dmabuflen/2) + i -= mi->dmabuflen/2; +#ifdef SM_DEBUG + if (!sm->debug_vals.last_pllcorr || + i < sm->debug_vals.last_pllcorr) + sm->debug_vals.last_pllcorr = i; +#endif /* SM_DEBUG */ + i--; + write_codec(dev, 15, i & 0xff); + write_codec(dev, 14, i >> 8); + restore_flags(flags); + sm_int_freq(sm); + sti(); + + if (msgcnt > 0) { + msgcnt--; + printk(KERN_DEBUG "sm: DMA residue: playback: %d " + "capture: %d\n", dmares, dmares2); + } + + /* + * check if transmitter active + */ + if (new_ptt) + mi->modulator(sm, bufw, mi->dmabuflen/2); + else + memset(bufw, 0x80, mi->dmabuflen/2); + mi->demodulator(sm, bufr, mi->dmabuflen/2); + hdlcdrv_arbitrate(dev, &sm->hdrv); + sm->modem.oldptt = new_ptt; + output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int wssfdx_open(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + struct modem_info *mi = (struct modem_info *)sm->hdrv.ops; + + if (!dev || !sm) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, WSS_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (wss_init_codec(dev, 0, 1, 1, 0, 0, -45, -45)) + return -ENODEV; + /* + * initialize some variables + */ + if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + if (!(sm->modem.dmabufw = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) { + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + return -ENOMEM; + } + sm->modem.shreg = sm->modem.last_sample = 0; + sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0; + sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0; + sm->modem.dmabufidx = sm->modem.oldptt = 0; + sm->modem.dcd_time = 120; + sm->modem.dcd_sum0 = 2; + if (request_dma(dev->dma, mi->mode_name)) { + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + kfree_s(sm->modem.dmabufw, mi->dmabuflen); + return -EBUSY; + } + if (request_dma(!dev->dma, mi->mode_name)) { + free_dma(dev->dma); + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + kfree_s(sm->modem.dmabufw, mi->dmabuflen); + return -EBUSY; + } + if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, + mi->mode_name, dev)) { + free_dma(dev->dma); + free_dma(!dev->dma); + kfree_s(sm->modem.dmabufr, mi->dmabuflen); + kfree_s(sm->modem.dmabufw, mi->dmabuflen); + return -EBUSY; + } + request_region(dev->base_addr, WSS_EXTENT, mi->mode_name); + setup_dma_wssfdx(dev); + output_open(sm); + printk(KERN_INFO "sm: wss fdx at iobase 0x%lx irq %u dma1 " + "%u dma2 %u\n", dev->base_addr, dev->irq, dev->dma, !dev->dma); + MOD_INC_USE_COUNT; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wssfdx_close(struct device *dev) +{ + struct sm_state *sm = (struct sm_state *)dev->priv; + + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + disable_dma(!dev->dma); + write_codec(dev, 9, 0x8); /* disable codec */ + free_irq(dev->irq, dev); + free_dma(dev->dma); + free_dma(!dev->dma); + release_region(dev->base_addr, WSS_EXTENT); + kfree(sm->modem.dmabufr); + kfree(sm->modem.dmabufw); + output_close(sm); + printk(KERN_INFO "sm: close wss fdx at iobase 0x%lx irq %u" + " dma %u\n", dev->base_addr, dev->irq, dev->dma); + MOD_DEC_USE_COUNT; + return 0; +} + +#endif /* ENABLE_WSSFDX */ + +/* --------------------------------------------------------------------- */ +/* + * ===================== hdlcdrv driver interface ========================= + */ + +static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd); + +#define SBC1200_SRATE (256-104) /* the SBC sampling rate, 256-(1E6/srate) */ +#define SBC1200_DMABUFLEN 192 /* DMA buffer duration exactly 20ms */ + +#define SBC9600_SRATE (256-26) /* the SBC sampling rate, 256-(1E6/srate) */ +#define SBC9600_DMABUFLEN 768 /* DMA buffer duration exactly 20ms */ + +#define WSS1200_DATAFMT 0x0e /* 8bit unsigned PCM, Mono, XTAL1, 9.6kHz */ +#define WSS1200_DMABUFLEN 192 /* DMA buffer duration exactly 20ms */ + +#define WSS9600_DATAFMT 0x0c /* 8bit unsigned PCM, Mono, XTAL1, 48kHz */ +#define WSS9600_DMABUFLEN 960 /* DMA buffer duration exactly 20ms */ + +/* --------------------------------------------------------------------- */ + +#ifdef ENABLE_SBC + +static const struct modem_info sbc1200_ops = { +{ 1200, sbc_open, sbc_close, sm_ioctl }, 9600, 1, 8, "sbc_1200", +SBC1200_SRATE, SBC1200_DMABUFLEN, +modulator_1200, demodulator_1200 +}; +/* --------------------------------------------------------------------- */ + +static const struct modem_info sbc9600_ops = { +{ 9600, sbc_open, sbc_close, sm_ioctl }, 38400, 1, 4, "sbc_9600", +SBC9600_SRATE, SBC9600_DMABUFLEN, +modulator_9600_4, demodulator_9600_4 +}; + +#endif /* ENABLE_SBC */ + +/* --------------------------------------------------------------------- */ + +#ifdef ENABLE_WSS + +static const struct modem_info wss1200_ops = { +{ 1200, wss_open, wss_close, sm_ioctl }, 9600, 0, 8, "wss_1200", +WSS1200_DATAFMT, WSS1200_DMABUFLEN, +modulator_1200, demodulator_1200 +}; + +/* --------------------------------------------------------------------- */ + +static const struct modem_info wss9600_ops = { +{ 9600, wss_open, wss_close, sm_ioctl }, 48000, 0, 5, "wss_9600", +WSS9600_DATAFMT, WSS9600_DMABUFLEN, +modulator_9600_5, demodulator_9600_5 +}; + +#endif /* ENABLE_WSS */ + +/* --------------------------------------------------------------------- */ + +#ifdef ENABLE_WSSFDX + +static const struct modem_info wss1200fdx_ops = { +{ 1200, wssfdx_open, wssfdx_close, sm_ioctl }, 9600, 0, 8, "wss_fdx_1200", +WSS1200_DATAFMT, WSS1200_DMABUFLEN, +modulator_1200, demodulator_1200 +}; + +/* --------------------------------------------------------------------- */ + +static const struct modem_info wss9600fdx_ops = { +{ 9600, wssfdx_open, wssfdx_close, sm_ioctl }, 48000, 0, 5, "wss_fdx_9600", +WSS9600_DATAFMT, WSS9600_DMABUFLEN, +modulator_9600_5, demodulator_9600_5 +}; + +#endif /* ENABLE_WSSFDX */ + +/* --------------------------------------------------------------------- */ + +static const struct modem_info dummy_ops = { +{ 0, NULL, NULL, sm_ioctl }, 0, 0, 0, "none", +0, 0, +NULL, NULL +}; + +/* --------------------------------------------------------------------- */ + +static const struct modem_info *ops_tab[3][2] = { +#ifdef ENABLE_SBC +{ &sbc1200_ops, &sbc9600_ops }, +#else /* ENABLE_SBC */ +{ &dummy_ops, &dummy_ops }, +#endif /* ENABLE_SBC */ +#ifdef ENABLE_WSS +{ &wss1200_ops, &wss9600_ops }, +#else /* ENABLE_WSS */ +{ &dummy_ops, &dummy_ops }, +#endif /* ENABLE_WSS */ +#ifdef ENABLE_WSSFDX +{ &wss1200fdx_ops, &wss9600fdx_ops } +#else /* ENABLE_WSSFDX */ +{ &dummy_ops, &dummy_ops } +#endif /* ENABLE_WSSFDX */ +}; + +/* --------------------------------------------------------------------- */ + +static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + int i; + struct sm_state *sm; + struct sm_ioctl bi; + unsigned long flags; + unsigned int newdiagmode; + unsigned int newdiagflags; + + if (!dev || !dev->priv || + ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { + printk(KERN_ERR "sm_ioctl: invalid device struct\n"); + return -EINVAL; + } + sm = (struct sm_state *)dev->priv; + + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + + switch (bi.cmd) { + default: + return -ENOIOCTLCMD; + + case SMCTL_GETMODEMTYPE: + bi.data.cfg.hardware = sm->config.hardware; + bi.data.cfg.mode = sm->config.mode; + break; + + case SMCTL_SETMODEMTYPE: + if (!suser() || dev->start) + return -EACCES; + if (bi.data.cfg.hardware < SM_HARDWARE_INVALID || + bi.data.cfg.hardware > SM_HARDWARE_WSSFDX || + bi.data.cfg.mode < SM_MODE_INVALID || + bi.data.cfg.mode > SM_MODE_FSK9600) + return -EINVAL; + sm->config.hardware = bi.data.cfg.hardware; + sm->config.mode = bi.data.cfg.mode; + if (bi.data.cfg.hardware == SM_HARDWARE_INVALID || + bi.data.cfg.mode == SM_MODE_INVALID) + sm->hdrv.ops = &dummy_ops.hops; + else { + sm->hdrv.ops = &ops_tab[sm->config.hardware][sm->config.mode]->hops; + if (!((struct modem_info *)sm->hdrv.ops)->samplerate) + return -ENODEV; + } + return 0; + +#ifdef SM_DEBUG + case SMCTL_GETDEBUG: + bi.data.dbg.debug1 = sm->hdrv.ptt_keyed; + bi.data.dbg.debug2 = sm->debug_vals.last_intcnt; + bi.data.dbg.debug3 = sm->debug_vals.last_pllcorr; + break; +#endif /* SM_DEBUG */ + + case SMCTL_GETMIXER: + i = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(bi)); + if (i) + return i; + i = 0; + bi.data.mix.sample_rate = ((struct modem_info *)sm->hdrv.ops)->samplerate; + bi.data.mix.bit_rate = sm->hdrv.ops->bitrate; + if (((struct modem_info *)sm->hdrv.ops)->sbcmix) { + switch (sm->modem.revhi) { + case 2: + bi.data.mix.mixer_type = SM_MIXER_CT1335; + break; + case 3: + bi.data.mix.mixer_type = SM_MIXER_CT1345; + break; + case 4: + bi.data.mix.mixer_type = SM_MIXER_CT1745; + break; + } + if (bi.data.mix.mixer_type != SM_MIXER_INVALID && + bi.data.mix.reg < 0x80) { + save_flags(flags); + cli(); + outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); + bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr)); + restore_flags(flags); + i = 1; + } + } else { + bi.data.mix.mixer_type = SM_MIXER_AD1848; + if ((0x20ff >> bi.data.mix.reg) & 1) { + bi.data.mix.data = read_codec(dev, bi.data.mix.reg); + i = 1; + } + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return i; + + case SMCTL_SETMIXER: + if (!suser()) + return -EACCES; + if (((struct modem_info *)sm->hdrv.ops)->sbcmix) { + switch (sm->modem.revhi) { + case 2: + if (bi.data.mix.mixer_type != SM_MIXER_CT1335) + return -EINVAL; + break; + case 3: + if (bi.data.mix.mixer_type != SM_MIXER_CT1345) + return -EINVAL; + break; + case 4: + if (bi.data.mix.mixer_type != SM_MIXER_CT1745) + return -EINVAL; + break; + default: + return -ENODEV; + } + if (bi.data.mix.reg >= 0x80) + return -EACCES; + save_flags(flags); + cli(); + outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); + outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr)); + restore_flags(flags); + return 0; + } else { + if (bi.data.mix.mixer_type != SM_MIXER_AD1848) + return -EINVAL; + if (!((0x20ff >> bi.data.mix.reg) & 1)) + return -EACCES; + write_codec(dev, bi.data.mix.reg, bi.data.mix.data); + return 0; + } + + case SMCTL_DIAGNOSE: + newdiagmode = bi.data.diag.mode; + newdiagflags = bi.data.diag.flags; + if (newdiagmode > SM_DIAGMODE_DEMOD) + return -EINVAL; + bi.data.diag.mode = sm->diag.mode; + bi.data.diag.flags = sm->diag.flags; + bi.data.diag.samplesperbit = ((struct modem_info *)sm->hdrv.ops)->sperbit; + if (sm->diag.mode != newdiagmode) { + save_flags(flags); + cli(); + sm->diag.ptr = -1; + sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID; + sm->diag.mode = newdiagmode; + restore_flags(flags); + break; + } + if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) + break; + if (bi.data.diag.datalen > DIAGDATALEN) + bi.data.diag.datalen = DIAGDATALEN; + if (sm->diag.ptr < bi.data.diag.datalen) + break; + i = verify_area(VERIFY_WRITE, bi.data.diag.data, + bi.data.diag.datalen * + sizeof(short)); + if (i) + return i; + if (copy_to_user(bi.data.diag.data, sm->diag.data, + bi.data.diag.datalen * sizeof(short))) + return -EFAULT; + bi.data.diag.flags |= SM_DIAGFLAG_VALID; + save_flags(flags); + cli(); + sm->diag.ptr = -1; + sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID; + sm->diag.mode = newdiagmode; + restore_flags(flags); + break; + + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +static +#endif /* MODULE */ +int sm_init(void) +{ + int i, j, found = 0; + char set_hw = 1; + struct sm_state *sm; + char ifname[HDLCDRV_IFNAMELEN]; + + printk(KERN_INFO "sm: compiled %s %s\n", __TIME__, __DATE__); + /* + * register net devices + */ + for (i = 0; i < NR_PORTS; i++) { + struct device *dev = sm_device+i; + sprintf(ifname, "sm%d", i); + + if (sm_ports[i].hardware < SM_HARDWARE_INVALID || + sm_ports[i].hardware > SM_HARDWARE_WSSFDX || + sm_ports[i].mode < SM_MODE_INVALID || + sm_ports[i].mode > SM_MODE_FSK9600) + set_hw = 0; + if (set_hw) { + j = hdlcdrv_register_hdlcdrv(dev, &ops_tab[sm_ports[i].hardware] + [sm_ports[i].mode]->hops, + sizeof(struct sm_state), ifname, + sm_ports[i].iobase, + sm_ports[i].irq, + sm_ports[i].dma); + if (!j) { + sm = (struct sm_state *)dev->priv; + sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase; + sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase; + sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase; + } + } else + j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops.hops, + sizeof(struct sm_state), + ifname, 0, 0, 0); + if (j) { + printk(KERN_WARNING "sm: cannot register net " + "device\n"); + } else + found++; + } + if (!found) + return -ENXIO; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +/* + * command line settable parameters + */ +int hardware = SM_HARDWARE_INVALID; +int mode = SM_MODE_INVALID; +int iobase = -1; +int irq = -1; +int dma = -1; +int seriobase = 0; +int pariobase = 0; +int midiiobase = 0; + +int init_module(void) +{ + printk(KERN_INFO "sm: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n"); + + if (hardware != SM_HARDWARE_INVALID) { + if (iobase == -1) + iobase = (hardware == SM_HARDWARE_SBC) ? 0x220 : 0x530; + if (irq == -1) + irq = (hardware == SM_HARDWARE_SBC) ? 5 : 11; + if (dma == -1) + dma = 1; + } + sm_ports[0].hardware = hardware; + sm_ports[0].mode = mode; + sm_ports[0].iobase = iobase; + sm_ports[0].irq = irq; + sm_ports[0].dma = dma; + sm_ports[0].seriobase = seriobase; + sm_ports[0].pariobase = pariobase; + sm_ports[0].midiiobase = midiiobase; + sm_ports[1].hardware = SM_HARDWARE_INVALID; + + return sm_init(); +} + +/* --------------------------------------------------------------------- */ + +void cleanup_module(void) +{ + int i; + + printk(KERN_INFO "sm: cleanup_module called\n"); + + for(i = 0; i < NR_PORTS; i++) { + struct device *dev = sm_device+i; + struct sm_state *sm = (struct sm_state *)dev->priv; + + if (sm) { + if (sm->hdrv.magic != HDLCDRV_MAGIC) + printk(KERN_ERR "sm: invalid magic in " + "cleanup_module\n"); + else + hdlcdrv_unregister_hdlcdrv(dev); + } + } +} + +#else /* MODULE */ +/* --------------------------------------------------------------------- */ +/* + * format: sm=hw,mode,io,irq,dma,serio,pario[,hw,mode,io,irq,dma,serio,pario] + * hw=0: SBC, hw=1: WSS; mode=0: AFSK1200, mode=1: FSK9600 + */ + +void sm_setup(char *str, int *ints) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + if (ints[0] >= 8*(i+1)) { + sm_ports[i].hardware = ints[8*i+1]; + sm_ports[i].mode = ints[8*i+2]; + sm_ports[i].iobase = ints[8*i+3]; + sm_ports[i].irq = ints[8*i+4]; + sm_ports[i].dma = ints[8*i+5]; + sm_ports[i].seriobase = ints[8*i+6]; + sm_ports[i].pariobase = ints[8*i+7]; + sm_ports[i].midiiobase = ints[8*i+8]; + } else + sm_ports[i].hardware = SM_HARDWARE_INVALID; + +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.6/linux/drivers/net/tulip.c linux/drivers/net/tulip.c --- v2.1.6/linux/drivers/net/tulip.c Thu Oct 10 19:10:56 1996 +++ linux/drivers/net/tulip.c Fri Nov 1 13:58:44 1996 @@ -1133,6 +1133,25 @@ } while (++i < 15); /* Now add this frame to the Tx list. */ + { + unsigned long flags; + unsigned int entry; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + tp->dirty_tx++; + restore_flags(flags); + + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + tp->tx_ring[entry].length = 192 | + (entry == TX_RING_SIZE-1 ? 0x0a000000 : 0x08000000); + tp->tx_ring[entry].buffer1 = virt_to_bus((char *)tp->setup_frame); + tp->tx_ring[entry].buffer2 = 0; + tp->tx_ring[entry].status = TRING_OWN; + /* Trigger an immediate transmit demand. */ + tio_write(TPOLL_TRIGGER, CSR1); + } } } diff -u --recursive --new-file v2.1.6/linux/drivers/scsi/aha1740.c linux/drivers/scsi/aha1740.c --- v2.1.6/linux/drivers/scsi/aha1740.c Fri Mar 1 07:50:52 1996 +++ linux/drivers/scsi/aha1740.c Wed Oct 30 01:20:26 1996 @@ -336,11 +336,11 @@ if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n"); for(i=0; iuse_sg; i++) { - cptr[i].dataptr = (long) sgpnt[i].address; cptr[i].datalen = sgpnt[i].length; + cptr[i].dataptr = virt_to_bus(sgpnt[i].address); } ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain); - ecb[ecbno].dataptr = (long) cptr; + ecb[ecbno].dataptr = virt_to_bus(cptr); #ifdef DEBUG printk("cptr %x: ",cptr); ptr = (unsigned char *) cptr; @@ -351,15 +351,15 @@ { SCpnt->host_scribble = NULL; ecb[ecbno].datalen = bufflen; - ecb[ecbno].dataptr = (long) buff; + ecb[ecbno].dataptr = virt_to_bus(buff); } ecb[ecbno].lun = SCpnt->lun; ecb[ecbno].ses = 1; /* Suppress underrun errors */ ecb[ecbno].dir= direction; ecb[ecbno].ars=1; /* Yes, get the sense on an error */ ecb[ecbno].senselen = 12; - ecb[ecbno].senseptr = (long) ecb[ecbno].sense; - ecb[ecbno].statusptr = (long) ecb[ecbno].status; + ecb[ecbno].senseptr = virt_to_bus(ecb[ecbno].sense); + ecb[ecbno].statusptr = virt_to_bus(ecb[ecbno].status); ecb[ecbno].done = done; ecb[ecbno].SCpnt = SCpnt; #ifdef DEBUG diff -u --recursive --new-file v2.1.6/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.1.6/linux/drivers/scsi/hosts.c Tue Oct 29 19:58:13 1996 +++ linux/drivers/scsi/hosts.c Thu Oct 31 09:16:01 1996 @@ -224,12 +224,6 @@ #ifdef CONFIG_SCSI_ADVANSYS ADVANSYS, #endif -#ifdef CONFIG_SCSI_EATA_DMA - EATA_DMA, -#endif -#ifdef CONFIG_SCSI_EATA_PIO - EATA_PIO, -#endif /* BusLogic must come before aha1542.c */ #ifdef CONFIG_SCSI_BUSLOGIC BUSLOGIC, @@ -287,6 +281,12 @@ #endif #ifdef CONFIG_SCSI_NCR53C8XX NCR53C8XX, +#endif +#ifdef CONFIG_SCSI_EATA_DMA + EATA_DMA, +#endif +#ifdef CONFIG_SCSI_EATA_PIO + EATA_PIO, #endif #ifdef CONFIG_SCSI_7000FASST WD7000, diff -u --recursive --new-file v2.1.6/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.1.6/linux/drivers/scsi/scsi.c Tue Oct 29 19:58:13 1996 +++ linux/drivers/scsi/scsi.c Wed Oct 30 03:11:51 1996 @@ -1799,7 +1799,8 @@ if ((++SCpnt->retries) < SCpnt->allowed) { if ((SCpnt->retries >= (SCpnt->allowed >> 1)) - && !(jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) + && !(SCpnt->host->last_reset > 0 && + jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) && !(SCpnt->flags & WAS_RESET)) { printk("scsi%d channel %d : resetting for second half of retries.\n", diff -u --recursive --new-file v2.1.6/linux/fs/ioctl.c linux/fs/ioctl.c --- v2.1.6/linux/fs/ioctl.c Tue Oct 29 19:58:42 1996 +++ linux/fs/ioctl.c Wed Oct 30 18:41:44 1996 @@ -25,27 +25,18 @@ return -EBADF; if (filp->f_inode->i_op->bmap == NULL) return -EINVAL; - error = verify_area(VERIFY_WRITE,(void *) arg,4); - if (error) + if ((error = get_user(block, (int *) arg)) != 0) return error; - get_user(block, (int *) arg); block = filp->f_inode->i_op->bmap(filp->f_inode,block); - put_user(block,(int *) arg); - return 0; + return put_user(block, (int *) arg); case FIGETBSZ: if (filp->f_inode->i_sb == NULL) return -EBADF; - error = verify_area(VERIFY_WRITE,(void *) arg,4); - if (error) - return error; - put_user(filp->f_inode->i_sb->s_blocksize, (int *) arg); - return 0; + return put_user(filp->f_inode->i_sb->s_blocksize, + (int *) arg); case FIONREAD: - error = verify_area(VERIFY_WRITE,(void *) arg,sizeof(int)); - if (error) - return error; - put_user(filp->f_inode->i_size - filp->f_pos, (int *) arg); - return 0; + return put_user(filp->f_inode->i_size - filp->f_pos, + (int *) arg); } if (filp->f_op && filp->f_op->ioctl) return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg); @@ -56,7 +47,7 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; - int on; + int on, error; if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) return -EBADF; @@ -70,11 +61,8 @@ return 0; case FIONBIO: - on = verify_area(VERIFY_READ, (unsigned int *)arg, - sizeof(unsigned int)); - if(on) - return on; - get_user(on, (unsigned int *) arg); + if ((error = get_user(on, (int *)arg)) != 0) + return error; if (on) filp->f_flags |= O_NONBLOCK; else @@ -83,11 +71,8 @@ case FIOASYNC: /* O_SYNC is not yet implemented, but it's here for completeness. */ - on = verify_area(VERIFY_READ, (unsigned int *)arg, - sizeof(unsigned int)); - if(on) - return on; - get_user(on, (unsigned int *) arg); + if ((error = get_user(on, (int *)arg)) != 0) + return error; if (on) filp->f_flags |= O_SYNC; else diff -u --recursive --new-file v2.1.6/linux/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h --- v2.1.6/linux/fs/ncpfs/ncplib_kernel.h Sat Mar 2 13:15:35 1996 +++ linux/fs/ncpfs/ncplib_kernel.h Wed Oct 30 01:24:35 1996 @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff -u --recursive --new-file v2.1.6/linux/fs/super.c linux/fs/super.c --- v2.1.6/linux/fs/super.c Tue Oct 29 19:58:44 1996 +++ linux/fs/super.c Wed Oct 30 01:58:50 1996 @@ -146,6 +146,8 @@ if (vfsmnttail->mnt_dev == dev) vfsmnttail = lptr; } + if (tofree == mru_vfsmnt) + mru_vfsmnt = NULL; kfree(tofree->mnt_devname); kfree(tofree->mnt_dirname); kfree_s(tofree, sizeof(struct vfsmount)); diff -u --recursive --new-file v2.1.6/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h --- v2.1.6/linux/include/asm-alpha/processor.h Wed Oct 16 10:48:29 1996 +++ linux/include/asm-alpha/processor.h Thu Oct 31 13:59:59 1996 @@ -20,15 +20,6 @@ #define MCA_bus 0 #define MCA_bus__is_a_macro /* for versions in ksyms.c */ -/* - * The VM exception save area. We need to save only the - * exception count, so that the exception handling can know - * whether the system is set up to handle exceptions.. - */ -struct exception_struct { - unsigned long count; -}; - struct thread_struct { /* the fields below are used by PALcode and must match struct pcb: */ unsigned long ksp; @@ -45,12 +36,10 @@ unsigned long res1, res2; /* the fields below are Linux-specific: */ - /* - * bit 0: perform syscall argument validation (get/set_fs) - * bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h) - */ + /* bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h) */ unsigned long flags; - struct exception_struct ex; + /* perform syscall argument validation (get/set_fs) */ + unsigned long fs; }; #define INIT_MMAP { &init_mm, 0xfffffc0000000000, 0xfffffc0010000000, \ @@ -61,7 +50,7 @@ 0, 0, 0, \ 0, 0, 0, \ 0, \ - { 0 } \ + 0 \ } #define alloc_kernel_stack() __get_free_page(GFP_KERNEL) diff -u --recursive --new-file v2.1.6/linux/include/asm-alpha/uaccess.h linux/include/asm-alpha/uaccess.h --- v2.1.6/linux/include/asm-alpha/uaccess.h Tue Oct 29 19:58:47 1996 +++ linux/include/asm-alpha/uaccess.h Thu Oct 31 13:59:59 1996 @@ -1,5 +1,9 @@ -#ifndef _ASM_SEGMENT_H -#define _ASM_SEGMENT_H +#ifndef __ALPHA_UACCESS_H +#define __ALPHA_UACCESS_H + +#include +#include + /* * The fs value determines whether argument validity checking should be @@ -9,19 +13,15 @@ * For historical reasons, these macros are grossly misnamed. */ -#define KERNEL_DS 0 -#define USER_DS 1 +#define KERNEL_DS (0UL) +#define USER_DS (-0x40000000000UL) #define VERIFY_READ 0 #define VERIFY_WRITE 1 -#define get_fs() (current->tss.flags & 0x1) -#define set_fs(x) (current->tss.flags = (current->tss.flags & ~0x1) | ((x) & 0x1)) - -static inline unsigned long get_ds(void) -{ - return 0; -} +#define get_fs() (current->tss.fs) +#define set_fs(x) (current->tss.fs = (x)) +#define get_ds() (KERNEL_DS) /* * Is a address valid? This does a straighforward calculation rather @@ -34,12 +34,17 @@ * - OR we are in kernel mode. */ #define __access_ok(addr,size,mask) \ - (((mask)&((addr | size | (addr+size)) >> 42))==0) -#define __access_mask (-(long)get_fs()) + (((mask) & (addr | size | (addr+size))) == 0) +#define __access_mask get_fs() #define access_ok(type,addr,size) \ __access_ok(((unsigned long)(addr)),(size),__access_mask) +extern inline int verify_area(int type, const void * addr, unsigned long size) +{ + return access_ok(type,addr,size) ? 0 : -EFAULT; +} + /* * These are the main single-value transfer routines. They automatically * use the right size if we just have the right pointer type. @@ -52,56 +57,39 @@ * (a) re-use the arguments for side effects (sizeof/typeof is ok) * (b) require any knowledge of processes at this stage */ -#define put_user(x,ptr) __put_user_check((x),(ptr),sizeof(*(ptr)),__access_mask) -#define get_user(x,ptr) __get_user_check((x),(ptr),sizeof(*(ptr)),__access_mask) +#define put_user(x,ptr) \ + __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)),__access_mask) +#define get_user(x,ptr) \ + __get_user_check((x),(ptr),sizeof(*(ptr)),__access_mask) /* * The "__xxx" versions do not do address space checking, useful when - * doing multiple accesses to the same area (the user has to do the + * doing multiple accesses to the same area (the programmer has to do the * checks by hand with "access_ok()") */ -#define __put_user(x,ptr) __put_user_nocheck((x),(ptr),sizeof(*(ptr))) -#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr))) - -#define copy_to_user(to,from,n) __copy_tofrom_user((to),(from),(n),__cu_to) -#define copy_from_user(to,from,n) __copy_tofrom_user((to),(from),(n),__cu_from) +#define __put_user(x,ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define __get_user(x,ptr) \ + __get_user_nocheck((x),(ptr),sizeof(*(ptr))) /* - * Not pretty? What do you mean not "not pretty"? + * The "lda %1, 2b-1b(%0)" bits are magic to get the assembler to + * encode the bits we need for resolving the exception. See the + * more extensive comments with fixup_inline_exception below for + * more information. */ -extern void __copy_user(void); - -#define __copy_tofrom_user(to,from,n,v) \ -({ \ - register void * __cu_to __asm__("$6") = (to); \ - register const void * __cu_from __asm__("$7") = (from); \ - register long __cu_len __asm__("$0") = (n); \ - if (__access_ok(((long)(v)),__cu_len,__access_mask)) { \ - register void * __cu_ex __asm__("$8"); \ - __cu_ex = ¤t->tss.ex; \ - __asm__ __volatile__( \ - "jsr $28,(%7),__copy_user" \ - : "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to) \ - : "0" (__cu_len), "1" (__cu_from), "2" (__cu_to), \ - "r" (__cu_ex), "r" (__copy_user) \ - : "$1","$2","$3","$4","$5","$28","memory"); \ - } \ - __cu_len; \ -}) extern void __get_user_unknown(void); #define __get_user_nocheck(x,ptr,size) \ ({ \ - long __gu_err = -EFAULT, __gu_val = 0; \ - const __typeof__(*(ptr)) *__gu_addr = (ptr); \ - long __gu_ex_count = current->tss.ex.count; \ + long __gu_err = 0, __gu_val; \ switch (size) { \ - case 1: __get_user_8; break; \ - case 2: __get_user_16; break; \ - case 4: __get_user_32; break; \ - case 8: __get_user_64; break; \ - default: __get_user_unknown(); break; \ + case 1: __get_user_8(ptr); break; \ + case 2: __get_user_16(ptr); break; \ + case 4: __get_user_32(ptr); break; \ + case 8: __get_user_64(ptr); break; \ + default: __get_user_unknown(); break; \ } \ (x) = (__typeof__(*(ptr))) __gu_val; \ __gu_err; \ @@ -112,99 +100,110 @@ long __gu_err = -EFAULT, __gu_val = 0; \ const __typeof__(*(ptr)) *__gu_addr = (ptr); \ if (__access_ok((long)__gu_addr,size,mask)) { \ - long __gu_ex_count = current->tss.ex.count; \ + __gu_err = 0; \ switch (size) { \ - case 1: __get_user_8; break; \ - case 2: __get_user_16; break; \ - case 4: __get_user_32; break; \ - case 8: __get_user_64; break; \ - default: __get_user_unknown(); break; \ + case 1: __get_user_8(__gu_addr); break; \ + case 2: __get_user_16(__gu_addr); break; \ + case 4: __get_user_32(__gu_addr); break; \ + case 8: __get_user_64(__gu_addr); break; \ + default: __get_user_unknown(); break; \ } \ } \ (x) = (__typeof__(*(ptr))) __gu_val; \ __gu_err; \ }) -#define __get_user_64 \ - __asm__("/* Inline __get_user_64 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "br 2f\n" /* exception! */ \ - "1:\t" \ - "stq %5,%3\n\t" /* store inc'ed exception count */ \ - "ldq %1,%2\n\t" /* actual data load */ \ - "stq %4,%3\n\t" /* restore exception count */ \ - "clr %0\n" /* no exception: error = 0 */ \ - "2:\t/* End __get_user_64 */" \ - : "=r"(__gu_err), "=r"(__gu_val) \ - : "m"(*__gu_addr), "m"(current->tss.ex.count), \ - "r"(__gu_ex_count), "r"(__gu_ex_count+1), \ - "0"(__gu_err), "1"(__gu_val) \ - : "$28") - -#define __get_user_32 \ - __asm__("/* Inline __get_user_32 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "br 2f\n" /* exception! */ \ - "1:\t" \ - "stq %5,%3\n\t" /* store inc'ed exception count */ \ - "ldl %1,%2\n\t" /* actual data load */ \ - "stq %4,%3\n\t" /* restore exception count */ \ - "clr %0\n" /* no exception: error = 0 */ \ - "2:\t/* End __get_user_32 */" \ - : "=r"(__gu_err), "=r"(__gu_val) \ - : "m"(*__gu_addr), "m"(current->tss.ex.count), \ - "r"(__gu_ex_count), "r"(__gu_ex_count+1), \ - "0"(__gu_err), "1"(__gu_val) \ - : "$28") - -#define __get_user_16 \ - __asm__("/* Inline __get_user_16 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "br 2f\n" /* exception! */ \ - "1:\t" \ - "stq %6,%4\n\t" /* store inc'ed exception count */ \ - "ldq_u %1,%2\n\t" /* actual data load */ \ - "stq %5,%4\n\t" /* restore exception count */ \ - "clr %0\n\t" /* no exception: error = 0 */ \ - "extwl %1,%3,%1\n" /* extract the short */ \ - "2:\t/* End __get_user_16 */" \ - : "=r"(__gu_err), "=r"(__gu_val) \ - : "m"(*__gu_addr), "r"(__gu_addr), \ - "m"(current->tss.ex.count), "r"(__gu_ex_count), \ - "r"(__gu_ex_count+1), "0"(__gu_err), "1"(__gu_val) \ - : "$28") - -#define __get_user_8 \ - __asm__("/* Inline __get_user_8 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "br 2f\n" /* exception! */ \ - "1:\t" \ - "stq %6,%4\n\t" /* store inc'ed exception count */ \ - "ldq_u %1,%2\n\t" /* actual data load */ \ - "stq %5,%4\n\t" /* restore exception count */ \ - "clr %0\n\t" /* no exception: error = 0 */ \ - "extbl %1,%3,%1\n" /* extract the byte */ \ - "2:\t/* End __get_user_8 */" \ - : "=r"(__gu_err), "=r"(__gu_val) \ - : "m"(*__gu_addr), "r"(__gu_addr), \ - "m"(current->tss.ex.count), "r"(__gu_ex_count), \ - "r"(__gu_ex_count+1), "0"(__gu_err), "1"(__gu_val) \ - : "$28") +struct __large_struct { unsigned long buf[100]; }; +#define __m(x) (*(struct __large_struct *)(x)) + +#define __get_user_64(addr) \ + __asm__("1: ldq %0,%2\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda %0, 2b-1b(%1)\n" \ + ".text" \ + : "=r"(__gu_val), "=r"(__gu_err) \ + : "m"(__m(addr)), "1"(__gu_err)) + +#define __get_user_32(addr) \ + __asm__("1: ldl %0,%2\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda %0, 2b-1b(%1)\n" \ + ".text" \ + : "=r"(__gu_val), "=r"(__gu_err) \ + : "m"(__m(addr)), "1"(__gu_err)) + +#ifdef __HAVE_CPU_BWX +/* Those lucky bastards with ev56 and later cpus can do byte/word moves. */ + +#define __get_user_16(addr) \ + __asm__("1: ldwu %0,%2\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda %0, 2b-1b(%1)\n" \ + ".text" \ + : "=r"(__gu_val), "=r"(__gu_err) \ + : "m"(__m(addr)), "1"(__gu_err)) + +#define __get_user_8(addr) \ + __asm__("1: ldbu %0,%2\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda %0, 2b-1b(%1)\n" \ + ".text" \ + : "=r"(__gu_val), "=r"(__gu_err) \ + : "m"(__m(addr)), "1"(__gu_err)) +#else +/* Unfortunately, we can't get an unaligned access trap for the sub-word + load, so we have to do a general unaligned operation. */ + +#define __get_user_16(addr) \ +{ \ + long __gu_tmp; \ + __asm__("1: ldq_u %0,0(%3)\n" \ + "2: ldq_u %1,1(%3)\n" \ + " extwl %0,%3,%0\n" \ + " extwh %1,%3,%1\n" \ + " or %0,%1,%0\n" \ + "3:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda %0, 3b-1b(%2)\n" \ + " .gprel32 2b\n" \ + " lda %0, 2b-1b(%2)\n" \ + ".text" \ + : "=&r"(__gu_val), "=&r"(__gu_tmp), "=r"(__gu_err) \ + : "r"(addr), "2"(__gu_err)); \ +} + +#define __get_user_8(addr) \ + __asm__("1: ldq_u %0,0(%2)\n" \ + " extbl %0,%2,%0\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda %0, 2b-1b(%1)\n" \ + ".text" \ + : "=&r"(__gu_val), "=r"(__gu_err) \ + : "r"(addr), "1"(__gu_err)) +#endif extern void __put_user_unknown(void); #define __put_user_nocheck(x,ptr,size) \ ({ \ - long __pu_err = -EFAULT; \ - __typeof__(*(ptr)) *__pu_addr = (ptr); \ - __typeof__(*(ptr)) __pu_val = (x); \ - long __pu_ex_count = current->tss.ex.count; \ + long __pu_err = 0; \ switch (size) { \ - case 1: __put_user_8; break; \ - case 2: __put_user_16; break; \ - case 4: __put_user_32; break; \ - case 8: __put_user_64; break; \ - default: __put_user_unknown(); break; \ + case 1: __put_user_8(x,ptr); break; \ + case 2: __put_user_16(x,ptr); break; \ + case 4: __put_user_32(x,ptr); break; \ + case 8: __put_user_64(x,ptr); break; \ + default: __put_user_unknown(); break; \ } \ __pu_err; \ }) @@ -213,95 +212,149 @@ ({ \ long __pu_err = -EFAULT; \ __typeof__(*(ptr)) *__pu_addr = (ptr); \ - __typeof__(*(ptr)) __pu_val = (x); \ if (__access_ok((long)__pu_addr,size,mask)) { \ - long __pu_ex_count = current->tss.ex.count; \ + __pu_err = 0; \ switch (size) { \ - case 1: __put_user_8; break; \ - case 2: __put_user_16; break; \ - case 4: __put_user_32; break; \ - case 8: __put_user_64; break; \ - default: __put_user_unknown(); break; \ + case 1: __put_user_8(x,__pu_addr); break; \ + case 2: __put_user_16(x,__pu_addr); break; \ + case 4: __put_user_32(x,__pu_addr); break; \ + case 8: __put_user_64(x,__pu_addr); break; \ + default: __put_user_unknown(); break; \ } \ } \ __pu_err; \ }) -#define __put_user_64 \ - __asm__("/* Inline __put_user_64 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "br 2f\n" /* exception! */ \ - "1:\t" \ - "stq %5,%3\n\t" /* store inc'ed exception count */ \ - "stq %2,%1\n\t" /* actual data store */ \ - "stq %4,%3\n\t" /* restore exception count */ \ - "clr %0\n" /* no exception: error = 0 */ \ - "2:\t/* End __put_user_64 */" \ - : "=r"(__pu_err), "=m"(*__pu_addr) \ - : "r"(__pu_val), "m"(current->tss.ex.count), \ - "r"(__pu_ex_count), "r"(__pu_ex_count+1), \ - "0"(__pu_err) \ - : "$28") - -#define __put_user_32 \ - __asm__("/* Inline __put_user_32 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "br 2f\n" /* exception! */ \ - "1:\t" \ - "stq %5,%3\n\t" /* store inc'ed exception count */ \ - "stl %2,%1\n\t" /* actual data store */ \ - "stq %4,%3\n\t" /* restore exception count */ \ - "clr %0\n" /* no exception: error = 0 */ \ - "2:\t/* End __put_user_32 */" \ - : "=r"(__pu_err), "=m"(*__pu_addr) \ - : "r"(__pu_val), "m"(current->tss.ex.count), \ - "r"(__pu_ex_count), "r"(__pu_ex_count+1), \ - "0"(__pu_err) \ - : "$28") - -#define __put_user_16 \ - __asm__("/* Inline __put_user_16 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "lda %0,%7\n\t" /* exception! error = -EFAULT */ \ - "br 2f\n" \ - "1:\t" \ - "stq %6,%4\n\t" /* store inc'ed exception count */ \ - "ldq_u %0,%1\n\t" /* masked data store */ \ - "inswl %2,%3,%2\n\t" \ - "mskwl %0,%3,%0\n\t" \ - "or %0,%2,%2\n\t" \ - "stq_u %2,%1\n\t" \ - "stq %5,%4\n\t" /* restore exception count */ \ - "clr %0\n" /* no exception: error = 0 */ \ - "2:\t/* End __put_user_16 */" \ - : "=r"(__pu_err), "=m"(*__pu_addr), "=r"(__pu_val) \ - : "r"(__pu_addr), "m"(current->tss.ex.count), \ - "r"(__pu_ex_count), "r"(__pu_ex_count+1), "i"(-EFAULT), \ - "2"(__pu_val) \ - : "$28") - -#define __put_user_8 \ - __asm__("/* Inline __put_user_8 */\n\t" \ - "br $28,1f\n\t" /* set up exception address */ \ - "lda %0,%7\n\t" /* exception! error = -EFAULT */ \ - "br 2f\n" \ - "1:\t" \ - "stq %6,%4\n\t" /* store inc'ed exception count */ \ - "ldq_u %0,%1\n\t" /* masked data store */ \ - "insbl %2,%3,%2\n\t" \ - "mskbl %0,%3,%0\n\t" \ - "or %0,%2,%2\n\t" \ - "stq_u %2,%1\n\t" \ - "stq %5,%4\n\t" /* restore exception count */ \ - "clr %0\n" /* no exception: error = 0 */ \ - "2:\t/* End __put_user_8 */" \ - : "=r"(__pu_err), "=m"(*__pu_addr), "=r"(__pu_val) \ - : "r"(__pu_addr), "m"(current->tss.ex.count), \ - "r"(__pu_ex_count), "r"(__pu_ex_count+1), "i"(-EFAULT), \ - "2"(__pu_val) \ - : "$28") +/* + * The "__put_user_xx()" macros tell gcc they read from memory + * instead of writing: this is because they do not write to + * any memory gcc knows about, so there are no aliasing issues + */ +#define __put_user_64(x,addr) \ +__asm__ __volatile__("1: stq %r2,%1\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda $31,2b-1b(%0)\n" \ + ".text" \ + : "=r"(__pu_err) \ + : "m" (__m(addr)), "rJ" (x), "0"(__pu_err)) + +#define __put_user_32(x,addr) \ +__asm__ __volatile__("1: stl %r2,%1\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda $31,2b-1b(%0)\n" \ + ".text" \ + : "=r"(__pu_err) \ + : "m"(__m(addr)), "rJ"(x), "0"(__pu_err)) + +#ifdef __HAVE_CPU_BWX +/* Those lucky bastards with ev56 and later cpus can do byte/word moves. */ + +#define __put_user_16(x,addr) \ +__asm__ __volatile__("1: stw %r2,%1\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda $31,2b-1b(%0)\n" \ + ".text" \ + : "=r"(__pu_err) \ + : "m"(__m(addr)), "rJ"(x), "0"(__pu_err)) + +#define __put_user_8(x,addr) \ +__asm__ __volatile__("1: stb %r2,%1\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda $31,2b-1b(%0)\n" \ + ".text" \ + : "=r"(__pu_err) \ + : "m"(__m(addr)), "rJ"(x), "0"(__pu_err)) +#else +/* Unfortunately, we can't get an unaligned access trap for the sub-word + write, so we have to do a general unaligned operation. */ + +#define __put_user_16(x,addr) \ +{ \ + long __pu_tmp1, __pu_tmp2, __pu_tmp3, __pu_tmp4; \ + __asm__ __volatile__( \ + "1: ldq_u %2,1(%5)\n" \ + "2: ldq_u %1,0(%5)\n" \ + " inswh %6,%5,%4\n" \ + " inswl %6,%5,%3\n" \ + " mskwh %2,%5,%2\n" \ + " mskwl %1,%5,%1\n" \ + " or %2,%4,%2\n" \ + " or %1,%3,%1\n" \ + "3: stq_u %2,1(%5)\n" \ + "4: stq_u %1,0(%5)\n" \ + "5:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda $31, 5b-1b(%0)\n" \ + " .gprel32 2b\n" \ + " lda $31, 5b-2b(%0)\n" \ + " .gprel32 3b\n" \ + " lda $31, 5b-3b(%0)\n" \ + " .gprel32 4b\n" \ + " lda $31, 5b-4b(%0)\n" \ + ".text" \ + : "=r"(__pu_err), "=&r"(__pu_tmp1), \ + "=&r"(__pu_tmp2), "=&r"(__pu_tmp3), \ + "=&r"(__pu_tmp4) \ + : "r"(addr), "r"((unsigned long)(x)), "0"(__pu_err)); \ +} + +#define __put_user_8(x,addr) \ +{ \ + long __pu_tmp1, __pu_tmp2; \ + __asm__ __volatile__( \ + "1: ldq_u %1,0(%4)\n" \ + " insbl %3,%4,%2\n" \ + " mskbl %1,%4,%1\n" \ + " or %1,%2,%1\n" \ + "2: stq_u %1,0(%4)\n" \ + "3:\n" \ + ".section __ex_table,\"a\"\n" \ + " .gprel32 1b\n" \ + " lda $31, 3b-1b(%0)\n" \ + " .gprel32 2b\n" \ + " lda $31, 3b-2b(%0)\n" \ + ".text" \ + : "=r"(__pu_err), \ + "=&r"(__pu_tmp1), "=&r"(__pu_tmp2) \ + : "r"((unsigned long)(x)), "r"(addr), "0"(__pu_err)); \ +} +#endif +/* + * Complex access routines + */ + +#define copy_to_user(to,from,n) __copy_tofrom_user((to),(from),(n),__cu_to) +#define copy_from_user(to,from,n) __copy_tofrom_user((to),(from),(n),__cu_from) + +extern void __copy_user(void); + +#define __copy_tofrom_user(to,from,n,v) \ +({ \ + register void * __cu_to __asm__("$6") = (to); \ + register const void * __cu_from __asm__("$7") = (from); \ + register long __cu_len __asm__("$0") = (n); \ + if (__access_ok(((long)(v)),__cu_len,__access_mask)) { \ + __asm__ __volatile__( \ + "jsr $28,(%3),__copy_user" \ + : "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to) \ + : "r" (__copy_user), "0" (__cu_len), \ + "1" (__cu_from), "2" (__cu_to) \ + : "$1","$2","$3","$4","$5","$28","memory"); \ + } \ + __cu_len; \ +}) + extern void __clear_user(void); #define clear_user(to,n) \ @@ -309,13 +362,10 @@ register void * __cl_to __asm__("$6") = (to); \ register long __cl_len __asm__("$0") = (n); \ if (__access_ok(((long)__cl_to),__cl_len,__access_mask)) { \ - register void * __cl_ex __asm__("$7"); \ - __cl_ex = ¤t->tss.ex; \ __asm__ __volatile__( \ "jsr $28,(%2),__clear_user" \ : "=r"(__cl_len), "=r"(__cl_to) \ - : "r"(__clear_user), "r"(__cl_ex), \ - "0"(__cl_len), "1"(__cl_to) \ + : "r"(__clear_user), "0"(__cl_len), "1"(__cl_to)\ : "$1","$2","$3","$4","$5","$28","memory"); \ } \ __cl_len; \ @@ -325,24 +375,66 @@ /* Returns: -EFAULT if exception before terminator, N if the entire buffer filled, else strlen. */ -struct exception_struct; -extern long __strncpy_from_user(char *__to, const char *__from, - long __to_len, struct exception_struct *); +extern long __strncpy_from_user(char *__to, const char *__from, long __to_len); #define strncpy_from_user(to,from,n) \ ({ \ char * __sfu_to = (to); \ const char * __sfu_from = (from); \ long __sfu_len = (n), __sfu_ret = -EFAULT; \ - if (__access_ok(((long)__sfu_from),__sfu_len,__access_mask)) { \ - __sfu_ret = __strncpy_from_user(__sfu_to,__sfu_from, \ - __sfu_len, ¤t->tss.ex); \ + if (__access_ok(((long)__sfu_from),__sfu_len,__access_mask)) \ + __sfu_ret=__strncpy_from_user(__sfu_to,__sfu_from,__sfu_len); \ __sfu_ret; \ }) -extern inline int verify_area(int type, const void * addr, unsigned long size) + +/* + * About the exception table: + * + * - insn is a 32-bit offset off of the kernel's or module's gp. + * - nextinsn is a 16-bit offset off of the faulting instruction + * (not off of the *next* instruction as branches are). + * - errreg is the register in which to place -EFAULT. + * - valreg is the final target register for the load sequence + * and will be zeroed. + * + * Either errreg or valreg may be $31, in which case nothing happens. + * + * The exception fixup information "just so happens" to be arranged + * as in a MEM format instruction. This lets us emit our three + * values like so: + * + * lda valreg, nextinsn(errreg) + * + */ + +struct exception_table_entry { - return access_ok(type,addr,size)?0:-EFAULT; -} + signed int insn; + union exception_fixup { + unsigned unit; + struct { + signed int nextinsn : 16; + unsigned int errreg : 5; + unsigned int valreg : 5; + } bits; + } fixup; +}; + +/* Returns 0 if exception not found and fixup.unit otherwise. */ +extern unsigned search_exception_table(unsigned long); + +/* Returns the new pc */ +#define fixup_exception(map_reg, fixup_unit, pc) \ +({ \ + union exception_fixup __fie_fixup; \ + __fie_fixup.unit = fixup_unit; \ + if (__fie_fixup.bits.valreg != 31) \ + map_reg(__fie_fixup.bits.valreg) = 0; \ + if (__fie_fixup.bits.errreg != 31) \ + map_reg(__fie_fixup.bits.errreg) = -EFAULT; \ + (pc) + __fie_fixup.bits.nextinsn; \ +}) + -#endif /* _ASM_SEGMENT_H */ +#endif /* __ALPHA_UACCESS_H */ diff -u --recursive --new-file v2.1.6/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h --- v2.1.6/linux/include/asm-i386/processor.h Wed Oct 16 10:48:29 1996 +++ linux/include/asm-i386/processor.h Fri Nov 1 11:56:43 1996 @@ -41,16 +41,6 @@ #define TASK_SIZE (0xC0000000UL) /* - * VM exception register save area.. - * - * When no exceptions are active, count = -1. - */ -struct exception_struct { - unsigned long count; - unsigned long eip; -}; - -/* * Size of io_bitmap in longwords: 32 is ports 0-0x3ff. */ #define IO_BITMAP_SIZE 32 @@ -120,7 +110,6 @@ struct vm86_struct * vm86_info; unsigned long screen_bitmap; unsigned long v86flags, v86mask, v86mode; - struct exception_struct ex; }; #define INIT_MMAP { &init_mm, 0xC0000000, 0xFFFFF000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC } @@ -139,7 +128,6 @@ _TSS(0), 0, 0, 0, KERNEL_DS, \ { { 0, }, }, /* 387 state */ \ NULL, 0, 0, 0, 0 /* vm86_info */, \ - { -1, } \ } #define alloc_kernel_stack() __get_free_page(GFP_KERNEL) diff -u --recursive --new-file v2.1.6/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h --- v2.1.6/linux/include/asm-i386/uaccess.h Tue Oct 29 19:58:47 1996 +++ linux/include/asm-i386/uaccess.h Fri Nov 1 15:25:03 1996 @@ -5,7 +5,6 @@ * User space memory access functions */ #include - #include #define VERIFY_READ 0 @@ -15,37 +14,65 @@ * The fs value determines whether argument validity checking should be * performed or not. If get_fs() == USER_DS, checking is performed, with * get_fs() == KERNEL_DS, checking is bypassed. - * + * * For historical reasons, these macros are grossly misnamed. */ - + #define get_fs() (current->tss.segment) #define set_fs(x) (current->tss.segment = (x)) #define get_ds() (KERNEL_DS) #define __user_ok(addr,size) \ -((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size)) + ((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size)) #define __kernel_ok \ -(get_fs() == KERNEL_DS) + (get_fs() == KERNEL_DS) extern int __verify_write(const void *, unsigned long); #if CPU > 386 #define __access_ok(type,addr,size) \ -(__kernel_ok || __user_ok(addr,size)) + (__kernel_ok || __user_ok(addr,size)) #else #define __access_ok(type,addr,size) \ -(__kernel_ok || (__user_ok(addr,size) && \ - ((type) == VERIFY_READ || wp_works_ok || __verify_write((void *)(addr),(size))))) + (__kernel_ok || (__user_ok(addr,size) && \ + ((type) == VERIFY_READ || wp_works_ok || \ + __verify_write((void *)(addr),(size))))) #endif /* CPU */ #define access_ok(type,addr,size) \ -__access_ok((type),(unsigned long)(addr),(size)) + __access_ok((type),(unsigned long)(addr),(size)) + +extern inline int verify_area(int type, const void * addr, unsigned long size) +{ + return access_ok(type,addr,size) ? 0 : -EFAULT; +} + + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + /* - * Uh, these should become the main single-value transfer routines.. - * They automatically use the right size if we just have the right - * pointer type.. + * These are the main single-value transfer routines. They automatically + * use the right size if we just have the right pointer type. * * This gets kind of ugly. We want to return _two_ values in "get_user()" * and yet we don't want to do any pointers, because that is too much @@ -57,280 +84,328 @@ * with a separate "access_ok()" call (this is used when we do multiple * accesses to the same area of user memory). */ +#define get_user(x,ptr) \ + __get_user_check((x),(ptr),sizeof(*(ptr))) #define put_user(x,ptr) \ -__do_put_user((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr)))) -#define __put_user(x,ptr) \ -__do_put_user_nocheck((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr)))) + __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) -struct __large_struct { unsigned long buf[100]; }; -#define __m(x) (*(struct __large_struct *)(x)) +#define __get_user(x,ptr) \ + __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x,ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) -#define __put_user_asm(x,addr,ret,bwl,reg,rtype) \ -__asm__ __volatile__( \ - "movl $1f,%0\n\t" \ - "incl %3\n\t" \ - "mov" #bwl " %" reg "1,%2\n\t" \ - "xorl %0,%0\n\t" \ - "decl %3\n1:" \ -:"=&d" (ret) \ -:#rtype (x), "m" (__m(addr)),"m" (current->tss.ex.count)) - -extern int __put_user_bad(void); - -#define __put_user_size(x,ptr,size,retval) \ -switch (size) { \ -case 1: __put_user_asm(x,ptr,retval,b,"b","iq"); break; \ -case 2: __put_user_asm(x,ptr,retval,w,"w","ir"); break; \ -case 4: __put_user_asm(x,ptr,retval,l,"","ir"); break; \ -default: retval = __put_user_bad(); } -static inline int __do_put_user(unsigned long x, void * ptr, int size) -{ - int retval = -EFAULT; - if (access_ok(VERIFY_WRITE, ptr, size)) - __put_user_size(x,ptr,size,retval); - return retval; -} +extern long __put_user_bad(void); -#define __do_put_user_nocheck(x, ptr, size) \ -({ int retval; __put_user_size(x,ptr,size,retval); retval; }) +#define __put_user_nocheck(x,ptr,size) \ +({ \ + long __pu_err; \ + __put_user_size((x),(ptr),(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_check(x,ptr,size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) *__pu_addr = (ptr); \ + if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ + __put_user_size((x),__pu_addr,(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __put_user_asm(x,ptr,retval,"b","b","iq"); break; \ + case 2: __put_user_asm(x,ptr,retval,"w","w","ir"); break; \ + case 4: __put_user_asm(x,ptr,retval,"l","","ir"); break; \ + default: __put_user_bad(); \ + } \ +} while (0) -#define get_user(x,ptr) \ -__do_get_user((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr))) +struct __large_struct { unsigned long buf[100]; }; +#define __m(x) (*(struct __large_struct *)(x)) -#define __get_user(x,ptr) \ -__do_get_user_nocheck((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr))) +/* + * Tell gcc we read from memory instead of writing: this is because + * we do not write to any memory gcc knows about, so there are no + * aliasing issues. + */ +#define __put_user_asm(x, addr, err, itype, rtype, ltype) \ + __asm__ __volatile__( \ + "1: mov"itype" %"rtype"1,%2\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movl %3,%0\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(err) \ + : ltype (x), "m"(__m(addr)), "i"(-EFAULT), "0"(err)) + + +#define __get_user_nocheck(x,ptr,size) \ +({ \ + long __gu_err, __gu_val; \ + __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +#define __get_user_check(x,ptr,size) \ +({ \ + long __gu_err = -EFAULT, __gu_val = 0; \ + const __typeof__(*(ptr)) *__gu_addr = (ptr); \ + if (access_ok(VERIFY_READ,__gu_addr,size)) \ + __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +extern long __get_user_bad(void); + +#define __get_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + switch (size) { \ + case 1: __get_user_asm(x,ptr,retval,"b","b","=q"); break; \ + case 2: __get_user_asm(x,ptr,retval,"w","w","=r"); break; \ + case 4: __get_user_asm(x,ptr,retval,"l","","=r"); break; \ + default: (x) = __get_user_bad(); \ + } \ +} while (0) + +#define __get_user_asm(x, addr, err, itype, rtype, ltype) \ + __asm__ __volatile__( \ + "1: mov"itype" %2,%"rtype"1\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movl %3,%0\n" \ + " xor"itype" %"rtype"1,%"rtype"1\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(err), ltype (x) \ + : "m"(__m(addr)), "i"(-EFAULT), "0"(err)) -#define __do_get_user(x,ptr,size,type) ({ \ -unsigned long __gu_addr = ptr; \ -int __gu_ret = -EFAULT; \ -unsigned long __gu_val = 0; \ -if (access_ok(VERIFY_READ,__gu_addr,size)) { \ -switch (size) { \ -case 1: __do_get_user_8(__gu_val,__gu_addr,__gu_ret); break; \ -case 2: __do_get_user_16(__gu_val,__gu_addr,__gu_ret); break; \ -case 4: __do_get_user_32(__gu_val,__gu_addr,__gu_ret); break; \ -default: __gu_ret = __do_get_user_bad(); break; \ -} } x = (type) __gu_val; __gu_ret; }) - -#define __do_get_user_nocheck(x,ptr,size,type) ({ \ -int __gu_ret; \ -unsigned long __gu_val; \ -switch (size) { \ -case 1: __do_get_user_8(__gu_val,ptr,__gu_ret); break; \ -case 2: __do_get_user_16(__gu_val,ptr,__gu_ret); break; \ -case 4: __do_get_user_32(__gu_val,ptr,__gu_ret); break; \ -default: __gu_ret = __do_get_user_bad(); __gu_val = 0; break; \ -} x = (type) __gu_val; __gu_ret; }) - -#define __do_get_user_asm(x,addr,ret,bwl,reg,rtype) \ -__asm__ __volatile__( \ - "movl $1f,%0\n\t" \ - "incl %3\n\t" \ - "mov" #bwl " %2,%" reg "1\n\t" \ - "xorl %0,%0\n\t" \ - "decl %3\n1:" \ -:"=&d" (ret), #rtype (x) \ -:"m" (__m(addr)),"m" (current->tss.ex.count)) - -#define __do_get_user_8(x,addr,ret) \ -__do_get_user_asm(x,addr,ret,b,"b","=&q") -#define __do_get_user_16(x,addr,ret) \ -__do_get_user_asm(x,addr,ret,w,"w","=&r") -#define __do_get_user_32(x,addr,ret) \ -__do_get_user_asm(x,addr,ret,l,"","=&r") - -extern int __do_get_user_bad(void); - -#define __copy_user(to,from,size) \ -__asm__ __volatile__( \ - "shrl $2,%1\n\t" \ - "movl $3f,%0\n\t" \ - "incl %3\n\t" \ - "rep; movsl\n\t" \ - "testl $2,%2\n\t" \ - "je 1f\n\t" \ - "movsw\n\t" \ - "subl $2,%2\n" \ - "1:\t" \ - "testl $1,%2\n\t" \ - "je 2f\n\t" \ - "movsb\n\t" \ - "decl %2\n" \ - "2:\t" \ - "decl %3\n" \ - "3:\tlea 0(%2,%1,4),%0" \ - :"=&d" (size) \ - :"c" (size), "r" (size & 3), "m" (current->tss.ex), \ - "D" (to), "S" (from) \ - :"cx","di","si","memory"); -static inline unsigned long __constant_copy_user(void * to, const void * from, unsigned long size) -{ - unsigned long result; +/* + * Copy To/From Userspace + */ - switch (size & 3) { - default: - __asm__ __volatile__( - "movl $1f,%0\n\t" - "incl %1\n\t" - "rep ; movsl\n\t" - "decl %1\n" - "1:\tlea 0(,%%ecx,4),%0" - :"=&d" (result) - :"m" (current->tss.ex), - "S" (from),"D" (to),"c" (size/4) - :"cx","di","si","memory"); - break; - case 1: - __asm__ __volatile__( - "movl $1f,%0\n\t" - "incl %3\n\t" - "rep ; movsl\n\t" - "movsb\n\t" - "decl %1\n\t" - "decl %3\n" - "1:\tlea 0(%1,%%ecx,4),%0" - :"=&d" (result) - :"ab" (1),"m" (current->tss.ex), - "S" (from),"D" (to), "c" (size/4) - :"cx","di","si","memory"); - break; - case 2: - __asm__ __volatile__( - "movl $1f,%0\n\t" - "incl %2\n\t" - "rep ; movsl\n\t" - "movsw\n\t" - "subl $2,%1\n\t" - "decl %2\n" - "1:\tlea 0(%1,%%ecx,4),%0" - :"=&d" (result) - :"ab" (2),"m" (current->tss.ex), - "S" (from),"D" (to),"c" (size/4) - :"cx","di","si","memory"); - break; - case 3: - __asm__ __volatile__( - "movl $1f,%0\n\t" - "incl %2\n\t" - "rep ; movsl\n\t" - "movsw\n\t" - "subl $2,%1\n\t" - "movsb\n\t" - "decl %1\n\t" - "decl %2\n" - "1:\tlea 0(%1,%%ecx,4),%0" - :"=&d" (result) - :"ab" (3),"m" (current->tss.ex), - "S" (from),"D" (to),"c" (size/4) - :"cx","di","si","memory"); - break; - } - return result; -} +/* Generic arbitrary sized copy. */ +#define __copy_user(to,from,size) \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + " movl %1,%0\n" \ + "1: rep; movsb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%1,%0,4),%0\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,2b\n" \ + ".text" \ + : "=c"(size) \ + : "r"(size & 3), "0"(size / 4), "D"(to), "S"(from) \ + : "di", "si", "memory") + +/* Optimize just a little bit when we know the size of the move. */ +#define __constant_copy_user(to, from, size) \ +do { \ + switch (size & 3) { \ + default: \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + "1:\n" \ + ".section .fixup,\"ax\"\n" \ + "2: shl $2,%0\n" \ + " jmp 1b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,2b\n" \ + ".text" \ + : "=c"(size) \ + : "S"(from), "D"(to), "0"(size/4) \ + : "di", "si", "memory"); \ + break; \ + case 1: \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + "1: movsb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: shl $2,%0\n" \ + "4: incl %0\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,4b\n" \ + ".text" \ + : "=c"(size) \ + : "S"(from), "D"(to), "0"(size/4) \ + : "di", "si", "memory"); \ + break; \ + case 2: \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + "1: movsw\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: shl $2,%0\n" \ + "4: addl $2,%0\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,4b\n" \ + ".text" \ + : "=c"(size) \ + : "S"(from), "D"(to), "0"(size/4) \ + : "di", "si", "memory"); \ + break; \ + case 3: \ + __asm__ __volatile__( \ + "0: rep; movsl\n" \ + "1: movsw\n" \ + "2: movsb\n" \ + "3:\n" \ + ".section .fixup,\"ax\"\n" \ + "4: shl $2,%0\n" \ + "5: addl $2,%0\n" \ + "6: incl %0\n" \ + " jmp 3b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,4b\n" \ + " .long 1b,5b\n" \ + " .long 2b,6b\n" \ + ".text" \ + : "=c"(size) \ + : "S"(from), "D"(to), "0"(size/4) \ + : "di", "si", "memory"); \ + break; \ + } \ +} while (0) -static inline unsigned long __generic_copy_to_user(void *to, const void *from, unsigned long n) +static inline unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) __copy_user(to,from,n); return n; } -static inline unsigned long __constant_copy_to_user(void *to, const void *from, unsigned long n) +static inline unsigned long +__constant_copy_to_user(void *to, const void *from, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) - n = __constant_copy_user(to,from,n); + __constant_copy_user(to,from,n); return n; } -static inline unsigned long __generic_copy_from_user(void *to, const void *from, unsigned long n) +static inline unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) { if (access_ok(VERIFY_READ, from, n)) __copy_user(to,from,n); return n; } -static inline unsigned long __constant_copy_from_user(void *to, const void *from, unsigned long n) +static inline unsigned long +__constant_copy_from_user(void *to, const void *from, unsigned long n) { if (access_ok(VERIFY_READ, from, n)) - n = __constant_copy_user(to,from,n); + __constant_copy_user(to,from,n); return n; } -#define copy_to_user(to,from,n) \ -(__builtin_constant_p(n) ? \ - __constant_copy_to_user((to),(from),(n)) : \ - __generic_copy_to_user((to),(from),(n))) - -#define copy_from_user(to,from,n) \ -(__builtin_constant_p(n) ? \ - __constant_copy_from_user((to),(from),(n)) : \ - __generic_copy_from_user((to),(from),(n))) - -#define __clear_user(addr,size) \ -__asm__ __volatile__( \ - "movl $3f,%0\n\t" \ - "incl %2\n\t" \ - "rep; stosl\n\t" \ - "testl $2,%3\n\t" \ - "je 1f\n\t" \ - "stosw\n\t" \ - "subl $2,%3\n" \ - "1:\t" \ - "testl $1,%3\n\t" \ - "je 2f\n\t" \ - "stosb\n\t" \ - "decl %3\n" \ - "2:\t" \ - "decl %2\n" \ - "3:\tlea 0(%3,%1,4),%0" \ - :"=&d" (size) \ - :"c" (size >> 2), "m" (current->tss.ex), "r" (size & 3), \ - "D" (addr), "a" (0) \ - :"cx","di","memory"); - -#define clear_user(addr,n) ({ \ -void * __cl_addr = (addr); \ -unsigned long __cl_size = (n); \ -if (__cl_size && __access_ok(VERIFY_WRITE, ((unsigned long)(__cl_addr)), __cl_size)) \ -__clear_user(__cl_addr, __cl_size); \ -__cl_size; }) - -#define __strncpy_from_user(dst,src,count,res) \ -__asm__ __volatile__( \ - "cld\n\t" \ - "movl $3f,%0\n\t" \ - "incl %2\n" \ - "1:\tdecl %1\n\t" \ - "js 2f\n\t" \ - "lodsb\n\t" \ - "stosb\n\t" \ - "testb %%al,%%al\n\t" \ - "jne 1b\n" \ - "2:\t" \ - "incl %1\n\t" \ - "xorl %0,%0\n\t" \ - "decl %2\n" \ - "3:" \ - :"=&d" (res), "=r" (count) \ - :"m" (current->tss.ex), "1" (count), "S" (src),"D" (dst) \ - :"si","di","ax","memory") - -#define strncpy_from_user(dest,src,count) ({ \ -const void * __sc_src = (src); \ -unsigned long __sc_count = (count); \ -long __sc_res = -EFAULT; \ -if (__access_ok(VERIFY_READ, ((unsigned long)(__sc_src)), __sc_count)) { \ - unsigned long __sc_residue = __sc_count; \ - __strncpy_from_user(dest,__sc_src,__sc_count,__sc_res); \ - if (!__sc_res) __sc_res = __sc_residue - __sc_count; \ -} __sc_res; }) +#define copy_to_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_to_user((to),(from),(n)) : \ + __generic_copy_to_user((to),(from),(n))) + +#define copy_from_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_from_user((to),(from),(n)) : \ + __generic_copy_from_user((to),(from),(n))) -extern inline int verify_area(int type, const void * addr, unsigned long size) + +/* + * Zero Userspace + */ + +#define __clear_user(addr,size) \ + __asm__ __volatile__( \ + "0: rep; stosl\n" \ + " movl %1,%0\n" \ + "1: rep; stosb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%1,%0,4),%0\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,2b\n" \ + ".text" \ + : "=c"(size) \ + : "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0) \ + : "di") + +static inline unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __clear_user(to, n); + return n; +} + + +/* + * Copy a null terminated string from userspace. + */ + +#define __strncpy_from_user(dst,src,count,res) \ + __asm__ __volatile__( \ + " testl %1,%1\n" \ + " jz 2f\n" \ + "0: lodsb\n" \ + " stosb\n" \ + " testb %%al,%%al\n" \ + " jz 1f\n" \ + " decl %1\n" \ + " jnz 0b\n" \ + "1: subl %1,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movl %2,%0\n" \ + " jmp 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + ".text" \ + : "=r"(res), "=r"(count) \ + : "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \ + : "si", "di", "ax", "memory") + +static inline long +strncpy_from_user(char *dst, const char *src, long count) { - return access_ok(type,addr,size)?0:-EFAULT; + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __strncpy_from_user(dst, src, count, res); + return res; } + #endif /* __i386_UACCESS_H */ diff -u --recursive --new-file v2.1.6/linux/include/linux/baycom.h linux/include/linux/baycom.h --- v2.1.6/linux/include/linux/baycom.h Mon Jul 8 10:21:46 1996 +++ linux/include/linux/baycom.h Wed Oct 30 14:22:22 1996 @@ -7,61 +7,38 @@ #ifndef _BAYCOM_H #define _BAYCOM_H -#include -#undef BAYCOM_DEBUG +#include +#include /* -------------------------------------------------------------------- */ +/* + * structs for the IOCTL commands + */ -struct baycom_statistics { - unsigned long rx_packets, tx_packets; - unsigned long ptt_keyed; - unsigned long rx_bufferoverrun, tx_bufferoverrun; +struct baycom_debug_data { + unsigned long debug1; + unsigned long debug2; + long debug3; }; -struct baycom_params { - int modem_type; - int iobase; - int irq; - int options; - int tx_delay; /* the transmitter keyup delay in 10ms units */ - int tx_tail; /* the transmitter keyoff delay in 10ms units */ - int slottime; /* the slottime in 10ms; usually 10 = 100ms */ - int ppersist; /* the p-persistence 0..255 */ - int fulldup; /* the driver does not support full duplex, setting */ - /* this just makes the driver send even if DCD is on */ -}; - -/* -------------------------------------------------------------------- */ - -#define BAYCOM_MAJOR 51 - -/* maximum packet length, excluding CRC */ -#define BAYCOM_MAXFLEN 400 - -/* the ioctl type of this driver */ -#define BAYCOM_IOCTL_TYPE 'B' +struct baycom_modem_type { + unsigned char modem_type; + unsigned int options; +}; -#define KISS_FEND ((unsigned char)0300) -#define KISS_FESC ((unsigned char)0333) -#define KISS_TFEND ((unsigned char)0334) -#define KISS_TFESC ((unsigned char)0335) - -#define KISS_CMD_DATA 0 -#define KISS_CMD_TXDELAY 1 -#define KISS_CMD_PPERSIST 2 -#define KISS_CMD_SLOTTIME 3 -#define KISS_CMD_TXTAIL 4 -#define KISS_CMD_FULLDUP 5 +struct baycom_ioctl { + int cmd; + union { + struct baycom_modem_type mt; + struct baycom_debug_data dbg; + } data; +}; -/* - * use bottom halves? (HDLC processing done with interrupts on or off) - */ -#define BAYCOM_USE_BH +/* -------------------------------------------------------------------- */ /* * modem types */ - #define BAYCOM_MODEM_INVALID 0 #define BAYCOM_MODEM_SER12 1 #define BAYCOM_MODEM_PAR96 2 @@ -71,59 +48,15 @@ */ #define BAYCOM_OPTIONS_SOFTDCD 1 - /* - * ioctl constants + * ioctl values change for baycom_net */ -#define BAYCOMCTL_GETDCD _IOR(BAYCOM_IOCTL_TYPE, 0, unsigned char) -#define BAYCOMCTL_GETPTT _IOR(BAYCOM_IOCTL_TYPE, 1, unsigned char) -#define BAYCOMCTL_PARAM_TXDELAY _IO(BAYCOM_IOCTL_TYPE, 2) -#define BAYCOMCTL_PARAM_PPERSIST _IO(BAYCOM_IOCTL_TYPE, 3) -#define BAYCOMCTL_PARAM_SLOTTIME _IO(BAYCOM_IOCTL_TYPE, 4) -#define BAYCOMCTL_PARAM_TXTAIL _IO(BAYCOM_IOCTL_TYPE, 5) -#define BAYCOMCTL_PARAM_FULLDUP _IO(BAYCOM_IOCTL_TYPE, 6) - -#define BAYCOMCTL_GETSTAT _IOR(BAYCOM_IOCTL_TYPE, 7, \ - struct baycom_statistics) - -#define BAYCOMCTL_GETPARAMS _IOR(BAYCOM_IOCTL_TYPE, 8, \ - struct baycom_params) -#define BAYCOMCTL_SETPARAMS _IOR(BAYCOM_IOCTL_TYPE, 9, \ - struct baycom_params) - -#define BAYCOMCTL_CALIBRATE _IO(BAYCOM_IOCTL_TYPE, 10) - -#ifdef BAYCOM_DEBUG -/* - * these are mainly for debugging purposes - */ -#define BAYCOMCTL_GETSAMPLES _IOR(BAYCOM_IOCTL_TYPE, 16, unsigned char) -#define BAYCOMCTL_GETBITS _IOR(BAYCOM_IOCTL_TYPE, 17, unsigned char) - -#define BAYCOMCTL_DEBUG1 _IOR(BAYCOM_IOCTL_TYPE, 18, unsigned long) -#define BAYCOMCTL_DEBUG2 _IOR(BAYCOM_IOCTL_TYPE, 19, unsigned long) -#define BAYCOMCTL_DEBUG3 _IOR(BAYCOM_IOCTL_TYPE, 20, unsigned long) -#endif /* BAYCOM_DEBUG */ +#define BAYCOMCTL_GETMODEMTYPE 0x90 +#define BAYCOMCTL_SETMODEMTYPE 0x91 +#define BAYCOMCTL_GETDEBUG 0x92 /* -------------------------------------------------------------------- */ #endif /* _BAYCOM_H */ /* --------------------------------------------------------------------- */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * End: - */ diff -u --recursive --new-file v2.1.6/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h --- v2.1.6/linux/include/linux/hdlcdrv.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/hdlcdrv.h Wed Oct 30 14:15:12 1996 @@ -0,0 +1,326 @@ +/* + * hdlcdrv.h -- HDLC packet radio network driver. + * The Linux soundcard driver for 1200 baud and 9600 baud packet radio + * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA + */ + +#ifndef _HDLCDRV_H +#define _HDLCDRV_H + +#include +#include +#include + +/* -------------------------------------------------------------------- */ +/* + * structs for the IOCTL commands + */ + +struct hdlcdrv_params { + int iobase; + int irq; + int dma; + int seriobase; + int pariobase; + int midiiobase; +}; + +struct hdlcdrv_channel_params { + int tx_delay; /* the transmitter keyup delay in 10ms units */ + int tx_tail; /* the transmitter keyoff delay in 10ms units */ + int slottime; /* the slottime in 10ms; usually 10 = 100ms */ + int ppersist; /* the p-persistence 0..255 */ + int fulldup; /* some driver do not support full duplex, setting */ + /* this just makes them send even if DCD is on */ +}; + +struct hdlcdrv_channel_state { + int ptt; + int dcd; + int ptt_keyed; + struct enet_statistics stats; +}; + +struct hdlcdrv_ioctl { + int cmd; + union { + struct hdlcdrv_params mp; + struct hdlcdrv_channel_params cp; + struct hdlcdrv_channel_state cs; + unsigned int calibrate; + unsigned char bits; + } data; +}; + +/* -------------------------------------------------------------------- */ + +/* + * ioctl values + */ +#define HDLCDRVCTL_GETMODEMPAR 0 +#define HDLCDRVCTL_SETMODEMPAR 1 +#define HDLCDRVCTL_GETCHANNELPAR 2 +#define HDLCDRVCTL_SETCHANNELPAR 3 +#define HDLCDRVCTL_GETSTAT 4 +#define HDLCDRVCTL_CALIBRATE 5 + +/* + * these are mainly for debugging purposes + */ +#define HDLCDRVCTL_GETSAMPLES 10 +#define HDLCDRVCTL_GETBITS 11 + +/* -------------------------------------------------------------------- */ + +#ifdef __KERNEL__ + +#define HDLCDRV_MAGIC 0x5ac6e778 +#define HDLCDRV_IFNAMELEN 6 +#define HDLCDRV_HDLCBUFFER 16 /* should be a power of 2 for speed reasons */ +#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */ +#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */ +#undef HDLCDRV_DEBUG + +/* maximum packet length, excluding CRC */ +#define HDLCDRV_MAXFLEN 400 + + +struct hdlcdrv_hdlcbuffer { + unsigned rd, wr; + unsigned short buf[HDLCDRV_HDLCBUFFER]; +}; + +#ifdef HDLCDRV_DEBUG +struct hdlcdrv_bitbuffer { + unsigned int rd; + unsigned int wr; + unsigned int shreg; + unsigned char buffer[HDLCDRV_BITBUFFER]; +}; + +extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, + unsigned int bit) +{ + unsigned char new; + + new = buf->shreg & 1; + buf->shreg >>= 1; + buf->shreg |= (!!bit) << 7; + if (new) { + buf->buffer[buf->wr] = buf->shreg; + buf->wr = (buf->wr+1) % sizeof(buf->buffer); + buf->shreg = 0x80; + } +} + +extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, + unsigned int bits) +{ + buf->buffer[buf->wr] = bits & 0xff; + buf->wr = (buf->wr+1) % sizeof(buf->buffer); + buf->buffer[buf->wr] = (bits >> 8) & 0xff; + buf->wr = (buf->wr+1) % sizeof(buf->buffer); + +} +#endif /* HDLCDRV_DEBUG */ + +/* -------------------------------------------------------------------- */ +/* + * Information that need to be kept for each driver. + */ + +struct hdlcdrv_ops { + /* + * first some informations needed by the hdlcdrv routines + */ + int bitrate; + /* + * the routines called by the hdlcdrv routines + */ + int (*open)(struct device *); + int (*close)(struct device *); + int (*ioctl)(struct device *, struct ifreq *, int); +}; + +struct hdlcdrv_state { + int magic; + + char ifname[HDLCDRV_IFNAMELEN]; + + const struct hdlcdrv_ops *ops; + + struct hdlcdrv_pttoutput { + int seriobase; + int pariobase; + int midiiobase; + unsigned int flags; + } ptt_out; + + struct hdlcdrv_channel_params ch_params; + + struct hdlcdrv_hdlcrx { + struct hdlcdrv_hdlcbuffer hbuf; + int in_hdlc_rx; + /* 0 = sync hunt, != 0 receiving */ + int rx_state; + unsigned int bitstream; + unsigned int bitbuf; + int numbits; + unsigned char dcd; + + int len; + unsigned char *bp; + unsigned char buffer[HDLCDRV_MAXFLEN+2]; + } hdlcrx; + + struct hdlcdrv_hdlctx { + struct hdlcdrv_hdlcbuffer hbuf; + int in_hdlc_tx; + /* + * 0 = send flags + * 1 = send txtail (flags) + * 2 = send packet + */ + int tx_state; + int numflags; + unsigned int bitstream; + unsigned char ptt; + int calibrate; + int slotcnt; + + unsigned int bitbuf; + int numbits; + + int len; + unsigned char *bp; + unsigned char buffer[HDLCDRV_MAXFLEN+2]; + } hdlctx; + +#ifdef HDLCDRV_DEBUG + struct hdlcdrv_bitbuffer bitbuf_channel; + struct hdlcdrv_bitbuffer bitbuf_hdlc; +#endif /* HDLCDRV_DEBUG */ + + struct enet_statistics stats; + int ptt_keyed; + + struct sk_buff_head send_queue; /* Packets awaiting transmission */ +}; + + +/* -------------------------------------------------------------------- */ + +extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) +{ + return !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) + % HDLCDRV_HDLCBUFFER); +} + +/* -------------------------------------------------------------------- */ + +extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb) +{ + return hb->rd == hb->wr; +} + +/* -------------------------------------------------------------------- */ + +extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb) +{ + unsigned newr; + unsigned short val; + unsigned long flags; + + if (hb->rd == hb->wr) + return 0; + save_flags(flags); + cli(); + newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER; + val = hb->buf[hb->rd]; + hb->rd = newr; + restore_flags(flags); + return val; +} + +/* -------------------------------------------------------------------- */ + +extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, + unsigned short val) +{ + unsigned newp; + unsigned long flags; + + save_flags(flags); + cli(); + newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER; + if (newp != hb->rd) { + hb->buf[hb->wr] = val & 0xffff; + hb->wr = newp; + } + restore_flags(flags); +} + +/* -------------------------------------------------------------------- */ + +extern void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits) +{ + hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits); +} + +extern unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s) +{ + unsigned int ret; + + if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) { + if (s->hdlctx.calibrate > 0) + s->hdlctx.calibrate--; + else + s->hdlctx.ptt = 0; + ret = 0; + } else + ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf); +#ifdef HDLCDRV_LOOPBACK + hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret); +#endif /* HDLCDRV_LOOPBACK */ + return ret; +} + +extern void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit) +{ +#ifdef HDLCDRV_DEBUG + hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit); +#endif /* HDLCDRV_DEBUG */ +} + +extern void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd) +{ + s->hdlcrx.dcd = !!dcd; +} + +extern int hdlcdrv_ptt(struct hdlcdrv_state *s) +{ + return s->hdlctx.ptt || (s->hdlctx.calibrate > 0); +} + +/* -------------------------------------------------------------------- */ + +void hdlcdrv_receiver(struct device *, struct hdlcdrv_state *); +void hdlcdrv_transmitter(struct device *, struct hdlcdrv_state *); +void hdlcdrv_arbitrate(struct device *, struct hdlcdrv_state *); +int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops, + unsigned int privsize, char *ifname, + unsigned int baseaddr, unsigned int irq, + unsigned int dma); +int hdlcdrv_unregister_hdlcdrv(struct device *dev); + +/* -------------------------------------------------------------------- */ + + + +#endif /* __KERNEL__ */ + +/* -------------------------------------------------------------------- */ + +#endif /* _HDLCDRV_H */ + +/* -------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.6/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v2.1.6/linux/include/linux/interrupt.h Thu Jun 6 12:59:42 1996 +++ linux/include/linux/interrupt.h Wed Oct 30 14:15:12 1996 @@ -33,7 +33,6 @@ DIGI_BH, SERIAL_BH, RISCOM8_BH, - BAYCOM_BH, NET_BH, IMMEDIATE_BH, KEYBOARD_BH, diff -u --recursive --new-file v2.1.6/linux/include/linux/md.h linux/include/linux/md.h --- v2.1.6/linux/include/linux/md.h Mon May 20 07:50:46 1996 +++ linux/include/linux/md.h Wed Oct 30 03:06:23 1996 @@ -18,7 +18,6 @@ #ifndef _MD_H #define _MD_H -#include #include #include #include diff -u --recursive --new-file v2.1.6/linux/include/linux/scc.h linux/include/linux/scc.h --- v2.1.6/linux/include/linux/scc.h Tue Oct 29 19:58:47 1996 +++ linux/include/linux/scc.h Thu Oct 31 08:05:23 1996 @@ -1,10 +1,8 @@ -/* $Id: scc.h,v 1.26 1996/10/09 16:35:56 jreuter Exp jreuter $ */ +/* $Id: scc.h,v 1.28 1996/10/30 20:01:15 jreuter Exp jreuter $ */ #ifndef _SCC_H #define _SCC_H -#include - /* selection of hardware types */ #define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */ @@ -14,10 +12,6 @@ #define DRSI 0x08 /* hardware type for DRSI PC*Packet card */ #define BAYCOM 0x10 /* hardware type for BayCom (U)SCC */ -/* Paranoia check... */ - -#define SCC_PARANOIA_CHECK /* tell the user if something is going wrong */ - /* DEV ioctl() commands */ #define SIOCSCCRESERVED (SIOCDEVPRIVATE+0) @@ -103,49 +97,8 @@ #define TXS_WAIT 5 /* Waiting for Mintime to expire */ #define TXS_TIMEOUT 6 /* We had a transmission timeout */ -#define TX_ON 1 /* command for scc_key_trx() */ -#define TX_OFF 0 /* dto */ - -/* Vector masks in RR2B */ - -#define VECTOR_MASK 0x06 -#define TXINT 0x00 -#define EXINT 0x02 -#define RXINT 0x04 -#define SPINT 0x06 - typedef unsigned long io_port; /* type definition for an 'io port address' */ -#ifdef SCC_DELAY -#define Inb(port) inb_p(port) -#define Outb(port, val) outb_p(val, port) -#else -#define Inb(port) inb(port) -#define Outb(port, val) outb(val, port) -#endif - -#define TIMER_OFF 65535U - -/* SCC channel control structure for KISS */ - -struct scc_kiss { - unsigned char txdelay; /* Transmit Delay 10 ms/cnt */ - unsigned char persist; /* Persistence (0-255) as a % */ - unsigned char slottime; /* Delay to wait on persistence hit */ - unsigned char tailtime; /* Delay after last byte written */ - unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */ - unsigned char waittime; /* Waittime before any transmit attempt */ - unsigned int maxkeyup; /* Maximum time to transmit (seconds) */ - unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */ - unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */ - unsigned int maxdefer; /* Timer for CSMA channel busy limit */ - unsigned char tx_inhibit; /* Transmit is not allowed when set */ - unsigned char group; /* Group ID for AX.25 TX interlocking */ - unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */ - unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */ -}; - - /* SCC statistical information */ struct scc_stat { @@ -207,6 +160,48 @@ }; +#ifdef __KERNEL__ + +#define TX_ON 1 /* command for scc_key_trx() */ +#define TX_OFF 0 /* dto */ + +/* Vector masks in RR2B */ + +#define VECTOR_MASK 0x06 +#define TXINT 0x00 +#define EXINT 0x02 +#define RXINT 0x04 +#define SPINT 0x06 + + +#ifdef SCC_DELAY +#define Inb(port) inb_p(port) +#define Outb(port, val) outb_p(val, port) +#else +#define Inb(port) inb(port) +#define Outb(port, val) outb(val, port) +#endif + +/* SCC channel control structure for KISS */ + +struct scc_kiss { + unsigned char txdelay; /* Transmit Delay 10 ms/cnt */ + unsigned char persist; /* Persistence (0-255) as a % */ + unsigned char slottime; /* Delay to wait on persistence hit */ + unsigned char tailtime; /* Delay after last byte written */ + unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */ + unsigned char waittime; /* Waittime before any transmit attempt */ + unsigned int maxkeyup; /* Maximum time to transmit (seconds) */ + unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */ + unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */ + unsigned int maxdefer; /* Timer for CSMA channel busy limit */ + unsigned char tx_inhibit; /* Transmit is not allowed when set */ + unsigned char group; /* Group ID for AX.25 TX interlocking */ + unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */ + unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */ +}; + + /* SCC channel structure */ struct scc_channel { @@ -235,7 +230,7 @@ struct scc_stat stat; /* statistical information */ struct scc_modem modem; /* modem information */ - struct sk_buff *tx_next_buff; /* next tx buffer */ + struct sk_buff_head tx_queue; /* next tx buffer */ struct sk_buff *rx_buff; /* pointer to frame currently received */ struct sk_buff *tx_buff; /* pointer to frame currently transmitted */ @@ -246,4 +241,5 @@ }; int scc_init(void); -#endif +#endif /* defined(__KERNEL__) */ +#endif /* defined(_SCC_H) */ diff -u --recursive --new-file v2.1.6/linux/include/linux/soundmodem.h linux/include/linux/soundmodem.h --- v2.1.6/linux/include/linux/soundmodem.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/soundmodem.h Wed Oct 30 14:15:12 1996 @@ -0,0 +1,107 @@ +/* + * The Linux soundcard driver for 1200 baud and 9600 baud packet radio + * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA + */ + +#ifndef _SOUNDMODEM_H +#define _SOUNDMODEM_H + +#include +#include + +/* -------------------------------------------------------------------- */ +/* + * structs for the IOCTL commands + */ + +struct sm_debug_data { + unsigned long debug1; + unsigned long debug2; + long debug3; +}; + +struct sm_diag_data { + unsigned int mode; + unsigned int flags; + unsigned int samplesperbit; + unsigned int datalen; + short *data; +}; + +struct sm_mixer_data { + unsigned int mixer_type; + unsigned int sample_rate; + unsigned int bit_rate; + unsigned int reg; + unsigned int data; +}; + +struct sm_config { + int hardware; + int mode; +}; + +struct sm_ioctl { + int cmd; + union { + struct sm_config cfg; + struct sm_diag_data diag; + struct sm_mixer_data mix; + struct sm_debug_data dbg; + } data; +}; + +/* -------------------------------------------------------------------- */ + +/* + * config: hardware + */ +#define SM_HARDWARE_INVALID -1 +#define SM_HARDWARE_SBC 0 +#define SM_HARDWARE_WSS 1 +#define SM_HARDWARE_WSSFDX 2 /* currently does not work! */ + +/* + * config: mode + */ +#define SM_MODE_INVALID -1 +#define SM_MODE_AFSK1200 0 +#define SM_MODE_FSK9600 1 + +/* + * diagnose modes + */ +#define SM_DIAGMODE_OFF 0 +#define SM_DIAGMODE_INPUT 1 +#define SM_DIAGMODE_DEMOD 2 + +/* + * diagnose flags + */ +#define SM_DIAGFLAG_DCDGATE (1<<0) +#define SM_DIAGFLAG_VALID (1<<1) + +/* + * mixer types + */ +#define SM_MIXER_INVALID 0 +#define SM_MIXER_AD1848 0x10 +#define SM_MIXER_CT1335 0x20 +#define SM_MIXER_CT1345 0x21 +#define SM_MIXER_CT1745 0x22 + +/* + * ioctl values + */ +#define SMCTL_GETMODEMTYPE 0x80 +#define SMCTL_SETMODEMTYPE 0x81 +#define SMCTL_DIAGNOSE 0x82 +#define SMCTL_GETMIXER 0x83 +#define SMCTL_SETMIXER 0x84 +#define SMCTL_GETDEBUG 0x85 + +/* -------------------------------------------------------------------- */ + +#endif /* _SOUNDMODEM_H */ + +/* --------------------------------------------------------------------- */ diff -u --recursive --new-file v2.1.6/linux/include/linux/tty.h linux/include/linux/tty.h --- v2.1.6/linux/include/linux/tty.h Mon Sep 30 11:19:00 1996 +++ linux/include/linux/tty.h Fri Nov 1 15:24:52 1996 @@ -292,7 +292,6 @@ extern int stl_init(void); extern int stli_init(void); extern int riscom8_init(void); -extern int baycom_init(void); extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device, const char *routine); diff -u --recursive --new-file v2.1.6/linux/init/main.c linux/init/main.c --- v2.1.6/linux/init/main.c Wed Oct 9 08:55:24 1996 +++ linux/init/main.c Wed Oct 30 14:15:12 1996 @@ -172,6 +172,9 @@ #ifdef CONFIG_BAYCOM extern void baycom_setup(char *str, int *ints); #endif +#ifdef CONFIG_SOUNDMODEM +extern void sm_setup(char *str, int *ints); +#endif #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) @@ -414,6 +417,9 @@ #endif #ifdef CONFIG_BAYCOM { "baycom=", baycom_setup }, +#endif +#ifdef CONFIG_SOUNDMODEM + { "soundmodem=", sm_setup }, #endif { 0, 0 } }; diff -u --recursive --new-file v2.1.6/linux/net/core/datagram.c linux/net/core/datagram.c --- v2.1.6/linux/net/core/datagram.c Tue Oct 29 19:58:49 1996 +++ linux/net/core/datagram.c Wed Oct 30 19:52:11 1996 @@ -68,9 +68,7 @@ static inline int connection_based(struct sock *sk) { - if(sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM) - return 1; - return 0; + return (sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM); } /* @@ -190,29 +188,26 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait) { - select_wait(sk->sleep, wait); + if (sk->err) + return 1; switch(sel_type) { case SEL_IN: - if (sk->err) - return 1; if (sk->shutdown & RCV_SHUTDOWN) return 1; if (connection_based(sk) && sk->state==TCP_CLOSE) { /* Connection closed: Wake up */ - return(1); + return 1; } - if (skb_peek(&sk->receive_queue) != NULL) + if (!skb_queue_empty(&sk->receive_queue)) { /* This appears to be consistent with other stacks */ - return(1); + return 1; } - return(0); + break; case SEL_OUT: - if (sk->err) - return 1; if (sk->shutdown & SEND_SHUTDOWN) return 1; if (connection_based(sk) && sk->state==TCP_SYN_SENT) @@ -222,18 +217,19 @@ } if (sk->prot && sock_wspace(sk) >= MIN_WRITE_SPACE) { - return(1); + return 1; } if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE) { - return(1); + return 1; } - return(0); + break; case SEL_EX: - if (sk->err) - return(1); /* Socket has gone into error state (eg icmp error) */ - return(0); + break; } - return(0); + + /* select failed.. */ + select_wait(sk->sleep, wait); + return 0; } diff -u --recursive --new-file v2.1.6/linux/net/core/dev.c linux/net/core/dev.c --- v2.1.6/linux/net/core/dev.c Tue Oct 29 19:58:49 1996 +++ linux/net/core/dev.c Wed Oct 30 14:15:12 1996 @@ -1369,6 +1369,9 @@ extern int scc_init(void); extern void sdla_setup(void); extern void dlci_setup(void); +extern int pt_init(void); +extern int sm_init(void); +extern int baycom_init(void); #ifdef CONFIG_PROC_FS static struct proc_dir_entry proc_net_dev = { @@ -1419,6 +1422,12 @@ #endif #if defined(CONFIG_SDLA) sdla_setup(); +#endif +#if defined(CONFIG_BAYCOM) + baycom_init(); +#endif +#if defined(CONFIG_SOUNDMODEM) + sm_init(); #endif /* * SLHC if present needs attaching so other people see it