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
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
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
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 6
+SUBLEVEL = 7
ARCH = i386
#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
.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
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
/*
* A fork is the same as clone(SIGCHLD, 0);
-*/
+ */
.align 3
.globl sys_fork
.ent sys_fork
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
restore_all:
RESTORE_ALL
rti
-
-
-/* PTRACE syscall handler */
+
+
+/* PTRACE syscall handler */
.align 3
strace:
/* set up signal stack, call syscall_trace */
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 */
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);
}
* $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
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 :
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 :
$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 :
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
* destination address in $6
* source address in $7
* exception pointer in $8
- * return address in $28 (exceptions expect it there)
+ * return address in $28
*
* Outputs:
* bytes left to copy in $0
* $1,$2,$3,$4,$5,$6,$7
*/
+/* Allow an exception for an insn; exit if we get one. */
+#define EX(x,y...) \
+ 99: x,##y; \
+ .section __ex_table,"a"; \
+ .gprel32 99b; \
+ lda $31, $exit-99b($31); \
+ .text
+
.set noat
.align 3
.globl __copy_user
.ent __copy_user
__copy_user:
- ldq $5,0($8)
- beq $0,$35
and $6,7,$3
- addq $5,1,$1
- stq $1,0($8)
+ beq $0,$35
beq $3,$36
subq $3,8,$3
.align 5
$37:
- ldq_u $1,0($7)
- ldq_u $2,0($6)
+ EX( ldq_u $1,0($7) )
+ EX( ldq_u $2,0($6) )
extbl $1,$7,$1
mskbl $2,$6,$2
insbl $1,$6,$1
addq $3,1,$3
bis $1,$2,$1
- stq_u $1,0($6)
+ EX( stq_u $1,0($6) )
subq $0,1,$0
addq $6,1,$6
addq $7,1,$7
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
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
$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
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
*
* -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 <asm/errno.h>
+#include <alpha/regdef.h>
+
+
+/* Allow an exception for an insn; exit if we get one. */
+#define EX(x,y...) \
+ 99: x,##y; \
+ .section __ex_table,"a"; \
+ .gprel32 99b; \
+ lda $31, $exception-99b($0); \
+ .text
+
.set noat
.set noreorder
-
.text
- .align 3
.globl __strncpy_from_user
.ent __strncpy_from_user
+ .frame $30, 0, $26
+
+ .align 3
+$aligned:
+ /* On entry to this basic block:
+ t0 == the first destination word for masking back in
+ t1 == the first source word. */
+
+ /* Create the 1st output word and detect 0's in the 1st input word. */
+ lda t2, -1 # e1 : build a mask against false zero
+ mskqh t2, a1, t2 # e0 : detection in the src word
+ mskqh t1, a1, t3 # e0 :
+ ornot t1, t2, t2 # .. e1 :
+ mskql t0, a1, t0 # e0 : assemble the first output word
+ cmpbge zero, t2, t7 # .. e1 : bits set iff null found
+ or t0, t3, t0 # e0 :
+ beq a2, $a_eoc # .. e1 :
+ bne t7, $a_eos # .. e1 :
+
+ /* On entry to this basic block:
+ t0 == a source word not containing a null. */
+
+$a_loop:
+ stq_u t0, 0(a0) # e0 :
+ addq a0, 8, a0 # .. e1 :
+ EX( ldq_u t0, 0(a1) ) # e0 :
+ addq a1, 8, a1 # .. e1 :
+ subq a2, 1, a2 # e0 :
+ cmpbge zero, t0, t7 # .. e1 (stall)
+ beq a2, $a_eoc # e1 :
+ beq t7, $a_loop # e1 :
+
+ /* Take care of the final (partial) word store. At this point
+ the end-of-count bit is set in t7 iff it applies.
+
+ On entry to this basic block we have:
+ t0 == the source word containing the null
+ t7 == the cmpbge mask that found it. */
+$a_eos:
+ negq t7, t8 # e0 : find low bit set
+ and t7, t8, t8 # e1 (stall)
+
+ /* For the sake of the cache, don't read a destination word
+ if we're not going to need it. */
+ and t8, 0x80, t6 # e0 :
+ bne t6, 1f # .. e1 (zdb)
+
+ /* We're doing a partial word store and so need to combine
+ our source and original destination words. */
+ ldq_u t1, 0(a0) # e0 :
+ subq t8, 1, t6 # .. e1 :
+ or t8, t6, t7 # e0 :
+ unop #
+ zapnot t0, t7, t0 # e0 : clear src bytes > null
+ zap t1, t7, t1 # .. e1 : clear dst bytes <= null
+ or t0, t1, t0 # e1 :
+
+1: stq_u t0, 0(a0)
+ br $finish_up
+
+ /* Add the end-of-count bit to the eos detection bitmask. */
+$a_eoc:
+ or t10, t7, t7
+ br $a_eos
+
+ /*** The Function Entry Point ***/
+ .align 3
__strncpy_from_user:
- .frame $30, 0, $26
- .prologue 0
- ldq $20, 0($19)
- beq $18, 9f
- br $28, 1f # set up exception return address
+ mov a0, v0 # save the string start
+ beq a2, $zerolength
- lda $0, -EFAULT
- ret
+ /* Are source and destination co-aligned? */
+ xor a0, a1, t1 # e0 :
+ and a0, 7, t0 # .. e1 : find dest misalignment
+ and t1, 7, t1 # e0 :
+ addq a2, t0, a2 # .. e1 : bias count by dest misalignment
+ subq a2, 1, a2 # e0 :
+ and a2, 7, t2 # e1 :
+ srl a2, 3, a2 # e0 : a2 = loop counter = (count - 1)/8
+ addq zero, 1, t10 # .. e1 :
+ sll t10, t2, t10 # e0 : t10 = bitmask of last count byte
+ bne t1, $unaligned # .. e1 :
+
+ /* We are co-aligned; take care of a partial first word. */
+
+ EX( ldq_u t1, 0(a1) ) # e0 : load first src word
+ addq a1, 8, a1 # .. e1 :
+
+ beq t0, $aligned # avoid loading dest word if not needed
+ ldq_u t0, 0(a0) # e0 :
+ br $aligned # .. e1 :
+
+
+/* The source and destination are not co-aligned. Align the destination
+ and cope. We have to be very careful about not reading too much and
+ causing a SEGV. */
+
+ .align 3
+$u_head:
+ /* We know just enough now to be able to assemble the first
+ full source word. We can still find a zero at the end of it
+ that prevents us from outputting the whole thing.
+
+ On entry to this basic block:
+ t0 == the first dest word, unmasked
+ t1 == the shifted low bits of the first source word
+ t6 == bytemask that is -1 in dest word bytes */
+
+ EX( ldq_u t2, 8(a1) ) # e0 : load second src word
+ addq a1, 8, a1 # .. e1 :
+ mskql t0, a0, t0 # e0 : mask trailing garbage in dst
+ extqh t2, a1, t4 # e0 :
+ or t1, t4, t1 # e1 : first aligned src word complete
+ mskqh t1, a0, t1 # e0 : mask leading garbage in src
+ or t0, t1, t0 # e0 : first output word complete
+ or t0, t6, t6 # e1 : mask original data for zero test
+ cmpbge zero, t6, t7 # e0 :
+ beq a2, $u_eocfin # .. e1 :
+ bne t7, $u_final # e1 :
+
+ lda t6, -1 # e1 : mask out the bits we have
+ mskql t6, a1, t6 # e0 : already seen
+ stq_u t0, 0(a0) # e0 : store first output word
+ or t6, t2, t2 # .. e1 :
+ cmpbge zero, t2, t7 # e0 : find nulls in second partial
+ addq a0, 8, a0 # .. e1 :
+ subq a2, 1, a2 # e0 :
+ bne t7, $u_late_head_exit # .. e1 :
+
+ /* Finally, we've got all the stupid leading edge cases taken care
+ of and we can set up to enter the main loop. */
+
+ extql t2, a1, t1 # e0 : position hi-bits of lo word
+ EX( ldq_u t2, 8(a1) ) # .. e1 : read next high-order source word
+ addq a1, 8, a1 # e0 :
+ cmpbge zero, t2, t7 # e1 (stall)
+ beq a2, $u_eoc # e1 :
+ bne t7, $u_eos # e1 :
+
+ /* Unaligned copy main loop. In order to avoid reading too much,
+ the loop is structured to detect zeros in aligned source words.
+ This has, unfortunately, effectively pulled half of a loop
+ iteration out into the head and half into the tail, but it does
+ prevent nastiness from accumulating in the very thing we want
+ to run as fast as possible.
+
+ On entry to this basic block:
+ t1 == the shifted high-order bits from the previous source word
+ t2 == the unshifted current source word
+
+ We further know that t2 does not contain a null terminator. */
+
+ .align 3
+$u_loop:
+ extqh t2, a1, t0 # e0 : extract high bits for current word
+ addq a1, 8, a1 # .. e1 :
+ extql t2, a1, t3 # e0 : extract low bits for next time
+ addq a0, 8, a0 # .. e1 :
+ or t0, t1, t0 # e0 : current dst word now complete
+ EX( ldq_u t2, 0(a1) ) # .. e1 : load high word for next time
+ stq_u t0, -8(a0) # e0 : save the current word
+ mov t3, t1 # .. e1 :
+ subq a2, 1, a2 # e0 :
+ cmpbge zero, t2, t7 # .. e1 : test new word for eos
+ beq a2, $u_eoc # e1 :
+ beq t7, $u_loop # e1 :
+
+ /* We've found a zero somewhere in the source word we just read.
+ If it resides in the lower half, we have one (probably partial)
+ word to write out, and if it resides in the upper half, we
+ have one full and one partial word left to write out.
+
+ On entry to this basic block:
+ t1 == the shifted high-order bits from the previous source word
+ t2 == the unshifted current source word. */
+$u_eos:
+ extqh t2, a1, t0 # e0 :
+ or t0, t1, t0 # e1 : first (partial) source word complete
+
+ cmpbge zero, t0, t7 # e0 : is the null in this first bit?
+ bne t7, $u_final # .. e1 (zdb)
+
+ stq_u t0, 0(a0) # e0 : the null was in the high-order bits
+ addq a0, 8, a0 # .. e1 :
+ subq a2, 1, a2 # e1 :
+
+$u_late_head_exit:
+ extql t2, a1, t0 # .. e0 :
+ cmpbge zero, t0, t7 # e0 :
+ or t7, t10, t6 # e1 :
+ cmoveq a2, t6, t7 # e0 :
+ nop # .. e1 :
+
+ /* Take care of a final (probably partial) result word.
+ On entry to this basic block:
+ t0 == assembled source word
+ t7 == cmpbge mask that found the null. */
+$u_final:
+ negq t7, t6 # e0 : isolate low bit set
+ and t6, t7, t8 # e1 :
+
+ and t8, 0x80, t6 # e0 : avoid dest word load if we can
+ bne t6, 1f # .. e1 (zdb)
+
+ ldq_u t1, 0(a0) # e0 :
+ subq t8, 1, t6 # .. e1 :
+ or t6, t8, t7 # e0 :
+ zapnot t0, t7, t0 # .. e1 : kill source bytes > null
+ zap t1, t7, t1 # e0 : kill dest bytes <= null
+ or t0, t1, t0 # e1 :
+
+1: stq_u t0, 0(a0) # e0 :
+ br $finish_up
+
+$u_eoc: # end-of-count
+ extqh t2, a1, t0
+ or t0, t1, t0
+ cmpbge zero, t0, t7
+
+$u_eocfin: # end-of-count, final word
+ or t10, t7, t7
+ br $u_final
+
+ /* Unaligned copy entry point. */
+ .align 3
+$unaligned:
+
+ EX( ldq_u t1, 0(a1) ) # e0 : load first source word
+
+ and a0, 7, t4 # .. e1 : find dest misalignment
+ and a1, 7, t5 # e0 : find src misalignment
+
+ /* Conditionally load the first destination word and a bytemask
+ with 0xff indicating that the destination byte is sacrosanct. */
+
+ mov zero, t0 # .. e1 :
+ mov zero, t6 # e0 :
+ beq t4, 1f # .. e1 :
+ ldq_u t0, 0(a0) # e0 :
+ lda t6, -1 # .. e1 :
+ mskql t6, a0, t6 # e0 :
+1:
+ subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr
+
+ /* If source misalignment is larger than dest misalignment, we need
+ extra startup checks to avoid SEGV. */
+
+ cmplt t4, t5, t8 # e1 :
+ extql t1, a1, t1 # .. e0 : shift src into place
+ lda t2, -1 # e0 : for creating masks later
+ beq t8, $u_head # e1 :
+
+ mskqh t2, t5, t2 # e0 : begin src byte validity mask
+ cmpbge zero, t1, t7 # .. e1 : is there a zero?
+ extql t2, a1, t2 # e0 :
+ or t7, t10, t6 # .. e1 : test for end-of-count too
+ cmpbge zero, t2, t3 # e0 :
+ cmoveq a2, t6, t7 # .. e1 :
+ andnot t7, t3, t7 # e0 :
+ beq t7, $u_head # .. e1 (zdb)
+
+ /* At this point we've found a zero in the first partial word of
+ the source. We need to isolate the valid source data and mask
+ it into the original destination data. (Incidentally, we know
+ that we'll need at least one byte of that original dest word.) */
+
+ ldq_u t0, 0(a0) # e0 :
+ negq t7, t6 # .. e1 : build bitmask of bytes <= zero
+ mskqh t1, t4, t1 # e0 :
+ and t6, t7, t8 # .. e1 :
+ subq t8, 1, t6 # e0 :
+ or t6, t8, t7 # e1 :
+
+ zapnot t2, t7, t2 # e0 : prepare source word; mirror changes
+ zapnot t1, t7, t1 # .. e1 : to source validity mask
+
+ andnot t0, t2, t0 # e0 : zero place for source to reside
+ or t0, t1, t0 # e1 : and put it there
+ stq_u t0, 0(a0) # e0 :
+
+$finish_up:
+ zapnot t0, t10, t4 # was last byte written null?
+ cmovne t4, 1, t4
+
+ and t10, 0xf0, t3 # binary search for the address of the
+ and t10, 0xcc, t2 # last byte written
+ and t10, 0xaa, t1
+ bic a0, 7, t0
+ cmovne t3, 4, t3
+ cmovne t2, 2, t2
+ cmovne t1, 1, t1
+ addq t0, t3, t0
+ addq t1, t2, t1
+ addq t0, t1, t0
+ addq t0, t4, t0 # add one if we filled the buffer
-1: addq $20, 1, $21
- mov $16, $0 # save the string start
- stq $21, 0($19) # increment exception count
- bsr $23, __stxncpy # do the work of the copy
-
- zapnot $1, $22, $5 # was last byte written null?
- stq $20, 0($19) # decrement exception count
- cmovne $5, 1, $5
-
- and $22, 0xf0, $4 # binary search for the address of the
- and $22, 0xcc, $3 # last byte written
- and $22, 0xaa, $2
- bic $16, 7, $1
- cmovne $4, 4, $4
- cmovne $3, 2, $3
- cmovne $2, 1, $2
- addq $1, $4, $1
- addq $2, $3, $2
- addq $1, $2, $1
- addq $1, $5, $1 # add one if we filled the buffer
-
- subq $1, $0, $0 # find string length
+ subq t0, v0, v0 # find string length
ret
-9: clr $0
+$zerolength:
+ clr v0
+$exception:
ret
.end __strncpy_from_user
#
# 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)
--- /dev/null
+/*
+ * linux/arch/alpha/mm/extable.c
+ */
+
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __start___ex_table[];
+extern const struct exception_table_entry __stop___ex_table[];
+
+static inline unsigned
+search_one_table(const struct exception_table_entry *first,
+ const struct exception_table_entry *last,
+ signed int value)
+{
+ while (first <= last) {
+ const struct exception_table_entry *mid;
+ long diff;
+
+ mid = (last - first) / 2 + first;
+ diff = mid->insn - value;
+ if (diff == 0)
+ return mid->fixup.unit;
+ else if (diff < 0)
+ first = mid+1;
+ else
+ last = mid-1;
+ }
+ return 0;
+}
+
+unsigned
+search_exception_table(unsigned long addr)
+{
+ unsigned ret;
+ signed int reladdr;
+
+ /* Search the kernel's table first. */
+ {
+ register unsigned long gp __asm__("$29");
+ reladdr = addr - gp;
+ }
+ ret = search_one_table(__start___ex_table,
+ __stop___ex_table-1, reladdr);
+ if (ret)
+ return ret;
+
+ /* FIXME -- search the module's tables here */
+
+ return 0;
+}
* -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);
*/
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;
}
* 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);
}
# 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
--- /dev/null
+/*
+ * linux/arch/i386/mm/extable.c
+ */
+
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __start___ex_table[];
+extern const struct exception_table_entry __stop___ex_table[];
+
+static inline unsigned long
+search_one_table(const struct exception_table_entry *first,
+ const struct exception_table_entry *last,
+ unsigned long value)
+{
+ /* Some versions of the linker are buggy and do not align the
+ __start pointer along with the section, thus we may be low. */
+ if ((long)first & 3)
+ (long)first = ((long)first | 3) + 1;
+
+ while (first <= last) {
+ const struct exception_table_entry *mid;
+ long diff;
+
+ mid = (last - first) / 2 + first;
+ diff = mid->insn - value;
+ if (diff == 0)
+ return mid->fixup;
+ else if (diff < 0)
+ first = mid+1;
+ else
+ last = mid-1;
+ }
+ return 0;
+}
+
+unsigned long
+search_exception_table(unsigned long addr)
+{
+ unsigned long ret;
+
+ /* Search the kernel's table first. */
+ ret = search_one_table(__start___ex_table,
+ __stop___ex_table-1, addr);
+ if (ret)
+ return ret;
+
+ /* FIXME -- search the module's tables here */
+
+ return 0;
+}
struct vm_area_struct * vma;
unsigned long address;
unsigned long page;
+ unsigned long fixup;
int write;
/* get the address */
*/
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;
#define MD_DRIVER
#include <linux/blk.h>
+#include <asm/uaccess.h>
static struct hd_struct md_hd_struct[MAX_MD_DEV];
static int md_blocksizes[MAX_MD_DEV];
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:
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
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);
}
-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);
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);
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/fcntl.h>
+#include <asm/uaccess.h>
#include <linux/cdrom.h>
#include <linux/ucdrom.h>
+++ /dev/null
- LINUX DRIVER FOR BAYCOM MODEMS
-
- Thomas M. Sailer <hb9jnx@radio.amiv.ethz.ch>
-
-This document describes the Linux Kernel Driver for simple Baycom style
-amateur radio modems. The driver supports the following modems:
-
-ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
- of a modulator/demodulator chip, usually a TI TCM3105. The computer
- is responsible for regenerating the receiver bit clock, as well as
- for handling the HDLC protocol. The modem connects to a serial port,
- hence the name. Since the serial port is not used as an async serial
- port, the kernel driver for serial ports cannot be used, and this
- driver only supports standard serial hardware (8250, 16450, 16550)
-
-par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
- The modem does all the filtering and regenerates the receiver clock.
- Data is transferred from and to the PC via a shift register.
- The shift register is filled with 16 bits and an interrupt is signalled.
- The PC then empties the shift register in a burst. This modem connects
- to the parallel port, hence the name. The modem leaves the
- implementation of the HDLC protocol and the scrambler polynomial to
- the PC.
-
-par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
- is protocol compatible to par96, but uses only three low power ICs
- and can therefore be fed from the parallel port and does not require
- an additional power supply.
-
-All of the above modems only support half duplex communications. However,
-the driver supports the KISS (see below) fullduplex command. It then simply
-starts to send as soon as there's a packet to transmit and does not care
-about DCD, i.e. it starts to send even if there's someone else on the channel.
-This command is required by some implementations of the DAMA channel
-access protocol.
-
-
-The Interface of the driver
-
-The driver interfaces to the AX25 stack via a KISS interface. The driver
-can be accessed as a character device with major 60. Major 60 is the first
-number of the local/experimental range. I did no steps to coordinate a
-major number for this driver, but I plan to do so in the near future.
-The driver supports multiple modems (currently four, as defined with
-NR_PORTS). It therefore responds to minor numbers 0 to 3. I recommend
-to access the driver via the special device files /dev/bc[0-3], which
-can be created with 'make bc'.
-
-
-Compiling and installing the driver
-
-First unpack the source files into a directory. Then enter the following: (you
-must be root to do it)
-
- make dep
- make
-
-This will create the files baycom.o and setbaycom. baycom.o is as well copied
-to /lib/modules/`uname -n`/misc. If you plan to use kerneld, do the following:
-
- depmod -a
-
-Do not forget to create the device special files if you install the driver the
-first time. This can be done with:
-
- make bc
-
-You are now ready to use the driver. You can now activate the driver manually
-by entering
-
- insmod baycom
-
-or leave this task to kerneld (if installed). Add the following line to
-/etc/conf.modules
-
- alias char-major-60 baycom
-
-
-
-Configuring the driver
-
-Every time the driver is inserted into the kernel, it has to know which
-modems it should access at which ports. This can be done with the setbaycom
-utility. If you are only using one modem, you can also configure the
-driver from the insmod command line (or by means of an option line in
-/etc/conf.modules).
-
-Examples:
- insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
- setbaycom -i /dev/bc0 -p ser12 0x3f8 4 1
-
-Both lines configure the first port to drive a ser12 modem at the first
-serial port (COM1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-
- insmod baycom modem=2 iobase=0x378 irq=7 options=1
- setbaycom -i /dev/bc0 -p par96 0x378 7 1
-
-Both lines configure the first port to drive a par96 or par97 modem at the
-first parallel port (LPT1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-
-The channel access parameters must be set through KISS parameter frames. The
-AX25 stack may be used to generate such frames. KA9Q NET derivatives such
-as WAMPES or TNOS offer the 'param' command for this purpose.
-
-
-
-Hardware DCD versus Software DCD
-
-To avoid collisions on the air, the driver must know when the channel is
-busy. This is the task of the DCD circuitry/software. The driver may either
-utilise a software DCD algorithm (options=1) or use a DCD signal from
-the hardware (options=0).
-
-ser12: if software DCD is utilised, the radio's squelch should always be
- open. It is highly recommended to use the software DCD algorithm,
- as it is much faster than most hardware squelch circuitry. The
- disadvantage is a slightly higher load on the system.
-
-par96: the software DCD algorithm for this type of modem is rather poor.
- The modem simply does not provide enough information to implement
- a reasonable DCD algorithm in software. Therefore, if your radio
- feeds the DCD input of the PAR96 modem, the use of the hardware
- DCD circuitry is recommended.
-
-par97: as far as I know it is in this respect equivalent to par96.
-
-
-
-Compatibility with the rest of the Linux kernel
-
-The tty interface gave me some headaches. I did not find a reasonable
-documentation of its interfaces, so I'm not particularly sure if I implemented
-it the way I should. Perhaps someone with a more profound knowledge about
-tty drivers could check the interface routines.
-The driver produces a rather high interrupt load. par96/par97 generates 600
-interrupts per second, ser12 1200 while transmitting and 2400 if hardware
-DCD is used, 3600 otherwise. If other device drivers disable interrupts
-too long, the performance of the driver drops (the packet loss rate increases),
-especially with the ser12 modem.
-There were also reports that under rather high load situations the driver
-drops frames. This might be either an interrupt problem, or an AX25 stack
-running in user mode might not get enough CPU time to process the packets
-before the drivers internal buffers overflow. There is no way to throttle
-the other radio stations from this layer, throttling must be done in the
-AX25 layer.
-
-The serial driver, the line printer (lp) driver and the baycom driver compete
-for the same hardware resources. Of course only one driver can access a given
-interface at a time. The serial driver grabs all interfaces it can find at
-startup time. Therefore the baycom driver subsequently won't be able to
-access a serial port. You might therefore find it necessary to release
-a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
-# is the number of the interface. The baycom driver does not reserve any
-port at startup, unless one is specified on the 'insmod' command line. Another
-method to solve the problem is to compile all three drivers as modules and
-leave it to kerneld to load the correct driver depending on the application.
-
-
-
-vy 73s de
-Tom Sailer, hb9jnx@radio.amiv.ethz.ch
-hb9jnx @ hb9w.ampr.org
+++ /dev/null
-/*****************************************************************************/
-
-/*
- * baycom.c -- baycom ser12 and par96 radio modem driver.
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- *
- * Supported modems
- *
- * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
- * of a modulator/demodulator chip, usually a TI TCM3105. The computer
- * is responsible for regenerating the receiver bit clock, as well as
- * for handling the HDLC protocol. The modem connects to a serial port,
- * hence the name. Since the serial port is not used as an async serial
- * port, the kernel driver for serial ports cannot be used, and this
- * driver only supports standard serial hardware (8250, 16450, 16550)
- *
- * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
- * The modem does all the filtering and regenerates the receiver clock.
- * Data is transferred from and to the PC via a shift register.
- * The shift register is filled with 16 bits and an interrupt is
- * signalled. The PC then empties the shift register in a burst. This
- * modem connects to the parallel port, hence the name. The modem
- * leaves the implementation of the HDLC protocol and the scrambler
- * polynomial to the PC.
- *
- * par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
- * modem is protocol compatible to par96, but uses only three low
- * power ICs and can therefore be fed from the parallel port and
- * does not require an additional power supply.
- *
- *
- * Command line options (insmod command line)
- *
- * major major number the driver should use; default 60
- * modem modem type of the first channel (minor 0); 1=ser12,
- * 2=par96/par97, any other value invalid
- * iobase base address of the port; common values are for ser12 0x3f8,
- * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
- * irq interrupt line of the port; common values are for ser12 3,4
- * and for par96/par97 7
- * options 0=use hardware DCD, 1=use software DCD
- *
- *
- * History:
- * 0.1 03.05.96 Renamed from ser12 0.5 and added support for par96
- * Various resource allocation cleanups
- * 0.2 12.05.96 Changed major to allocated 51. Integrated into kernel
- * source tree
- * 0.3 04.06.96 Major bug fixed (forgot to wake up after write) which
- * interestingly manifested only with kernel ax25
- * (the slip line discipline)
- * introduced bottom half and tq_baycom
- * HDLC processing now done with interrupts on
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/version.h>
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <asm/uaccess.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/interrupt.h>
-#include <linux/tqueue.h>
-#include <linux/baycom.h>
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_TYPE_NORMAL 0 /* not used */
-#define TTY_DRIVER_TYPE_BAYCOM 6
-
-/*
- * ser12 options:
- * BAYCOM_OPTIONS_SOFTDCD: if undefined, you must use the transmitters
- * hardware carrier detect circuitry, the driver will report DCD as soon as
- * there are transitions on the input line. Advantage: lower interrupt load
- * on the system. Disadvantage: slower, since hardware carrier detect
- * circuitry is usually slow.
- */
-
-#define BUFLEN_RX 8192
-#define BUFLEN_TX 8192
-
-#define NR_PORTS 4
-
-#define KISS_VERBOSE
-
-#define BAYCOM_MAGIC 0x3105bac0
-
-/* --------------------------------------------------------------------- */
-
-/*
- * user settable parameters (from the command line)
- */
-#ifndef MODULE
-static
-#endif /* MODULE */
-int major = BAYCOM_MAJOR;
-
-/* --------------------------------------------------------------------- */
-
-static struct tty_struct *baycom_table[NR_PORTS];
-static struct termios *baycom_termios[NR_PORTS];
-static struct termios *baycom_termios_locked[NR_PORTS];
-
-static int baycom_refcount;
-
-static struct tty_driver baycom_driver;
-
-static struct {
- int modem, iobase, irq, options;
-} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, };
-
-/* --------------------------------------------------------------------- */
-
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define SER12_EXTENT 8
-
-#define LPT_DATA(iobase) (iobase+0)
-#define LPT_STATUS(iobase) (iobase+1)
-#define LPT_CONTROL(iobase) (iobase+2)
-#define LPT_IRQ_ENABLE 0x10
-#define PAR96_BURSTBITS 16
-#define PAR96_BURST 4
-#define PAR96_PTT 2
-#define PAR96_TXBIT 1
-#define PAR96_ACK 0x40
-#define PAR96_RXBIT 0x20
-#define PAR96_DCD 0x10
-#define PAR97_POWER 0xf8
-
-#define PAR96_EXTENT 3
-
-/* ---------------------------------------------------------------------- */
-
-struct access_params {
- int tx_delay;
- int tx_tail;
- int slottime;
- int ppersist;
- int fulldup;
-};
-
-struct hdlc_state_rx {
- int rx_state; /* 0 = sync hunt, != 0 receiving */
- unsigned int bitstream;
- unsigned int bitbuf;
- int numbits;
- unsigned int shreg1, shreg2;
-
- int len;
- unsigned char *bp;
- unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */
-};
-
-struct hdlc_state_tx {
- /*
- * 0 = send flags
- * 1 = send txtail (flags)
- * 2 = send packet
- */
- int tx_state;
- int numflags;
- unsigned int bitstream;
- unsigned int current_byte;
- unsigned char ptt;
-
- unsigned int bitbuf;
- int numbits;
- unsigned int shreg1, shreg2;
-
- int len;
- unsigned char *bp;
- unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */
-};
-
-struct modem_state_ser12 {
- unsigned char last_sample;
- unsigned char interm_sample;
- unsigned int bit_pll;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
- unsigned char last_rxbit;
- unsigned char tx_bit;
-};
-
-struct modem_state_par96 {
- int dcd_count;
- unsigned int dcd_shreg;
- unsigned long descram;
- unsigned long scram;
-};
-
-struct modem_state {
- unsigned char dcd;
- short arb_divider;
- unsigned char flags;
- struct modem_state_ser12 ser12;
- struct modem_state_par96 par96;
-};
-
-struct packet_buffer {
- unsigned int rd;
- unsigned int wr;
-
- unsigned int buflen;
- unsigned char *buffer;
-};
-
-struct packet_hdr {
- unsigned int next;
- unsigned int len;
- /* packet following */
-};
-
-#ifdef BAYCOM_DEBUG
-struct bit_buffer {
- unsigned int rd;
- unsigned int wr;
- unsigned int shreg;
- unsigned char buffer[64];
-};
-
-struct debug_vals {
- unsigned long last_jiffies;
- unsigned cur_intcnt;
- unsigned last_intcnt;
- int cur_pllcorr;
- int last_pllcorr;
-};
-#endif /* BAYCOM_DEBUG */
-
-struct kiss_decode {
- unsigned char dec_state; /* 0 = hunt FEND */
- unsigned char escaped;
- unsigned char pkt_buf[BAYCOM_MAXFLEN+1];
- unsigned int wr;
-};
-
-/* ---------------------------------------------------------------------- */
-
-struct baycom_state {
- int magic;
-
- unsigned char modem_type;
-
- unsigned int iobase;
- unsigned int irq;
- unsigned int options;
-
- int opened;
- struct tty_struct *tty;
-
-#ifdef BAYCOM_USE_BH
- struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate;
-#endif /* BAYCOM_USE_BH */
-
- struct packet_buffer rx_buf;
- struct packet_buffer tx_buf;
-
- struct access_params ch_params;
-
- struct hdlc_state_rx hdlc_rx;
- struct hdlc_state_tx hdlc_tx;
-
- int calibrate;
-
- struct modem_state modem;
-
-#ifdef BAYCOM_DEBUG
- struct bit_buffer bitbuf_channel;
- struct bit_buffer bitbuf_hdlc;
-
- struct debug_vals debug_vals;
-#endif /* BAYCOM_DEBUG */
-
- struct kiss_decode kiss_decode;
-
- struct baycom_statistics stat;
-};
-
-/* --------------------------------------------------------------------- */
-
-struct baycom_state baycom_state[NR_PORTS];
-
-#ifdef BAYCOM_USE_BH
-DECLARE_TASK_QUEUE(tq_baycom);
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-
-/*
- * the CRC routines are stolen from WAMPES
- * by Dieter Deyke
- */
-
-static const unsigned short crc_ccitt_table[] = {
- 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-
-/*---------------------------------------------------------------------------*/
-
-static inline void append_crc_ccitt(unsigned char *buffer, int len)
-{
- unsigned int crc = 0xffff;
-
- for (;len>0;len--)
- crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
- crc ^= 0xffff;
- *buffer++ = crc;
- *buffer++ = crc >> 8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static inline int check_crc_ccitt(const unsigned char *buf,int cnt)
-{
- unsigned int crc = 0xffff;
-
- for (; cnt > 0; cnt--)
- crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
- return (crc & 0xffff) == 0xf0b8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-#if 0
-static int calc_crc_ccitt(const unsigned char *buf,int cnt)
-{
- unsigned int crc = 0xffff;
-
- for (; cnt > 0; cnt--)
- crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
- crc ^= 0xffff;
- return (crc & 0xffff);
-}
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-static int store_packet(struct packet_buffer *buf, unsigned char *data,
- char from_user, unsigned int len)
-{
- unsigned int free;
- struct packet_hdr *hdr;
- unsigned int needed = sizeof(struct packet_hdr)+len;
-
- free = buf->rd-buf->wr;
- if(buf->rd <= buf->wr) {
- free = buf->buflen - buf->wr;
- if((free < needed) && (buf->rd >= needed)) {
- hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
- hdr->next = 0;
- hdr->len = 0;
- buf->wr = 0;
- free = buf->rd;
- }
- }
- if(free < needed) return 0; /* buffer overrun */
- hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
- if (from_user)
- copy_from_user(hdr+1,data,len);
- else
- memcpy(hdr+1,data,len);
- hdr->len = len;
- hdr->next = buf->wr+needed;
- if (hdr->next + sizeof(struct packet_hdr) >= buf->buflen)
- hdr->next = 0;
- buf->wr = hdr->next;
- return 1;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void get_packet(struct packet_buffer *buf, unsigned char **data,
- unsigned int *len)
-{
- struct packet_hdr *hdr;
-
- *data = NULL;
- *len = 0;
- if (buf->rd == buf->wr)
- return;
- hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
- while (!(hdr->len)) {
- buf->rd = hdr->next;
- if (buf->rd == buf->wr)
- return;
- hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
- }
- *data = (unsigned char *)(hdr+1);
- *len = hdr->len;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void ack_packet(struct packet_buffer *buf)
-{
- struct packet_hdr *hdr;
-
- if (buf->rd == buf->wr)
- return;
- hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
- buf->rd = hdr->next;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int store_kiss_packet(struct packet_buffer *buf, unsigned char *data,
- unsigned int len)
-{
- unsigned char *bp = data;
- int ln = len;
- /*
- * variables of buf
- */
- unsigned int rd;
- unsigned int wr;
- unsigned int buflen;
- unsigned char *buffer;
-
- if (!len || !data || !buf)
- return 0;
- buflen = buf->buflen;
- rd = buf->rd;
- wr = buf->wr;
- buffer = buf->buffer;
-
-#define ADD_CHAR(c) {\
- buffer[wr++] = c;\
- if (wr >= buflen) wr = 0;\
- if (wr == rd) return 0;\
- }
-#define ADD_KISSCHAR(c) {\
- if (((c) & 0xff) == KISS_FEND) {\
- ADD_CHAR(KISS_FESC);\
- ADD_CHAR(KISS_TFEND);\
- } else if (((c) & 0xff) == KISS_FESC) {\
- ADD_CHAR(KISS_FESC);\
- ADD_CHAR(KISS_TFESC);\
- } else {\
- ADD_CHAR(c);\
- }\
- }
-
- ADD_CHAR(KISS_FEND);
- ADD_KISSCHAR(KISS_CMD_DATA);
- for(; ln > 0; ln--,bp++) {
- ADD_KISSCHAR(*bp);
- }
- ADD_CHAR(KISS_FEND);
- buf->wr = wr;
-#undef ADD_CHAR
-#undef ADD_KISSCHAR
- return 1;
-}
-
-/* ---------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit)
-{
- unsigned char new;
-
- if (!buf) return;
- new = buf->shreg & 1;
- buf->shreg >>= 1;
- if (bit)
- buf->shreg |= 0x80;
- if (new) {
- buf->buffer[buf->wr] = buf->shreg;
- buf->wr = (buf->wr+1) % sizeof(buf->buffer);
- buf->shreg = 0x80;
- }
-}
-
-static inline void add_bitbuffer_word(struct bit_buffer * buf,
- unsigned int bits)
-{
- buf->buffer[buf->wr] = bits & 0xff;
- buf->wr = (buf->wr+1) % sizeof(buf->buffer);
- buf->buffer[buf->wr] = (bits >> 8) & 0xff;
- buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-
-}
-#endif /* BAYCOM_DEBUG */
-
-/* ---------------------------------------------------------------------- */
-
-static inline unsigned int tenms_to_2flags(struct baycom_state *bc,
- unsigned int tenms)
-{
- switch (bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- return tenms * 3 / 4;
- case BAYCOM_MODEM_PAR96:
- return tenms * 6;
- default:
- return 0;
- }
-}
-
-/* ---------------------------------------------------------------------- */
-/*
- * The HDLC routines
- */
-
-static inline int hdlc_rx_add_bytes(struct baycom_state *bc,
- unsigned int bits, int num)
-{
- int added = 0;
- while (bc->hdlc_rx.rx_state && num >= 8) {
- if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
- bc->hdlc_rx.rx_state = 0;
- return 0;
- }
- *bc->hdlc_rx.bp++ = bits >> (32-num);
- bc->hdlc_rx.len++;
- num -= 8;
- added += 8;
- }
- return added;
-}
-
-static inline void hdlc_rx_flag(struct baycom_state *bc)
-{
- if (bc->hdlc_rx.len < 4)
- return;
- if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len))
- return;
- bc->stat.rx_packets++;
- if (!store_kiss_packet(&bc->rx_buf,
- bc->hdlc_rx.buffer,
- bc->hdlc_rx.len-2))
- bc->stat.rx_bufferoverrun++;
-}
-
-static void hdlc_rx_word(struct baycom_state *bc, unsigned int word)
-{
- int i;
- unsigned int mask1, mask2, mask3, mask4, mask5, mask6;
-
- if (!bc) return;
-
- word &= 0xffff;
-#ifdef BAYCOM_DEBUG
- add_bitbuffer_word(&bc->bitbuf_hdlc, word);
-#endif /* BAYCOM_DEBUG */
- bc->hdlc_rx.bitstream >>= 16;
- bc->hdlc_rx.bitstream |= word << 16;
- bc->hdlc_rx.bitbuf >>= 16;
- bc->hdlc_rx.bitbuf |= word << 16;
- bc->hdlc_rx.numbits += 16;
- for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
- mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff;
- i >= 0;
- i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1,
- mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
- if ((bc->hdlc_rx.bitstream & mask1) == mask1)
- bc->hdlc_rx.rx_state = 0; /* abort received */
- else if ((bc->hdlc_rx.bitstream & mask2) == mask3) {
- /* flag received */
- if (bc->hdlc_rx.rx_state) {
- hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf <<
- (8 + i), bc->hdlc_rx.numbits
- - 8 - i);
- hdlc_rx_flag(bc);
- }
- bc->hdlc_rx.len = 0;
- bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
- bc->hdlc_rx.rx_state = 1;
- bc->hdlc_rx.numbits = i;
- } else if ((bc->hdlc_rx.bitstream & mask4) == mask5) {
- /* stuffed bit */
- bc->hdlc_rx.numbits--;
- bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) |
- ((bc->hdlc_rx.bitbuf & mask6) << 1);
- }
- }
- bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf,
- bc->hdlc_rx.numbits);
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned int hdlc_tx_word(struct baycom_state *bc)
-{
- unsigned int mask1, mask2, mask3;
- int i;
-
- if (!bc || !bc->hdlc_tx.ptt)
- return 0;
- for (;;) {
- if (bc->hdlc_tx.numbits >= 16) {
- unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff;
- bc->hdlc_tx.bitbuf >>= 16;
- bc->hdlc_tx.numbits -= 16;
- return ret;
- }
- switch (bc->hdlc_tx.tx_state) {
- default:
- bc->hdlc_tx.ptt = 0;
- bc->hdlc_tx.tx_state = 0;
- return 0;
- case 0:
- case 1:
- if (bc->hdlc_tx.numflags) {
- bc->hdlc_tx.numflags--;
- bc->hdlc_tx.bitbuf |=
- 0x7e7e << bc->hdlc_tx.numbits;
- bc->hdlc_tx.numbits += 16;
- break;
- }
- if (bc->hdlc_tx.tx_state == 1) {
- bc->hdlc_tx.ptt = 0;
- return 0;
- }
- get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
- &bc->hdlc_tx.len);
- if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
- bc->hdlc_tx.tx_state = 1;
- bc->hdlc_tx.numflags = tenms_to_2flags
- (bc, bc->ch_params.tx_tail);
- break;
- }
- if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
- bc->hdlc_tx.tx_state = 0;
- bc->hdlc_tx.numflags = 1;
- ack_packet(&bc->tx_buf);
- break;
- }
- memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp,
- bc->hdlc_tx.len);
- ack_packet(&bc->tx_buf);
- bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
- append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len);
- /* the appended CRC */
- bc->hdlc_tx.len += 2;
- bc->hdlc_tx.tx_state = 2;
- bc->hdlc_tx.bitstream = 0;
- bc->stat.tx_packets++;
- break;
- case 2:
- if (!bc->hdlc_tx.len) {
- bc->hdlc_tx.tx_state = 0;
- bc->hdlc_tx.numflags = 1;
- break;
- }
- bc->hdlc_tx.len--;
- bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp <<
- bc->hdlc_tx.numbits;
- bc->hdlc_tx.bitstream >>= 8;
- bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16;
- mask1 = 0x1f000;
- mask2 = 0x10000;
- mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits);
- bc->hdlc_tx.numbits += 8;
- for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1,
- mask3 = (mask3 << 1) | 1) {
- if ((bc->hdlc_tx.bitstream & mask1) != mask1)
- continue;
- bc->hdlc_tx.bitstream &= ~mask2;
- bc->hdlc_tx.bitbuf =
- (bc->hdlc_tx.bitbuf & mask3) |
- ((bc->hdlc_tx.bitbuf &
- (~mask3)) << 1);
- bc->hdlc_tx.numbits++;
- mask3 = (mask3 << 1) | 1;
- }
- break;
- }
- }
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned short random_seed;
-
-static inline unsigned short random_num(void)
-{
- random_seed = 28629 * random_seed + 157;
- return random_seed;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static inline void tx_arbitrate(struct baycom_state *bc)
-{
- unsigned char *bp;
- unsigned int len;
-
- if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd)
- return;
- get_packet(&bc->tx_buf, &bp, &len);
- if (!bp || !len)
- return;
-
- if (!bc->ch_params.fulldup) {
- if ((random_num() % 256) > bc->ch_params.ppersist)
- return;
- }
- bc->hdlc_tx.tx_state = 0;
- bc->hdlc_tx.numflags = tenms_to_2flags(bc, bc->ch_params.tx_delay);
- bc->hdlc_tx.numbits = bc->hdlc_tx.bitbuf = bc->hdlc_tx.bitstream = 0;
- bc->hdlc_tx.ptt = 1;
- bc->stat.ptt_keyed++;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static void inline baycom_int_freq(struct baycom_state *bc)
-{
- unsigned long cur_jiffies = jiffies;
- /*
- * measure the interrupt frequency
- */
- bc->debug_vals.cur_intcnt++;
- if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
- bc->debug_vals.last_jiffies = cur_jiffies;
- bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
- bc->debug_vals.cur_intcnt = 0;
- bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
- bc->debug_vals.cur_pllcorr = 0;
- }
-}
-#endif /* BAYCOM_DEBUG */
-
-/* --------------------------------------------------------------------- */
-
-static inline void rx_chars_to_flip(struct baycom_state *bc)
-{
- int flip_free;
- unsigned int cnt;
- unsigned int new_rd;
- unsigned long flags;
-
- if ((!bc) || (!bc->tty) || (bc->tty->flip.count >= TTY_FLIPBUF_SIZE) ||
- (bc->rx_buf.rd == bc->rx_buf.wr) ||
- (!bc->tty->flip.char_buf_ptr) ||
- (!bc->tty->flip.flag_buf_ptr))
- return;
- for(;;) {
- flip_free = TTY_FLIPBUF_SIZE - bc->tty->flip.count;
- if (bc->rx_buf.rd <= bc->rx_buf.wr)
- cnt = bc->rx_buf.wr - bc->rx_buf.rd;
- else
- cnt = bc->rx_buf.buflen - bc->rx_buf.rd;
- if ((flip_free <= 0) || (!cnt)) {
- tty_schedule_flip(bc->tty);
- return;
- }
- if (cnt > flip_free)
- cnt = flip_free;
- save_flags(flags); cli();
- memcpy(bc->tty->flip.char_buf_ptr, bc->rx_buf.buffer+bc->rx_buf.rd, cnt);
- memset(bc->tty->flip.flag_buf_ptr, TTY_NORMAL, cnt);
- bc->tty->flip.count += cnt;
- bc->tty->flip.char_buf_ptr += cnt;
- bc->tty->flip.flag_buf_ptr += cnt;
- restore_flags(flags);
- new_rd = bc->rx_buf.rd+cnt;
- if (new_rd >= bc->rx_buf.buflen)
- new_rd -= bc->rx_buf.buflen;
- bc->rx_buf.rd = new_rd;
- }
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== SER12 specific routines =========================
- */
-
-static void inline ser12_set_divisor(struct baycom_state *bc,
- unsigned char divisor)
-{
- outb(0x81, LCR(bc->iobase)); /* DLAB = 1 */
- outb(divisor, DLL(bc->iobase));
- outb(0, DLM(bc->iobase));
- outb(0x01, LCR(bc->iobase)); /* word length = 6 */
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * must call the TX arbitrator every 10ms
- */
-#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
- 36 : 24)
-#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
- 240 : 12)
-
-static void baycom_ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct baycom_state *bc = (struct baycom_state *)dev_id;
- unsigned char cur_s;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
- /*
- * make sure the next interrupt is generated;
- * 0 must be used to power the modem; the modem draws its
- * power from the TxD line
- */
- outb(0x00, THR(bc->iobase));
- rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
- baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
- /*
- * check if transmitter active
- */
- if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
- ser12_set_divisor(bc, 12); /* one interrupt per channel bit */
- /*
- * first output the last bit (!) then call HDLC transmitter,
- * since this may take quite long
- */
- outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase));
- if (bc->hdlc_tx.shreg1 <= 1) {
- if (bc->calibrate > 0) {
- bc->hdlc_tx.shreg1 = 0x10000;
- bc->calibrate--;
- } else {
-#ifdef BAYCOM_USE_BH
- bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
- bc->hdlc_tx.shreg2 = 0;
- queue_task_irq_off(&bc->tq_transmitter,
- &tq_baycom);
- mark_bh(BAYCOM_BH);
-#ifdef HDLC_LOOPBACK
- bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
- queue_task_irq_off(&bc->tq_receiver,
- &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#else /* BAYCOM_USE_BH */
- bc->hdlc_tx.shreg1 = hdlc_tx_word(bc)
- | 0x10000;
-#ifdef HDLC_LOOPBACK
- hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
-#endif /* HDLC_LOOPBACK */
-#endif /* BAYCOM_USE_BH */
- }
- }
- if (!(bc->hdlc_tx.shreg1 & 1))
- bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
- bc->hdlc_tx.shreg1 >>= 1;
- return;
- }
- /*
- * do demodulator
- */
- outb(0x0d, MCR(bc->iobase)); /* transmitter off */
- cur_s = inb(MSR(bc->iobase)) & 0x10; /* the CTS line */
-#ifdef BAYCOM_DEBUG
- add_bitbuffer(&bc->bitbuf_channel, cur_s);
-#endif /* BAYCOM_DEBUG */
- bc->modem.ser12.dcd_shreg <<= 1;
- if(cur_s != bc->modem.ser12.last_sample) {
- bc->modem.ser12.dcd_shreg |= 1;
-
- if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
- unsigned int dcdspos, dcdsneg;
-
- dcdspos = dcdsneg = 0;
- dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
- if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
- dcdspos += 2;
- dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
- dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
- dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
-
- bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
- } else
- bc->modem.ser12.dcd_sum0--;
- }
- bc->modem.ser12.last_sample = cur_s;
- if(!bc->modem.ser12.dcd_time) {
- bc->modem.dcd = (bc->modem.ser12.dcd_sum0 +
- bc->modem.ser12.dcd_sum1 +
- bc->modem.ser12.dcd_sum2) < 0;
- bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
- bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
- /* offset to ensure DCD off on silent input */
- bc->modem.ser12.dcd_sum0 = 2;
- bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
- }
- bc->modem.ser12.dcd_time--;
- if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
- /*
- * PLL code for the improved software DCD algorithm
- */
- if (bc->modem.ser12.interm_sample) {
- /*
- * intermediate sample; set timing correction to normal
- */
- ser12_set_divisor(bc, 4);
- } else {
- /*
- * do PLL correction and call HDLC receiver
- */
- switch (bc->modem.ser12.dcd_shreg & 7) {
- case 1: /* transition too late */
- ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
- break;
- case 4: /* transition too early */
- ser12_set_divisor(bc, 3);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
- break;
- default:
- ser12_set_divisor(bc, 4);
- break;
- }
- bc->hdlc_rx.shreg1 >>= 1;
- if (bc->modem.ser12.last_sample ==
- bc->modem.ser12.last_rxbit)
- bc->hdlc_rx.shreg1 |= 0x10000;
- bc->modem.ser12.last_rxbit =
- bc->modem.ser12.last_sample;
- }
- if (++bc->modem.ser12.interm_sample >= 3)
- bc->modem.ser12.interm_sample = 0;
- } else {
- /*
- * PLL algorithm for the hardware squelch DCD algorithm
- */
- if (bc->modem.ser12.interm_sample) {
- /*
- * intermediate sample; set timing correction to normal
- */
- ser12_set_divisor(bc, 6);
- } else {
- /*
- * do PLL correction and call HDLC receiver
- */
- switch (bc->modem.ser12.dcd_shreg & 3) {
- case 1: /* transition too late */
- ser12_set_divisor(bc, 7);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
- break;
- case 2: /* transition too early */
- ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
- break;
- default:
- ser12_set_divisor(bc, 6);
- break;
- }
- bc->hdlc_rx.shreg1 >>= 1;
- if (bc->modem.ser12.last_sample ==
- bc->modem.ser12.last_rxbit)
- bc->hdlc_rx.shreg1 |= 0x10000;
- bc->modem.ser12.last_rxbit =
- bc->modem.ser12.last_sample;
- }
- bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
- }
- if (bc->hdlc_rx.shreg1 & 1) {
-#ifdef BAYCOM_USE_BH
- bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000;
- queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
- mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
- hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1);
-#endif /* BAYCOM_USE_BH */
- bc->hdlc_rx.shreg1 = 0x10000;
- }
- if (--bc->modem.arb_divider <= 0) {
-#ifdef BAYCOM_USE_BH
- queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
- mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
- tx_arbitrate(bc);
-#endif /* BAYCOM_USE_BH */
- bc->modem.arb_divider = bc->ch_params.slottime *
- SER12_ARB_DIVIDER(bc);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
- c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
- { "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart ser12_check_uart(unsigned int iobase)
-{
- unsigned char b1,b2,b3;
- enum uart u;
- enum uart uart_tab[] =
- { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
- b1 = inb(MCR(iobase));
- outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
- b2 = inb(MSR(iobase));
- outb(0x1a, MCR(iobase));
- b3 = inb(MSR(iobase)) & 0xf0;
- outb(b1, MCR(iobase)); /* restore old values */
- outb(b2, MSR(iobase));
- if (b3 != 0x90)
- return c_uart_unknown;
- inb(RBR(iobase));
- inb(RBR(iobase));
- outb(0x01, FCR(iobase)); /* enable FIFOs */
- u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
- if (u == c_uart_16450) {
- outb(0x5a, SCR(iobase));
- b1 = inb(SCR(iobase));
- outb(0xa5, SCR(iobase));
- b2 = inb(SCR(iobase));
- if ((b1 != 0x5a) || (b2 != 0xa5))
- u = c_uart_8250;
- }
- return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_allocate_resources(unsigned int iobase, unsigned int irq,
- unsigned int options)
-{
- enum uart u;
-
- if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
- return -ENXIO;
- if (check_region(iobase, SER12_EXTENT))
- return -EACCES;
- if ((u = ser12_check_uart(iobase)) == c_uart_unknown)
- return -EIO;
- request_region(iobase, SER12_EXTENT, "baycom_ser12");
- outb(0, FCR(iobase)); /* disable FIFOs */
- outb(0x0d, MCR(iobase));
- printk(KERN_INFO "baycom: ser12 at iobase 0x%x irq %u options 0x%x "
- "uart %s\n", iobase, irq, options, uart_str[u]);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_deallocate_resources(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
- return;
- /*
- * disable interrupts
- */
- outb(0, IER(bc->iobase));
- outb(1, MCR(bc->iobase));
- /*
- * this should prevent kernel: Trying to free IRQx
- * messages
- */
- if (bc->opened > 0)
- free_irq(bc->irq, bc);
- release_region(bc->iobase, SER12_EXTENT);
- bc->modem_type = BAYCOM_MODEM_INVALID;
- printk(KERN_INFO "baycom: release ser12 at iobase 0x%x irq %u\n",
- bc->iobase, bc->irq);
- bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_on_open(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
- return -ENXIO;
- /*
- * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
- * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
- * depending on the usage of the software DCD routine
- */
- ser12_set_divisor(bc, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
- outb(0x0d, MCR(bc->iobase));
- outb(0, IER(bc->iobase));
- if (request_irq(bc->irq, baycom_ser12_interrupt, SA_INTERRUPT,
- "baycom_ser12", bc))
- return -EBUSY;
- /*
- * enable transmitter empty interrupt
- */
- outb(2, IER(bc->iobase));
- /*
- * the value here serves to power the modem
- */
- outb(0x00, THR(bc->iobase));
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_on_close(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
- return;
- /*
- * disable interrupts
- */
- outb(0, IER(bc->iobase));
- outb(1, MCR(bc->iobase));
- free_irq(bc->irq, bc);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== PAR96 specific routines =========================
- */
-
-#define PAR96_DESCRAM_TAP1 0x20000
-#define PAR96_DESCRAM_TAP2 0x01000
-#define PAR96_DESCRAM_TAP3 0x00001
-
-#define PAR96_DESCRAM_TAPSH1 17
-#define PAR96_DESCRAM_TAPSH2 12
-#define PAR96_DESCRAM_TAPSH3 0
-
-#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
-#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- register struct baycom_state *bc = (struct baycom_state *)dev_id;
- int i;
- unsigned int data, descx, mask, mask2;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
-
- rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
- baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
- /*
- * check if transmitter active
- */
- if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
- /*
- * first output the last 16 bits (!) then call HDLC
- * transmitter, since this may take quite long
- * do the differential encoder and the scrambler on the fly
- */
- data = bc->hdlc_tx.shreg1;
- for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
- unsigned char val = PAR97_POWER;
- bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
- (bc->modem.par96.scram & 1));
- if (!(data & 1))
- bc->modem.par96.scram ^= 1;
- if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
- bc->modem.par96.scram ^=
- (PAR96_SCRAM_TAPN << 1);
- if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
- val |= PAR96_TXBIT;
- outb(val, LPT_DATA(bc->iobase));
- outb(val | PAR96_BURST, LPT_DATA(bc->iobase));
- }
- if (bc->calibrate > 0) {
- bc->hdlc_tx.shreg1 = 0x10000;
- bc->calibrate--;
- } else {
-#ifdef BAYCOM_USE_BH
- bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
- bc->hdlc_tx.shreg2 = 0;
- queue_task_irq_off(&bc->tq_transmitter, &tq_baycom);
- mark_bh(BAYCOM_BH);
-#ifdef HDLC_LOOPBACK
- bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
- queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#else /* BAYCOM_USE_BH */
- bc->hdlc_tx.shreg1 = hdlc_tx_word(bc);
-#ifdef HDLC_LOOPBACK
- hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
-#endif /* HDLC_LOOPBACK */
-#endif /* BAYCOM_USE_BH */
- }
- return;
- }
- /*
- * do receiver; differential decode and descramble on the fly
- */
- for(data = i = 0; i < PAR96_BURSTBITS; i++) {
- bc->modem.par96.descram = (bc->modem.par96.descram << 1);
- if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT)
- bc->modem.par96.descram |= 1;
- descx = bc->modem.par96.descram ^
- (bc->modem.par96.descram >> 1);
- /* now the diff decoded data is inverted in descram */
- outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase));
- descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
- (descx >> PAR96_DESCRAM_TAPSH2));
- data >>= 1;
- if (!(descx & 1))
- data |= 0x8000;
- outb(PAR97_POWER | PAR96_PTT | PAR96_BURST,
- LPT_DATA(bc->iobase));
- }
-#ifdef BAYCOM_USE_BH
- bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1;
- bc->hdlc_rx.shreg1 = data | 0x10000;
- queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
- mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
- hdlc_rx_word(bc, data);
-#endif /* BAYCOM_USE_BH */
- /*
- * do DCD algorithm
- */
- if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
- bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
- | (data << 16);
- /* search for flags and set the dcd counter appropriately */
- for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
- i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
- if ((bc->modem.par96.dcd_shreg & mask) == mask2)
- bc->modem.par96.dcd_count = BAYCOM_MAXFLEN+4;
- /* check for abort/noise sequences */
- for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
- i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
- if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
- (bc->modem.par96.dcd_count >= 0))
- bc->modem.par96.dcd_count -= BAYCOM_MAXFLEN-10;
- /* decrement and set the dcd variable */
- if (bc->modem.par96.dcd_count >= 0)
- bc->modem.par96.dcd_count -= 2;
- bc->modem.dcd = bc->modem.par96.dcd_count > 0;
- } else {
- bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase))
- & PAR96_DCD);
- }
- if (--bc->modem.arb_divider <= 0) {
-#ifdef BAYCOM_USE_BH
- queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
- mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
- tx_arbitrate(bc);
-#endif /* BAYCOM_USE_BH */
- bc->modem.arb_divider = bc->ch_params.slottime * 6;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_check_lpt(unsigned int iobase)
-{
- unsigned char b1,b2;
- int i;
-
- b1 = inb(LPT_DATA(iobase));
- b2 = inb(LPT_CONTROL(iobase));
- outb(0xaa, LPT_DATA(iobase));
- i = inb(LPT_DATA(iobase)) == 0xaa;
- outb(0x55, LPT_DATA(iobase));
- i &= inb(LPT_DATA(iobase)) == 0x55;
- outb(0x0a, LPT_CONTROL(iobase));
- i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
- outb(0x05, LPT_CONTROL(iobase));
- i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
- outb(b1, LPT_DATA(iobase));
- outb(b2, LPT_CONTROL(iobase));
- return !i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_allocate_resources(unsigned int iobase, unsigned int irq,
- unsigned int options)
-{
- if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
- return -ENXIO;
- if (check_region(iobase, PAR96_EXTENT))
- return -EACCES;
- if (par96_check_lpt(iobase))
- return -EIO;
- request_region(iobase, PAR96_EXTENT, "baycom_par96");
- outb(0, LPT_CONTROL(iobase)); /* disable interrupt */
- outb(PAR96_PTT | PAR97_POWER, LPT_DATA(iobase)); /* switch off PTT */
- printk(KERN_INFO "baycom: par96 at iobase 0x%x irq %u options 0x%x\n",
- iobase, irq, options);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_deallocate_resources(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
- return;
- outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */
- outb(PAR96_PTT, LPT_DATA(bc->iobase)); /* switch off PTT */
- /*
- * this should prevent kernel: Trying to free IRQx
- * messages
- */
- if (bc->opened > 0)
- free_irq(bc->irq, bc);
- release_region(bc->iobase, PAR96_EXTENT);
- bc->modem_type = BAYCOM_MODEM_INVALID;
- printk(KERN_INFO "baycom: release par96 at iobase 0x%x irq %u\n",
- bc->iobase, bc->irq);
- bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_on_open(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
- return -ENXIO;
- outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */
- /* switch off PTT */
- outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
- if (request_irq(bc->irq, baycom_par96_interrupt, SA_INTERRUPT,
- "baycom_par96", bc))
- return -EBUSY;
- outb(LPT_IRQ_ENABLE, LPT_CONTROL(bc->iobase)); /* enable interrupt */
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_on_close(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
- return;
- outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */
- /* switch off PTT */
- outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
- free_irq(bc->irq, bc);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== Bottom half (soft interrupt) ====================
- */
-
-#ifdef BAYCOM_USE_BH
-static void bh_receiver(void *private)
-{
- struct baycom_state *bc = (struct baycom_state *)private;
- unsigned int temp;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
- if (!bc->hdlc_rx.shreg2)
- return;
- temp = bc->hdlc_rx.shreg2;
- bc->hdlc_rx.shreg2 = 0;
- hdlc_rx_word(bc, temp);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void bh_transmitter(void *private)
-{
- struct baycom_state *bc = (struct baycom_state *)private;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
- if (bc->hdlc_tx.shreg2)
- return;
- bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void bh_arbitrate(void *private)
-{
- struct baycom_state *bc = (struct baycom_state *)private;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
- tx_arbitrate(bc);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_bottom_half(void)
-{
- run_task_queue(&tq_baycom);
-}
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== TTY interface routines ==========================
- */
-
-static inline int baycom_paranoia_check(struct baycom_state *bc,
- const char *routine)
-{
- if (!bc || bc->magic != BAYCOM_MAGIC) {
- printk(KERN_ERR "baycom: bad magic number for baycom struct "
- "in routine %s\n", routine);
- return 1;
- }
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * Here the tty driver code starts
- */
-
-static void baycom_put_fend(struct baycom_state *bc)
-{
- if (bc->kiss_decode.wr <= 0 ||
- (bc->kiss_decode.pkt_buf[0] & 0xf0) != 0)
- return;
-
- switch (bc->kiss_decode.pkt_buf[0] & 0xf) {
- case KISS_CMD_DATA:
- if (bc->kiss_decode.wr <= 8)
- break;
- if (!store_packet(&bc->tx_buf, bc->kiss_decode.pkt_buf+1, 0,
- bc->kiss_decode.wr-1))
- bc->stat.tx_bufferoverrun++;
- break;
-
- case KISS_CMD_TXDELAY:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.tx_delay = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: TX delay = %ums\n",
- bc->ch_params.tx_delay * 10);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_PPERSIST:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: p-persistence = %u\n",
- bc->ch_params.ppersist);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_SLOTTIME:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: slottime = %ums\n",
- bc->ch_params.slottime * 10);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_TXTAIL:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.tx_tail = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: TX tail = %ums\n",
- bc->ch_params.tx_tail * 10);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_FULLDUP:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.fulldup = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: %s duplex\n",
- bc->ch_params.fulldup ? "full" : "half");
-#endif /* KISS_VERBOSE */
- break;
-
- default:
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: unhandled KISS packet code %u\n",
- bc->kiss_decode.pkt_buf[0] & 0xf);
-#endif /* KISS_VERBOSE */
- break;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_put_char(struct tty_struct *tty, unsigned char ch)
-{
- struct baycom_state *bc;
-
- if (!tty)
- return;
- if (baycom_paranoia_check(bc = tty->driver_data, "put_char"))
- return;
-
- if (ch == KISS_FEND) {
- baycom_put_fend(bc);
- bc->kiss_decode.wr = 0;
- bc->kiss_decode.escaped = 0;
- bc->kiss_decode.dec_state = 1;
- return;
- }
- if (!bc->kiss_decode.dec_state)
- return;
- if (ch == KISS_FESC) {
- bc->kiss_decode.escaped = 1;
- return;
- }
- if (bc->kiss_decode.wr >= sizeof(bc->kiss_decode.pkt_buf)) {
- bc->kiss_decode.wr = 0;
- bc->kiss_decode.dec_state = 0;
- return;
- }
- if (bc->kiss_decode.escaped) {
- if (ch == KISS_TFEND)
- bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] =
- KISS_FEND;
- else if (ch == KISS_TFESC)
- bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] =
- KISS_FESC;
- else {
- bc->kiss_decode.wr = 0;
- bc->kiss_decode.dec_state = 0;
- }
- bc->kiss_decode.escaped = 0;
- return;
- }
- bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = ch;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- int c;
- const unsigned char *bp;
- struct baycom_state *bc;
-
- if (!tty || !buf || count <= 0)
- return count;
-
- if (baycom_paranoia_check(bc = tty->driver_data, "write"))
- return count;
-
- if (from_user) {
- for(c = count, bp = buf; c > 0; c--,bp++)
- baycom_put_char(tty, get_user(bp));
- } else {
- for(c = count, bp = buf; c > 0; c--,bp++)
- baycom_put_char(tty, *bp);
- }
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
- wake_up_interruptible(&tty->write_wait);
- return count;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_write_room(struct tty_struct *tty)
-{
- int free;
- struct baycom_state *bc;
-
- if (!tty)
- return 0;
- if (baycom_paranoia_check(bc = tty->driver_data, "write_room"))
- return 0;
-
- free = bc->tx_buf.rd - bc->tx_buf.wr;
- if (free <= 0) {
- free = bc->tx_buf.buflen - bc->tx_buf.wr;
- if (free < bc->tx_buf.rd)
- free = bc->tx_buf.rd; /* we may fold */
- }
-
- return free / 2; /* a rather pessimistic estimate */
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_chars_in_buffer(struct tty_struct *tty)
-{
- int cnt;
- struct baycom_state *bc;
-
- if (!tty)
- return 0;
- if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer"))
- return 0;
-
- cnt = bc->tx_buf.wr - bc->tx_buf.rd;
- if (cnt < 0)
- cnt += bc->tx_buf.buflen;
-
- return cnt;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_flush_buffer(struct tty_struct *tty)
-{
- struct baycom_state *bc;
-
- if (!tty)
- return;
- if (baycom_paranoia_check(bc = tty->driver_data, "flush_buffer"))
- return;
-
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void baycom_dealloc_hw(struct baycom_state *bc)
-{
- if (!bc || bc->magic != BAYCOM_MAGIC ||
- bc->modem_type == BAYCOM_MODEM_INVALID)
- return;
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- ser12_deallocate_resources(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- par96_deallocate_resources(bc);
- break;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_set_hardware(struct baycom_state *bc,
- unsigned int modem_type, unsigned int iobase,
- unsigned int irq, unsigned int options)
-{
- int i;
-
- if (!bc)
- return -EINVAL;
-
- if (modem_type == BAYCOM_MODEM_SER12) {
- i = ser12_allocate_resources(iobase, irq, options);
- if (i < 0)
- return i;
- } else if (modem_type == BAYCOM_MODEM_PAR96) {
- i = par96_allocate_resources(iobase, irq, options);
- if (i < 0)
- return i;
- } else if (modem_type == BAYCOM_MODEM_INVALID) {
- iobase = irq = options = 0;
- } else {
- return -ENXIO;
- }
- baycom_dealloc_hw(bc);
- bc->modem_type = modem_type;
- bc->iobase = iobase;
- bc->irq = irq;
- bc->options = options;
- i = 0;
- if (bc->opened > 0) {
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- i = ser12_on_open(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- i = par96_on_open(bc);
- break;
- }
- }
- return i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- int i;
- struct baycom_state *bc;
- struct baycom_params par;
-
- if (!tty)
- return -EINVAL;
- if (baycom_paranoia_check(bc = tty->driver_data, "ioctl"))
- return -EINVAL;
-
- switch (cmd) {
- default:
- return -ENOIOCTLCMD;
-
- case TIOCMGET:
- i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
- if (i)
- return i;
- i = (bc->modem.dcd ? TIOCM_CAR : 0) |
- (bc->hdlc_tx.ptt ? TIOCM_RTS : 0);
- put_user(i, (int *) arg);
- return 0;
-
- case BAYCOMCTL_GETDCD:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i)
- put_user(bc->modem.dcd, (unsigned char *) arg);
- return i;
-
- case BAYCOMCTL_GETPTT:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i)
- put_user(bc->hdlc_tx.ptt, (unsigned char *) arg);
- return i;
-
- case BAYCOMCTL_PARAM_TXDELAY:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.tx_delay = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_PPERSIST:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.ppersist = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_SLOTTIME:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.slottime = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_TXTAIL:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.tx_tail = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_FULLDUP:
- bc->ch_params.fulldup = arg ? 1 : 0;
- return 0;
-
- case BAYCOMCTL_CALIBRATE:
- bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ?
- 600 : 75);
- return 0;
-
- case BAYCOMCTL_GETPARAMS:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(par));
- if (i)
- return i;
- par.modem_type = bc->modem_type;
- par.iobase = bc->iobase;
- par.irq = bc->irq;
- par.options = bc->options;
- par.tx_delay = bc->ch_params.tx_delay;
- par.tx_tail = bc->ch_params.tx_tail;
- par.slottime = bc->ch_params.slottime;
- par.ppersist = bc->ch_params.ppersist;
- par.fulldup = bc->ch_params.fulldup;
- copy_to_user((void *)arg, &par, sizeof(par));
- return 0;
-
- case BAYCOMCTL_SETPARAMS:
- if (!suser())
- return -EPERM;
- i = verify_area(VERIFY_READ, (void *) arg,
- sizeof(par));
- if (i)
- return i;
- copy_from_user(&par, (void *)arg, sizeof(par));
- printk(KERN_INFO "baycom: changing hardware type: modem %u "
- "iobase 0x%x irq %u options 0x%x\n", par.modem_type,
- par.iobase, par.irq, par.options);
- i = baycom_set_hardware(bc, par.modem_type, par.iobase,
- par.irq, par.options);
- if (i)
- return i;
- bc->ch_params.tx_delay = par.tx_delay;
- bc->ch_params.tx_tail = par.tx_tail;
- bc->ch_params.slottime = par.slottime;
- bc->ch_params.ppersist = par.ppersist;
- bc->ch_params.fulldup = par.fulldup;
- return 0;
-
- case BAYCOMCTL_GETSTAT:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct baycom_statistics));
- if (i)
- return i;
- copy_to_user((void *)arg, &bc->stat,
- sizeof(struct baycom_statistics));
- return 0;
-
-
-#ifdef BAYCOM_DEBUG
- case BAYCOMCTL_GETSAMPLES:
- if (bc->bitbuf_channel.rd == bc->bitbuf_channel.wr)
- return -EAGAIN;
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i) {
- put_user(bc->bitbuf_channel.buffer
- [bc->bitbuf_channel.rd],
- (unsigned char *) arg);
- bc->bitbuf_channel.rd = (bc->bitbuf_channel.rd+1) %
- sizeof(bc->bitbuf_channel.buffer);
- }
- return i;
-
- case BAYCOMCTL_GETBITS:
- if (bc->bitbuf_hdlc.rd == bc->bitbuf_hdlc.wr)
- return -EAGAIN;
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i) {
- put_user(bc->bitbuf_hdlc.buffer[bc->bitbuf_hdlc.rd],
- (unsigned char *) arg);
- bc->bitbuf_hdlc.rd = (bc->bitbuf_hdlc.rd+1) %
- sizeof(bc->bitbuf_hdlc.buffer);
- }
- return i;
-
- case BAYCOMCTL_DEBUG1:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned long));
- if (!i)
- put_user((bc->rx_buf.wr-bc->rx_buf.rd) %
- bc->rx_buf.buflen, (unsigned long *)arg);
- return i;
-
- case BAYCOMCTL_DEBUG2:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned long));
- if (!i)
- put_user(bc->debug_vals.last_intcnt,
- (unsigned long *)arg);
- return i;
-
- case BAYCOMCTL_DEBUG3:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned long));
- if (!i)
- put_user((long)bc->debug_vals.last_pllcorr,
- (long *)arg);
- return i;
-#endif /* BAYCOM_DEBUG */
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-int baycom_open(struct tty_struct *tty, struct file * filp)
-{
- int line;
- struct baycom_state *bc;
- int i;
-
- if(!tty)
- return -ENODEV;
-
- line = MINOR(tty->device) - tty->driver.minor_start;
- if (line < 0 || line >= NR_PORTS)
- return -ENODEV;
- bc = baycom_state+line;
-
- if (bc->opened > 0) {
- bc->opened++;
- MOD_INC_USE_COUNT;
- return 0;
- }
- /*
- * initialise some variables
- */
- bc->calibrate = 0;
-
- /*
- * allocate the buffer space
- */
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buflen = BUFLEN_RX;
- bc->tx_buf.buflen = BUFLEN_TX;
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- bc->rx_buf.buffer = kmalloc(bc->rx_buf.buflen, GFP_KERNEL);
- bc->tx_buf.buffer = kmalloc(bc->tx_buf.buflen, GFP_KERNEL);
- if (!bc->rx_buf.buffer || !bc->tx_buf.buffer) {
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
- bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
- return -ENOMEM;
- }
- /*
- * check if the modem type has been set
- */
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- i = ser12_on_open(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- i = par96_on_open(bc);
- break;
- case BAYCOM_MODEM_INVALID:
- /*
- * may open even if no hardware specified, in order to
- * subsequently allow the BAYCOMCTL_SETPARAMS ioctl
- */
- i = 0;
- break;
- default:
- return -ENODEV;
- }
- if (i)
- return i;
-
- bc->opened++;
- MOD_INC_USE_COUNT;
-
- tty->driver_data = bc;
- bc->tty = tty;
-
- return 0;
-}
-
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_close(struct tty_struct *tty, struct file * filp)
-{
- struct baycom_state *bc;
-
- if(!tty) return;
- if (baycom_paranoia_check(bc = tty->driver_data, "close"))
- return;
-
- MOD_DEC_USE_COUNT;
- bc->opened--;
- if (bc->opened <= 0) {
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- ser12_on_close(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- par96_on_close(bc);
- break;
- }
- tty->driver_data = NULL;
- bc->tty = NULL;
- bc->opened = 0;
- /*
- * free the buffers
- */
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
- bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
- }
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * And now the modules code and kernel interface.
- */
-
-static void init_channel(struct baycom_state *bc)
-{
- struct access_params dflt_ch_params = { 20, 2, 10, 40, 0 };
-
- if (!bc)
- return;
-
- bc->hdlc_rx.rx_state = 0;
-
- bc->hdlc_tx.tx_state = bc->hdlc_tx.numflags = 0;
- bc->hdlc_tx.bitstream = 0;
- bc->hdlc_tx.current_byte = bc->hdlc_tx.ptt = 0;
-
- memset(&bc->modem, 0, sizeof(bc->modem));
-
-#ifdef BAYCOM_DEBUG
- bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0;
- bc->bitbuf_channel.shreg = 0x80;
-
- bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0;
- bc->bitbuf_hdlc.shreg = 0x80;
-#endif /* BAYCOM_DEBUG */
-
- bc->kiss_decode.dec_state = bc->kiss_decode.escaped =
- bc->kiss_decode.wr = 0;
-
- bc->ch_params = dflt_ch_params;
-
-#ifdef BAYCOM_USE_BH
- bc->tq_receiver.next = bc->tq_transmitter.next =
- bc->tq_arbitrate.next = NULL;
- bc->tq_receiver.sync = bc->tq_transmitter.sync =
- bc->tq_arbitrate.sync = 0;
- bc->tq_receiver.data = bc->tq_transmitter.data =
- bc->tq_arbitrate.data = bc;
- bc->tq_receiver.routine = bh_receiver;
- bc->tq_transmitter.routine = bh_transmitter;
- bc->tq_arbitrate.routine = bh_arbitrate;
-#endif /* BAYCOM_USE_BH */
-}
-
-static void init_datastructs(void)
-{
- int i;
-
- for(i = 0; i < NR_PORTS; i++) {
- struct baycom_state *bc = baycom_state+i;
-
- bc->magic = BAYCOM_MAGIC;
- bc->modem_type = BAYCOM_MODEM_INVALID;
- bc->iobase = bc->irq = bc->options = bc->opened = 0;
- bc->tty = NULL;
-
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->rx_buf.buflen = 0;
- bc->rx_buf.buffer = NULL;
-
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- bc->tx_buf.buflen = 0;
- bc->tx_buf.buffer = NULL;
-
- memset(&bc->stat, 0, sizeof(bc->stat));
-
- init_channel(bc);
- }
-}
-
-int baycom_init(void) {
- int i, j;
-
- /*
- * initialize the data structures
- */
- init_datastructs();
- /*
- * initialize bottom half handler
- */
-#ifdef BAYCOM_USE_BH
- init_bh(BAYCOM_BH, baycom_bottom_half);
-#endif /* BAYCOM_USE_BH */
- /*
- * register the driver as tty driver
- */
- memset(&baycom_driver, 0, sizeof(struct tty_driver));
- baycom_driver.magic = TTY_DRIVER_MAGIC;
- baycom_driver.name = "baycom";
- baycom_driver.major = major;
- baycom_driver.minor_start = 0;
- baycom_driver.num = NR_PORTS;
- baycom_driver.type = TTY_DRIVER_TYPE_BAYCOM;
- baycom_driver.subtype = BAYCOM_TYPE_NORMAL;
- baycom_driver.init_termios.c_iflag = 0;
- baycom_driver.init_termios.c_oflag = 0;
- baycom_driver.init_termios.c_cflag = CS8 | B1200 | CREAD | CLOCAL;
- baycom_driver.init_termios.c_lflag = 0;
- baycom_driver.flags = TTY_DRIVER_REAL_RAW;
- baycom_driver.refcount = &baycom_refcount;
- baycom_driver.table = baycom_table;
- baycom_driver.termios = baycom_termios;
- baycom_driver.termios_locked = baycom_termios_locked;
- /*
- * the functions
- */
- baycom_driver.open = baycom_open;
- baycom_driver.close = baycom_close;
- baycom_driver.write = baycom_write;
- baycom_driver.put_char = baycom_put_char;
- baycom_driver.flush_chars = NULL;
- baycom_driver.write_room = baycom_write_room;
- baycom_driver.chars_in_buffer = baycom_chars_in_buffer;
- baycom_driver.flush_buffer = baycom_flush_buffer;
- baycom_driver.ioctl = baycom_ioctl;
- /*
- * cannot throttle the transmitter on this layer
- */
- baycom_driver.throttle = NULL;
- baycom_driver.unthrottle = NULL;
- /*
- * no special actions on termio changes
- */
- baycom_driver.set_termios = NULL;
- /*
- * no XON/XOFF and no hangup on the radio port
- */
- baycom_driver.stop = NULL;
- baycom_driver.start = NULL;
- baycom_driver.hangup = NULL;
- baycom_driver.set_ldisc = NULL;
-
- if (tty_register_driver(&baycom_driver)) {
- printk(KERN_WARNING "baycom: tty_register_driver failed\n");
- return -EIO;
- }
-
- for (i = 0; i < NR_PORTS &&
- baycom_ports[i].modem != BAYCOM_MODEM_INVALID; i++) {
- j = baycom_set_hardware(baycom_state+i,
- baycom_ports[i].modem,
- baycom_ports[i].iobase,
- baycom_ports[i].irq,
- baycom_ports[i].options);
- if (j < 0) {
- const char *s;
- switch (-j) {
- case ENXIO:
- s = "invalid iobase and/or irq";
- break;
- case EACCES:
- s = "io region already used";
- break;
- case EIO:
- s = "no uart/lpt port at iobase";
- break;
- case EBUSY:
- s = "interface already in use";
- break;
- case EINVAL:
- s = "internal error";
- break;
- default:
- s = "unknown error";
- break;
- }
- printk(KERN_WARNING "baycom: modem %u iobase 0x%x "
- "irq %u: (%i) %s\n", baycom_ports[i].modem,
- baycom_ports[i].iobase, baycom_ports[i].irq,
- j, s);
- }
- }
-
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-int modem = BAYCOM_MODEM_INVALID;
-int iobase = 0x3f8;
-int irq = 4;
-int options = BAYCOM_OPTIONS_SOFTDCD;
-
-int init_module(void)
-{
- int i;
-
- printk(KERN_INFO "baycom: init_module called\n");
-
- baycom_ports[0].modem = modem;
- baycom_ports[0].iobase = iobase;
- baycom_ports[0].irq = irq;
- baycom_ports[0].options = options;
- baycom_ports[1].modem = BAYCOM_MODEM_INVALID;
-
- i = baycom_init();
- if (i)
- return i;
-
- printk(KERN_INFO "baycom: version 0.3; "
- "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n");
-
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
- int i;
-
- printk(KERN_INFO "baycom: cleanup_module called\n");
-
- disable_bh(BAYCOM_BH);
- if (tty_unregister_driver(&baycom_driver))
- printk(KERN_WARNING "baycom: failed to unregister tty "
- "driver\n");
- for(i = 0; i < NR_PORTS; i++) {
- struct baycom_state *bc = baycom_state+i;
-
- if (bc->magic != BAYCOM_MAGIC)
- printk(KERN_ERR "baycom: invalid magic in "
- "cleanup_module\n");
- else {
- baycom_dealloc_hw(bc);
- /*
- * free the buffers
- */
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
- bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
- }
- }
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom=modem,io,irq,options[,modem,io,irq,options]
- * modem=1: ser12, modem=2: par96
- * options=0: hardware DCD, options=1: software DCD
- */
-
-void baycom_setup(char *str, int *ints)
-{
- int i;
-
- for (i = 0; i < NR_PORTS; i++)
- if (ints[0] >= 4*i+4) {
- baycom_ports[i].modem = ints[4*i+1];
- baycom_ports[i].iobase = ints[4*i+2];
- baycom_ports[i].irq = ints[4*i+3];
- baycom_ports[i].options = ints[4*i+4];
- } else
- baycom_ports[i].modem = BAYCOM_MODEM_INVALID;
-
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include "tracing.h"
#include "ftape-eof.h"
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/ioctl.h>
#include <linux/mtio.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include "tracing.h"
#include "ftape-read.h"
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/ftape.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include "tracing.h"
#include "ftape-write.h"
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/fs.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/major.h>
#include <linux/symtab_end.h>
};
+#if defined(CONFIG_PROC_FS) && !defined(MODULE)
static struct proc_dir_entry proc_misc = {
0, 4, "misc",
S_IFREG | S_IRUGO, 1, 0, 0,
0, NULL /* ops -- default to array */,
&proc_misc_read /* get_info */,
};
+#endif
int misc_init(void)
{
#endif
#ifdef CONFIG_RISCOM8
riscom8_init();
-#endif
-#ifdef CONFIG_BAYCOM
- baycom_init();
#endif
pty_init();
vcs_init();
*
*/
+#include <asm/uaccess.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
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;
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;
*
*/
+#include <asm/uaccess.h>
#include <linux/config.h>
#define __NO_VERSION__
#include <linux/module.h>
/* TODO: right tbusy handling when using MP */
+#include <asm/uaccess.h>
#include <linux/config.h>
#define __NO_VERSION__
#include <linux/module.h>
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);
{
struct ippp_struct *is;
struct ippp_buf_queue *b;
- int r;
unsigned long flags;
is = file->private_data;
{
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;
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;
*
*/
+#include <asm/uaccess.h>
#define __NO_VERSION__
#include <linux/config.h>
#include <linux/module.h>
*/
#define __NO_VERSION__
#include "teles.h"
+#include <asm/uaccess.h>
extern struct IsdnCard cards[];
extern int nrcards;
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
CONFIG_8390_MODULE :=
CONFIG_SLHC_BUILTIN :=
CONFIG_SLHC_MODULE :=
+CONFIG_HDLCDRV_BUILTIN :=
+CONFIG_HDLCDRV_MODULE :=
ifeq ($(CONFIG_ISDN),y)
ifeq ($(CONFIG_ISDN_PPP),y)
endif
endif
+ifeq ($(CONFIG_BAYCOM),y)
+L_OBJS += baycom.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+ ifeq ($(CONFIG_BAYCOM),m)
+ CONFIG_HDLCDRV_MODULE = y
+ M_OBJS += baycom.o
+ endif
+endif
+
+ifeq ($(CONFIG_SOUNDMODEM),y)
+L_OBJS += soundmodem.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+ ifeq ($(CONFIG_SOUNDMODEM),m)
+ CONFIG_HDLCDRV_MODULE = y
+ M_OBJS += soundmodem.o
+ endif
+endif
+
+# If anything built-in uses the hdlcdrv, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_HDLCDRV_BUILTIN
+LX_OBJS += hdlcdrv.o
+else
+ ifdef CONFIG_HDLCDRV_MODULE
+ MX_OBJS += hdlcdrv.o
+ endif
+endif
+
+
include $(TOPDIR)/Rules.make
clean:
--- /dev/null
+ LINUX DRIVER FOR BAYCOM MODEMS
+
+ Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This document describes the Linux Kernel Driver for simple Baycom style
+amateur radio modems. The driver supports the following modems:
+
+ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
+ of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ is responsible for regenerating the receiver bit clock, as well as
+ for handling the HDLC protocol. The modem connects to a serial port,
+ hence the name. Since the serial port is not used as an async serial
+ port, the kernel driver for serial ports cannot be used, and this
+ driver only supports standard serial hardware (8250, 16450, 16550)
+
+par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ The modem does all the filtering and regenerates the receiver clock.
+ Data is transferred from and to the PC via a shift register.
+ The shift register is filled with 16 bits and an interrupt is signalled.
+ The PC then empties the shift register in a burst. This modem connects
+ to the parallel port, hence the name. The modem leaves the
+ implementation of the HDLC protocol and the scrambler polynomial to
+ the PC.
+
+picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
+ is protocol compatible to par96, but uses only three low power ICs
+ and can therefore be fed from the parallel port and does not require
+ an additional power supply. Furthermore, it incorporates a carrier
+ detect circuitry.
+
+All of the above modems only support half duplex communications. However,
+the driver supports the KISS (see below) fullduplex command. It then simply
+starts to send as soon as there's a packet to transmit and does not care
+about DCD, i.e. it starts to send even if there's someone else on the channel.
+This command is required by some implementations of the DAMA channel
+access protocol.
+
+
+The Interface of the driver
+
+Unlike previous drivers, the driver is no longer a character device,
+but it is now a true kernel network interface. Installation is therefore
+simple. Once installed, four interfaces named bc[0-3] are available.
+sethdlc from the ax25 utilities may be used to set driver states etc.
+Users of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples:
+ insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
+ sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1
+
+Both lines configure the first port to drive a ser12 modem at the first
+serial port (COM1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+
+ insmod baycom modem=2 iobase=0x378 irq=7 options=1
+ sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1
+
+Both lines configure the first port to drive a par96 or par97 modem at the
+first parallel port (LPT1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Hardware DCD versus Software DCD
+
+To avoid collisions on the air, the driver must know when the channel is
+busy. This is the task of the DCD circuitry/software. The driver may either
+utilise a software DCD algorithm (options=1) or use a DCD signal from
+the hardware (options=0).
+
+ser12: if software DCD is utilised, the radio's squelch should always be
+ open. It is highly recommended to use the software DCD algorithm,
+ as it is much faster than most hardware squelch circuitry. The
+ disadvantage is a slightly higher load on the system.
+
+par96: the software DCD algorithm for this type of modem is rather poor.
+ The modem simply does not provide enough information to implement
+ a reasonable DCD algorithm in software. Therefore, if your radio
+ feeds the DCD input of the PAR96 modem, the use of the hardware
+ DCD circuitry is recommended.
+
+picpar: the picpar modem features a builtin DCD hardware, which is highly
+ recommended.
+
+
+
+Compatibility with the rest of the Linux kernel
+
+The serial driver, the line printer (lp) driver and the baycom driver compete
+for the same hardware resources. Of course only one driver can access a given
+interface at a time. The serial driver grabs all interfaces it can find at
+startup time. Therefore the baycom driver subsequently won't be able to
+access a serial port. You might therefore find it necessary to release
+a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
+# is the number of the interface. The baycom driver does not reserve any
+port at startup, unless one is specified on the 'insmod' command line. Another
+method to solve the problem is to compile all three drivers as modules and
+leave it to kerneld to load the correct driver depending on the application.
+
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+hb9jnx @ hb9w.ampr.org
--- /dev/null
+ LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
+
+ Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss)
+compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK
+AX.25 packet radio modems. Only half duplex operation is supported; an
+attempt to include full duplex support failed because the hardware did
+not support it (it appeared that the card only provides one DMA channel,
+although the Codec chip would support two channels). The driver needs
+some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise
+interactive performance of the computer may become sluggish.
+
+
+The Interface of the driver
+
+The driver provides a kernel network drivers named sm[0-3]. sethdlc
+from the ax25 utilities may be used to set driver states etc. Users
+of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Some sound cards need to be initialized before they operate in either
+SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this;
+you may use the standard linux sound driver to initialize the soundcard;
+compile it as a module, and do
+ insmod sound
+ rmmod sound
+The soundcard should then be initialized correctly. If this does not help,
+you'll have to write your own initialization utility.
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples:
+ insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1
+ sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1
+
+Both lines configure the first port to drive a soundblaster card
+in 1200 baud AFSK mode.
+
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Input and output levels
+
+It is important that the input and output levels are adjusted properly.
+There are two utilities, available in the ax25 utilities distribution,
+to facilitate this: smmixer and smdiag. smdiag allows you to display
+the input signal in an oscilloscope like display or an eye diagram.
+smmixer allows you to adjust input/output levels. See the respective
+man pages.
+
+
+Transmitter keying
+
+Since soundcards do not have a DC coupled output; PTT keying options include
+the following:
+* VOX circuitry
+* Serial port pin
+* Parallel port pin
+* MPU401 MIDI output via a retriggerable monoflop.
+Circuit schematics may be found at
+http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html.
+
+
+Compatibility with the rest of the Linux kernel
+
+The sound driver and the soundcard modem driver compete for the same
+hardware resources. Of course only one driver can access a given
+interface at a time. Worse yet, the sound driver grabs the soundcard
+at startup time. Therefore the soundcard modem driver subsequently won't
+be able to access the soundcard. You might therefore find it necessary to
+unload the sound driver before using the soundcard modem driver.
+
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+hb9jnx @ hb9w.ampr.org
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * baycom.c -- baycom ser12 and par96 radio modem driver.
+ *
+ * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please note that the GPL allows you to use the driver, NOT the radio.
+ * In order to use the radio, you need a license from the communications
+ * authority of your country.
+ *
+ *
+ * Supported modems
+ *
+ * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
+ * of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ * is responsible for regenerating the receiver bit clock, as well as
+ * for handling the HDLC protocol. The modem connects to a serial port,
+ * hence the name. Since the serial port is not used as an async serial
+ * port, the kernel driver for serial ports cannot be used, and this
+ * driver only supports standard serial hardware (8250, 16450, 16550)
+ *
+ * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ * The modem does all the filtering and regenerates the receiver clock.
+ * Data is transferred from and to the PC via a shift register.
+ * The shift register is filled with 16 bits and an interrupt is
+ * signalled. The PC then empties the shift register in a burst. This
+ * modem connects to the parallel port, hence the name. The modem
+ * leaves the implementation of the HDLC protocol and the scrambler
+ * polynomial to the PC. This modem is no longer available (at least
+ * from Baycom) and has been replaced by the PICPAR modem (see below).
+ * You may however still build one from the schematics published in
+ * cq-DL :-).
+ *
+ * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
+ * modem is protocol compatible to par96, but uses only three low
+ * power ICs and can therefore be fed from the parallel port and
+ * does not require an additional power supply.
+ *
+ *
+ * Command line options (insmod command line)
+ *
+ * modem modem type of the first channel; 1=ser12,
+ * 2=par96/par97, any other value invalid
+ * iobase base address of the port; common values are for ser12 0x3f8,
+ * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
+ * irq interrupt line of the port; common values are for ser12 3,4
+ * and for par96/par97 7
+ * options 0=use hardware DCD, 1=use software DCD
+ *
+ *
+ * History:
+ * 0.1 26.06.96 Adapted from baycom.c and made network driver interface
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+ int modem, iobase, irq, options;
+} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+#define LPT_DATA(iobase) (iobase+0)
+#define LPT_STATUS(iobase) (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE 0x10
+#define PAR96_BURSTBITS 16
+#define PAR96_BURST 4
+#define PAR96_PTT 2
+#define PAR96_TXBIT 1
+#define PAR96_ACK 0x40
+#define PAR96_RXBIT 0x20
+#define PAR96_DCD 0x10
+#define PAR97_POWER 0xf8
+
+#define PAR96_EXTENT 3
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+ struct hdlcdrv_state hdrv;
+
+ unsigned int options;
+
+ struct modem_state {
+ short arb_divider;
+ unsigned char flags;
+ unsigned int shreg;
+ struct modem_state_ser12 {
+ unsigned char last_sample;
+ unsigned char interm_sample;
+ unsigned int bit_pll;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+ unsigned char last_rxbit;
+ unsigned char tx_bit;
+ } ser12;
+ struct modem_state_par96 {
+ int dcd_count;
+ unsigned int dcd_shreg;
+ unsigned long descram;
+ unsigned long scram;
+ } par96;
+ } modem;
+
+#ifdef BAYCOM_DEBUG
+ struct debug_vals {
+ unsigned long last_jiffies;
+ unsigned cur_intcnt;
+ unsigned last_intcnt;
+ int cur_pllcorr;
+ int last_pllcorr;
+ } debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/* --------------------------------------------------------------------- */
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+ unsigned long cur_jiffies = jiffies;
+ /*
+ * measure the interrupt frequency
+ */
+ bc->debug_vals.cur_intcnt++;
+ if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+ bc->debug_vals.last_jiffies = cur_jiffies;
+ bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+ bc->debug_vals.cur_intcnt = 0;
+ bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+ bc->debug_vals.cur_pllcorr = 0;
+ }
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+static void inline ser12_set_divisor(struct device *dev,
+ unsigned char divisor)
+{
+ outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */
+ outb(divisor, DLL(dev->base_addr));
+ outb(0, DLM(dev->base_addr));
+ outb(0x01, LCR(dev->base_addr)); /* word length = 6 */
+ /*
+ * make sure the next interrupt is generated;
+ * 0 must be used to power the modem; the modem draws its
+ * power from the TxD line
+ */
+ outb(0x00, THR(dev->base_addr));
+ /*
+ * it is important not to set the divider while transmitting;
+ * this reportedly makes some UARTs generating interrupts
+ * in the hundredthousands per second region
+ * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+ */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * must call the TX arbitrator every 10ms
+ */
+#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+ 36 : 24)
+#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+ 240 : 12)
+
+static inline void ser12_tx(struct device *dev, struct baycom_state *bc)
+{
+ /* one interrupt per channel bit */
+ ser12_set_divisor(dev, 12);
+ /*
+ * first output the last bit (!) then call HDLC transmitter,
+ * since this may take quite long
+ */
+ outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+ if (bc->modem.shreg <= 1)
+ bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+ bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
+ (bc->modem.shreg & 1));
+ bc->modem.shreg >>= 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_rx(struct device *dev, struct baycom_state *bc)
+{
+ unsigned char cur_s;
+ /*
+ * do demodulator
+ */
+ cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */
+ hdlcdrv_channelbit(&bc->hdrv, cur_s);
+ bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
+ (cur_s != bc->modem.ser12.last_sample);
+ bc->modem.ser12.last_sample = cur_s;
+ if(bc->modem.ser12.dcd_shreg & 1) {
+ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+ unsigned int dcdspos, dcdsneg;
+
+ dcdspos = dcdsneg = 0;
+ dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+ if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+ dcdspos += 2;
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+ bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+ } else
+ bc->modem.ser12.dcd_sum0--;
+ }
+ if(!bc->modem.ser12.dcd_time) {
+ hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+ bc->modem.ser12.dcd_sum1 +
+ bc->modem.ser12.dcd_sum2) < 0);
+ bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+ bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+ /* offset to ensure DCD off on silent input */
+ bc->modem.ser12.dcd_sum0 = 2;
+ bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+ }
+ bc->modem.ser12.dcd_time--;
+ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+ /*
+ * PLL code for the improved software DCD algorithm
+ */
+ if (bc->modem.ser12.interm_sample) {
+ /*
+ * intermediate sample; set timing correction to normal
+ */
+ ser12_set_divisor(dev, 4);
+ } else {
+ /*
+ * do PLL correction and call HDLC receiver
+ */
+ switch (bc->modem.ser12.dcd_shreg & 7) {
+ case 1: /* transition too late */
+ ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+ break;
+ case 4: /* transition too early */
+ ser12_set_divisor(dev, 3);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+ break;
+ default:
+ ser12_set_divisor(dev, 4);
+ break;
+ }
+ bc->modem.shreg >>= 1;
+ if (bc->modem.ser12.last_sample ==
+ bc->modem.ser12.last_rxbit)
+ bc->modem.shreg |= 0x10000;
+ bc->modem.ser12.last_rxbit =
+ bc->modem.ser12.last_sample;
+ }
+ if (++bc->modem.ser12.interm_sample >= 3)
+ bc->modem.ser12.interm_sample = 0;
+ /*
+ * DCD stuff
+ */
+ if (bc->modem.ser12.dcd_shreg & 1) {
+ unsigned int dcdspos, dcdsneg;
+
+ dcdspos = dcdsneg = 0;
+ dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+ dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+ << 1;
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+ dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+ bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+ }
+ } else {
+ /*
+ * PLL algorithm for the hardware squelch DCD algorithm
+ */
+ if (bc->modem.ser12.interm_sample) {
+ /*
+ * intermediate sample; set timing correction to normal
+ */
+ ser12_set_divisor(dev, 6);
+ } else {
+ /*
+ * do PLL correction and call HDLC receiver
+ */
+ switch (bc->modem.ser12.dcd_shreg & 3) {
+ case 1: /* transition too late */
+ ser12_set_divisor(dev, 7);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+ break;
+ case 2: /* transition too early */
+ ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+ bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+ break;
+ default:
+ ser12_set_divisor(dev, 6);
+ break;
+ }
+ bc->modem.shreg >>= 1;
+ if (bc->modem.ser12.last_sample ==
+ bc->modem.ser12.last_rxbit)
+ bc->modem.shreg |= 0x10000;
+ bc->modem.ser12.last_rxbit =
+ bc->modem.ser12.last_sample;
+ }
+ bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
+ /*
+ * DCD stuff
+ */
+ bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
+ }
+ outb(0x0d, MCR(dev->base_addr)); /* transmitter off */
+ if (bc->modem.shreg & 1) {
+ hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+ bc->modem.shreg = 0x10000;
+ }
+ if(!bc->modem.ser12.dcd_time) {
+ hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+ bc->modem.ser12.dcd_sum1 +
+ bc->modem.ser12.dcd_sum2) < 0);
+ bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+ bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+ /* offset to ensure DCD off on silent input */
+ bc->modem.ser12.dcd_sum0 = 2;
+ bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+ }
+ bc->modem.ser12.dcd_time--;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+
+ baycom_int_freq(bc);
+ /*
+ * check if transmitter active
+ */
+ if (hdlcdrv_ptt(&bc->hdrv))
+ ser12_tx(dev, bc);
+ else {
+ ser12_rx(dev, bc);
+ if (--bc->modem.arb_divider <= 0) {
+ bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+ sti();
+ hdlcdrv_arbitrate(dev, &bc->hdrv);
+ }
+ }
+ sti();
+ hdlcdrv_transmitter(dev, &bc->hdrv);
+ hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+ c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+ { "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+ unsigned char b1,b2,b3;
+ enum uart u;
+ enum uart uart_tab[] =
+ { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+ b1 = inb(MCR(iobase));
+ outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
+ b2 = inb(MSR(iobase));
+ outb(0x1a, MCR(iobase));
+ b3 = inb(MSR(iobase)) & 0xf0;
+ outb(b1, MCR(iobase)); /* restore old values */
+ outb(b2, MSR(iobase));
+ if (b3 != 0x90)
+ return c_uart_unknown;
+ inb(RBR(iobase));
+ inb(RBR(iobase));
+ outb(0x01, FCR(iobase)); /* enable FIFOs */
+ u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+ if (u == c_uart_16450) {
+ outb(0x5a, SCR(iobase));
+ b1 = inb(SCR(iobase));
+ outb(0xa5, SCR(iobase));
+ b2 = inb(SCR(iobase));
+ if ((b1 != 0x5a) || (b2 != 0xa5))
+ u = c_uart_8250;
+ }
+ return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+ enum uart u;
+
+ if (!dev || !bc)
+ return -ENXIO;
+ if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+ dev->irq < 2 || dev->irq > 15)
+ return -ENXIO;
+ if (check_region(dev->base_addr, SER12_EXTENT))
+ return -EACCES;
+ memset(&bc->modem, 0, sizeof(bc->modem));
+ if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+ return -EIO;
+ outb(0, FCR(dev->base_addr)); /* disable FIFOs */
+ outb(0x0d, MCR(dev->base_addr));
+ outb(0x0d, MCR(dev->base_addr));
+ outb(0, IER(dev->base_addr));
+ if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+ "baycom_ser12", dev))
+ return -EBUSY;
+ request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+ /*
+ * enable transmitter empty interrupt
+ */
+ outb(2, IER(dev->base_addr));
+ /*
+ * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
+ * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
+ * depending on the usage of the software DCD routine
+ */
+ ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
+ printk(KERN_INFO "baycom: ser12 at iobase 0x%lx irq %u options "
+ "0x%x uart %s\n", dev->base_addr, dev->irq, bc->options,
+ uart_str[u]);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ outb(0, IER(dev->base_addr));
+ outb(1, MCR(dev->base_addr));
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, SER12_EXTENT);
+ printk(KERN_INFO "baycom: close ser12 at iobase 0x%lx irq %u\n",
+ dev->base_addr, dev->irq);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== PAR96 specific routines =========================
+ */
+
+#define PAR96_DESCRAM_TAP1 0x20000
+#define PAR96_DESCRAM_TAP2 0x01000
+#define PAR96_DESCRAM_TAP3 0x00001
+
+#define PAR96_DESCRAM_TAPSH1 17
+#define PAR96_DESCRAM_TAPSH2 12
+#define PAR96_DESCRAM_TAPSH3 0
+
+#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
+#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static inline void par96_tx(struct device *dev, struct baycom_state *bc)
+{
+ int i;
+ unsigned int data = hdlcdrv_getbits(&bc->hdrv);
+
+ for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
+ unsigned char val = PAR97_POWER;
+ bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
+ (bc->modem.par96.scram & 1));
+ if (!(data & 1))
+ bc->modem.par96.scram ^= 1;
+ if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
+ bc->modem.par96.scram ^=
+ (PAR96_SCRAM_TAPN << 1);
+ if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
+ val |= PAR96_TXBIT;
+ outb(val, LPT_DATA(dev->base_addr));
+ outb(val | PAR96_BURST, LPT_DATA(dev->base_addr));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void par96_rx(struct device *dev, struct baycom_state *bc)
+{
+ int i;
+ unsigned int data, mask, mask2, descx;
+
+ /*
+ * do receiver; differential decode and descramble on the fly
+ */
+ for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+ bc->modem.par96.descram = (bc->modem.par96.descram << 1);
+ if (inb(LPT_STATUS(dev->base_addr)) & PAR96_RXBIT)
+ bc->modem.par96.descram |= 1;
+ descx = bc->modem.par96.descram ^
+ (bc->modem.par96.descram >> 1);
+ /* now the diff decoded data is inverted in descram */
+ outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev->base_addr));
+ descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
+ (descx >> PAR96_DESCRAM_TAPSH2));
+ data >>= 1;
+ if (!(descx & 1))
+ data |= 0x8000;
+ outb(PAR97_POWER | PAR96_PTT | PAR96_BURST,
+ LPT_DATA(dev->base_addr));
+ }
+ hdlcdrv_putbits(&bc->hdrv, data);
+ /*
+ * do DCD algorithm
+ */
+ if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+ bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
+ | (data << 16);
+ /* search for flags and set the dcd counter appropriately */
+ for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
+ i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+ if ((bc->modem.par96.dcd_shreg & mask) == mask2)
+ bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
+ /* check for abort/noise sequences */
+ for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
+ i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+ if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
+ (bc->modem.par96.dcd_count >= 0))
+ bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
+ /* decrement and set the dcd variable */
+ if (bc->modem.par96.dcd_count >= 0)
+ bc->modem.par96.dcd_count -= 2;
+ hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
+ } else {
+ hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev->base_addr))
+ & PAR96_DCD));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+
+ baycom_int_freq(bc);
+ /*
+ * check if transmitter active
+ */
+ if (hdlcdrv_ptt(&bc->hdrv))
+ par96_tx(dev, bc);
+ else {
+ par96_rx(dev, bc);
+ if (--bc->modem.arb_divider <= 0) {
+ bc->modem.arb_divider = 6;
+ sti();
+ hdlcdrv_arbitrate(dev, &bc->hdrv);
+ }
+ }
+ sti();
+ hdlcdrv_transmitter(dev, &bc->hdrv);
+ hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_check_lpt(unsigned int iobase)
+{
+ unsigned char b1,b2;
+ int i;
+
+ b1 = inb(LPT_DATA(iobase));
+ b2 = inb(LPT_CONTROL(iobase));
+ outb(0xaa, LPT_DATA(iobase));
+ i = inb(LPT_DATA(iobase)) == 0xaa;
+ outb(0x55, LPT_DATA(iobase));
+ i &= inb(LPT_DATA(iobase)) == 0x55;
+ outb(0x0a, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+ outb(0x05, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+ outb(b1, LPT_DATA(iobase));
+ outb(b2, LPT_CONTROL(iobase));
+ return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_open(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc)
+ return -ENXIO;
+ if (!dev->base_addr || dev->base_addr > 0x1000-PAR96_EXTENT ||
+ dev->irq < 2 || dev->irq > 15)
+ return -ENXIO;
+ if (check_region(dev->base_addr, PAR96_EXTENT))
+ return -EACCES;
+ memset(&bc->modem, 0, sizeof(bc->modem));
+ if (par96_check_lpt(dev->base_addr))
+ return -EIO;
+ /* disable interrupt */
+ outb(0, LPT_CONTROL(dev->base_addr));
+ /* switch off PTT */
+ outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr));
+ printk(KERN_INFO "baycom: par96 at iobase 0x%lx irq %u "
+ "options 0x%x\n", dev->base_addr, dev->irq, bc->options);
+ if (request_irq(dev->irq, par96_interrupt, SA_INTERRUPT,
+ "baycom_par96", dev))
+ return -EBUSY;
+ request_region(dev->base_addr, PAR96_EXTENT, "baycom_par96");
+ /* enable interrupt */
+ outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev->base_addr));
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_close(struct device *dev)
+{
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (!dev || !bc)
+ return -EINVAL;
+ /* disable interrupt */
+ outb(0, LPT_CONTROL(dev->base_addr));
+ /* switch off PTT */
+ outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr));
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, PAR96_EXTENT);
+ printk(KERN_INFO "baycom: close par96 at iobase 0x%lx irq %u\n",
+ dev->base_addr, dev->irq);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+ 1200,
+ ser12_open,
+ ser12_close,
+ baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops par96_ops = {
+ 9600,
+ par96_open,
+ par96_close,
+ baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops dummy_ops = {
+ 0,
+ NULL,
+ NULL,
+ baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops *ops_tab[3] = {
+ &dummy_ops, &ser12_ops, &par96_ops
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct baycom_state *bc;
+ struct baycom_ioctl bi;
+ unsigned long flags;
+
+ if (!dev || !dev->priv ||
+ ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+ return -EINVAL;
+ }
+ bc = (struct baycom_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+ case BAYCOMCTL_GETMODEMTYPE:
+ bi.data.mt.modem_type = BAYCOM_MODEM_INVALID;
+ if (bc->hdrv.ops == &ser12_ops)
+ bi.data.mt.modem_type = BAYCOM_MODEM_SER12;
+ else if (bc->hdrv.ops == &par96_ops)
+ bi.data.mt.modem_type = BAYCOM_MODEM_PAR96;
+ else if (bc->hdrv.ops != &dummy_ops) {
+ printk(KERN_ERR "baycom: BAYCOMCTL_GETMODEMTYPE: "
+ "modem ops invalid\n");
+ }
+ bi.data.mt.options = bc->options;
+ break;
+
+ case BAYCOMCTL_SETMODEMTYPE:
+ if (!suser() || dev->start)
+ return -EACCES;
+ if (bi.data.mt.modem_type < BAYCOM_MODEM_SER12 ||
+ bi.data.mt.modem_type > BAYCOM_MODEM_PAR96)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ bc->hdrv.ops = ops_tab[bi.data.mt.modem_type];
+ bc->options = bi.data.mt.options;
+ restore_flags(flags);
+ return 0;
+
+#ifdef BAYCOM_DEBUG
+ case BAYCOMCTL_GETDEBUG:
+ bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+ bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+ bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+ break;
+#endif /* BAYCOM_DEBUG */
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static
+#endif /* MODULE */
+int baycom_init(void)
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct baycom_state *bc;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+
+ printk(KERN_INFO "baycom: compiled %s %s\n", __TIME__, __DATE__);
+ /*
+ * register net devices
+ */
+ for (i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ sprintf(ifname, "bc%d", i);
+
+ if (baycom_ports[i].modem < BAYCOM_MODEM_SER12 ||
+ baycom_ports[i].modem > BAYCOM_MODEM_PAR96)
+ set_hw = 0;
+ if (set_hw) {
+ j = hdlcdrv_register_hdlcdrv(dev, ops_tab[baycom_ports[i].modem],
+ sizeof(struct baycom_state),
+ ifname, baycom_ports[i].iobase,
+ baycom_ports[i].irq, 0);
+ if (!j) {
+ bc = (struct baycom_state *)dev->priv;
+ bc->options = baycom_ports[i].options;
+ }
+ } else
+ j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops,
+ sizeof(struct baycom_state),
+ ifname, 0, 0, 0);
+ if (j) {
+ printk(KERN_WARNING "baycom: cannot register net "
+ "device\n");
+ } else
+ found++;
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+int modem = BAYCOM_MODEM_INVALID;
+int iobase = 0x3f8;
+int irq = 4;
+int options = BAYCOM_OPTIONS_SOFTDCD;
+
+int init_module(void)
+{
+ printk(KERN_INFO "baycom: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+
+ baycom_ports[0].modem = modem;
+ baycom_ports[0].iobase = iobase;
+ baycom_ports[0].irq = irq;
+ baycom_ports[0].options = options;
+ baycom_ports[1].modem = BAYCOM_MODEM_INVALID;
+
+ return baycom_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ int i;
+
+ printk(KERN_INFO "baycom: cleanup_module called\n");
+
+ for(i = 0; i < NR_PORTS; i++) {
+ struct device *dev = baycom_device+i;
+ struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+ if (bc) {
+ if (bc->hdrv.magic != HDLCDRV_MAGIC)
+ printk(KERN_ERR "baycom: invalid magic in "
+ "cleanup_module\n");
+ else
+ hdlcdrv_unregister_hdlcdrv(dev);
+ }
+ }
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom=modem,io,irq,options[,modem,io,irq,options]
+ * modem=1: ser12, modem=2: par96
+ * options=0: hardware DCD, options=1: software DCD
+ */
+
+void baycom_setup(char *str, int *ints)
+{
+ int i;
+
+ for (i = 0; i < NR_PORTS; i++)
+ if (ints[0] >= 4*i+4) {
+ baycom_ports[i].modem = ints[4*i+1];
+ baycom_ports[i].iobase = ints[4*i+2];
+ baycom_ports[i].irq = ints[4*i+3];
+ baycom_ports[i].options = ints[4*i+4];
+ } else
+ baycom_ports[i].modem = BAYCOM_MODEM_INVALID;
+
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * hdlcdrv.c -- HDLC packet radio network driver.
+ *
+ * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please note that the GPL allows you to use the driver, NOT the radio.
+ * In order to use the radio, you need a license from the communications
+ * authority of your country.
+ *
+ * The driver was derived from Donald Beckers skeleton.c
+ * Written 1993-94 by Donald Becker.
+ *
+ * History:
+ * 0.1 21.09.96 Started
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/hdlcdrv.h>
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/net_alias.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY 1
+#define PARAM_PERSIST 2
+#define PARAM_SLOTTIME 3
+#define PARAM_TXTAIL 4
+#define PARAM_FULLDUP 5
+#define PARAM_HARDWARE 6
+#define PARAM_RETURN 255
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*---------------------------------------------------------------------------*/
+
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ unsigned int crc = 0xffff;
+
+ for (;len>0;len--)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+ crc ^= 0xffff;
+ *buffer++ = crc;
+ *buffer++ = crc >> 8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+ unsigned int crc = 0xffff;
+
+ for (; cnt > 0; cnt--)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+ return (crc & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+ unsigned int crc = 0xffff;
+
+ for (; cnt > 0; cnt--)
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+ crc ^= 0xffff;
+ return (crc & 0xffff);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_2flags(s,tenms) ((tenms * s->ops->bitrate) / 100 / 16)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The HDLC routines
+ */
+
+static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits,
+ int num)
+{
+ int added = 0;
+
+ while (s->hdlcrx.rx_state && num >= 8) {
+ if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
+ s->hdlcrx.rx_state = 0;
+ return 0;
+ }
+ *s->hdlcrx.bp++ = bits >> (32-num);
+ s->hdlcrx.len++;
+ num -= 8;
+ added += 8;
+ }
+ return added;
+}
+
+static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s)
+{
+ struct sk_buff *skb;
+ int pkt_len;
+ unsigned char *cp;
+
+ if (s->hdlcrx.len < 4)
+ return;
+ if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len))
+ return;
+ pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
+ if (!(skb = dev_alloc_skb(pkt_len))) {
+ printk("%s: memory squeeze, dropping packet\n",
+ s->ifname);
+ s->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = dev;
+ cp = skb_put(skb, pkt_len);
+ *cp++ = 0; /* KISS kludge */
+ memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ IS_SKB(skb);
+ netif_rx(skb);
+ s->stats.rx_packets++;
+}
+
+void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s)
+{
+ int i;
+ unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
+
+ if (!s || s->magic != HDLCDRV_MAGIC)
+ return;
+ if (set_bit(0, &s->hdlcrx.in_hdlc_rx))
+ return;
+
+ while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
+ word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);
+
+#ifdef HDLCDRV_DEBUG
+ hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
+#endif /* HDLCDRV_DEBUG */
+ s->hdlcrx.bitstream >>= 16;
+ s->hdlcrx.bitstream |= word << 16;
+ s->hdlcrx.bitbuf >>= 16;
+ s->hdlcrx.bitbuf |= word << 16;
+ s->hdlcrx.numbits += 16;
+ for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+ mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff;
+ i >= 0;
+ i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1,
+ mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+ if ((s->hdlcrx.bitstream & mask1) == mask1)
+ s->hdlcrx.rx_state = 0; /* abort received */
+ else if ((s->hdlcrx.bitstream & mask2) == mask3) {
+ /* flag received */
+ if (s->hdlcrx.rx_state) {
+ hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf
+ << (8+i),
+ s->hdlcrx.numbits
+ -8-i);
+ hdlc_rx_flag(dev, s);
+ }
+ s->hdlcrx.len = 0;
+ s->hdlcrx.bp = s->hdlcrx.buffer;
+ s->hdlcrx.rx_state = 1;
+ s->hdlcrx.numbits = i;
+ } else if ((s->hdlcrx.bitstream & mask4) == mask5) {
+ /* stuffed bit */
+ s->hdlcrx.numbits--;
+ s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
+ ((s->hdlcrx.bitbuf & mask6) << 1);
+ }
+ }
+ s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
+ s->hdlcrx.numbits);
+ }
+ clear_bit(0, &s->hdlcrx.in_hdlc_rx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void inline do_kiss_params(struct hdlcdrv_state *s,
+ unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b)
+#else /* KISS_VERBOSE */
+#define PKP(a,b)
+#endif /* KISS_VERBOSE */
+
+ if (len < 2)
+ return;
+ switch(data[0]) {
+ case PARAM_TXDELAY:
+ s->ch_params.tx_delay = data[1];
+ PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
+ break;
+ case PARAM_PERSIST:
+ s->ch_params.ppersist = data[1];
+ PKP("p persistence = %u", s->ch_params.ppersist);
+ break;
+ case PARAM_SLOTTIME:
+ s->ch_params.slottime = data[1];
+ PKP("slot time = %ums", s->ch_params.slottime);
+ break;
+ case PARAM_TXTAIL:
+ s->ch_params.tx_tail = data[1];
+ PKP("TX tail = %ums", s->ch_params.tx_tail);
+ break;
+ case PARAM_FULLDUP:
+ s->ch_params.fulldup = !!data[1];
+ PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
+ break;
+ default:
+ break;
+ }
+#undef PKP
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s)
+{
+ unsigned int mask1, mask2, mask3;
+ int i;
+ struct sk_buff *skb;
+ int pkt_len;
+
+ if (!s || s->magic != HDLCDRV_MAGIC)
+ return;
+ if (set_bit(0, &s->hdlctx.in_hdlc_tx))
+ return;
+ for (;;) {
+ if (s->hdlctx.numbits >= 16) {
+ if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
+ clear_bit(0, &s->hdlctx.in_hdlc_tx);
+ return;
+ }
+ hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
+ s->hdlctx.bitbuf >>= 16;
+ s->hdlctx.numbits -= 16;
+ }
+ switch (s->hdlctx.tx_state) {
+ default:
+ clear_bit(0, &s->hdlctx.in_hdlc_tx);
+ return;
+ case 0:
+ case 1:
+ if (s->hdlctx.numflags) {
+ s->hdlctx.numflags--;
+ s->hdlctx.bitbuf |=
+ 0x7e7e << s->hdlctx.numbits;
+ s->hdlctx.numbits += 16;
+ break;
+ }
+ if (s->hdlctx.tx_state == 1) {
+ clear_bit(0, &s->hdlctx.in_hdlc_tx);
+ return;
+ }
+ if (!(skb = skb_dequeue(&s->send_queue))) {
+ int flgs = tenms_to_2flags
+ (s, s->ch_params.tx_tail);
+ if (flgs < 2)
+ flgs = 2;
+ s->hdlctx.tx_state = 1;
+ s->hdlctx.numflags = flgs;
+ break;
+ }
+ if (skb->data[0] != 0) {
+ do_kiss_params(s, skb->data, skb->len);
+ dev_kfree_skb(skb, FREE_WRITE);
+ break;
+ }
+ pkt_len = skb->len-1; /* strip KISS byte */
+ if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+ s->hdlctx.tx_state = 0;
+ s->hdlctx.numflags = 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ break;
+ }
+ memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+ dev_kfree_skb(skb, FREE_WRITE);
+ s->hdlctx.bp = s->hdlctx.buffer;
+ append_crc_ccitt(s->hdlctx.buffer, pkt_len);
+ s->hdlctx.len = pkt_len+2; /* the appended CRC */
+ s->hdlctx.tx_state = 2;
+ s->hdlctx.bitstream = 0;
+ s->stats.tx_packets++;
+ break;
+ case 2:
+ if (!s->hdlctx.len) {
+ s->hdlctx.tx_state = 0;
+ s->hdlctx.numflags = 1;
+ break;
+ }
+ s->hdlctx.len--;
+ s->hdlctx.bitbuf |= *s->hdlctx.bp <<
+ s->hdlctx.numbits;
+ s->hdlctx.bitstream >>= 8;
+ s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
+ mask1 = 0x1f000;
+ mask2 = 0x10000;
+ mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
+ s->hdlctx.numbits += 8;
+ for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1,
+ mask3 = (mask3 << 1) | 1) {
+ if ((s->hdlctx.bitstream & mask1) != mask1)
+ continue;
+ s->hdlctx.bitstream &= ~mask2;
+ s->hdlctx.bitbuf =
+ (s->hdlctx.bitbuf & mask3) |
+ ((s->hdlctx.bitbuf &
+ (~mask3)) << 1);
+ s->hdlctx.numbits++;
+ mask3 = (mask3 << 1) | 1;
+ }
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void start_tx(struct device *dev, struct hdlcdrv_state *s)
+{
+ s->hdlctx.tx_state = 0;
+ s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
+ s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
+ hdlcdrv_transmitter(dev, s);
+ s->hdlctx.ptt = 1;
+ s->ptt_keyed++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+ random_seed = 28629 * random_seed + 157;
+ return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s)
+{
+ if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt ||
+ skb_queue_empty(&s->send_queue))
+ return;
+ if (s->ch_params.fulldup) {
+ start_tx(dev, s);
+ return;
+ }
+ if (s->hdlcrx.dcd) {
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ return;
+ }
+ if ((--s->hdlctx.slotcnt) > 0)
+ return;
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ if ((random_num() % 256) > s->ch_params.ppersist)
+ return;
+ start_tx(dev, s);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static inline int hdlcdrv_paranoia_check(struct device *dev,
+ const char *routine)
+{
+ if (!dev || !dev->priv ||
+ ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state "
+ "struct in routine %s\n", routine);
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct hdlcdrv_state *sm;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
+ return 0;
+ sm = (struct hdlcdrv_state *)dev->priv;
+ /*
+ * If some higher layer thinks we've missed an tx-done interrupt
+ * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ * itself.
+ */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+ skb_queue_tail(&sm->send_queue, skb);
+ dev->trans_start = jiffies;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_set_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ /* addr is an AX.25 shifted ASCII mac address */
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
+{
+ struct hdlcdrv_state *sm;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats"))
+ return NULL;
+ sm = (struct hdlcdrv_state *)dev->priv;
+ /*
+ * Get the current statistics. This may be called with the
+ * card open or closed.
+ */
+ return &sm->stats;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int hdlcdrv_open(struct device *dev)
+{
+ struct hdlcdrv_state *s;
+ int i;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open"))
+ return -EINVAL;
+ s = (struct hdlcdrv_state *)dev->priv;
+
+ if (dev->start)
+ return 0;
+ if (!s->ops || !s->ops->open)
+ return -ENODEV;
+
+ dev->start = 1;
+ /*
+ * initialise some variables
+ */
+ s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+ s->hdlcrx.in_hdlc_rx = 0;
+ s->hdlcrx.rx_state = 0;
+
+ s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+ s->hdlctx.in_hdlc_tx = 0;
+ s->hdlctx.tx_state = 1;
+ s->hdlctx.numflags = 0;
+ s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+ s->hdlctx.ptt = 0;
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ s->hdlctx.calibrate = 0;
+
+ i = s->ops->open(dev);
+ if (i) {
+ dev->start = 0;
+ return i;
+ }
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * The inverse routine to hdlcdrv_open().
+ */
+
+static int hdlcdrv_close(struct device *dev)
+{
+ struct hdlcdrv_state *s;
+ struct sk_buff *skb;
+ int i = 0;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close"))
+ return -EINVAL;
+ s = (struct hdlcdrv_state *)dev->priv;
+
+ if (!dev->start)
+ return 0;
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (s->ops && s->ops->close)
+ i = s->ops->close(dev);
+ /* Free any buffers left in the hardware transmit queue */
+ while ((skb = skb_dequeue(&s->send_queue)))
+ dev_kfree_skb(skb, FREE_WRITE);
+ return i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct hdlcdrv_state *s;
+ struct hdlcdrv_ioctl bi;
+
+ if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl"))
+ return -EINVAL;
+ s = (struct hdlcdrv_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE) {
+ if (s->ops->ioctl)
+ return s->ops->ioctl(dev, ifr, cmd);
+ return -ENOIOCTLCMD;
+ }
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ if (s->ops->ioctl)
+ return s->ops->ioctl(dev, ifr, cmd);
+ return -ENOIOCTLCMD;
+
+ case HDLCDRVCTL_GETCHANNELPAR:
+ bi.data.cp.tx_delay = s->ch_params.tx_delay;
+ bi.data.cp.tx_tail = s->ch_params.tx_tail;
+ bi.data.cp.slottime = s->ch_params.slottime;
+ bi.data.cp.ppersist = s->ch_params.ppersist;
+ bi.data.cp.fulldup = s->ch_params.fulldup;
+ break;
+
+ case HDLCDRVCTL_SETCHANNELPAR:
+ if (!suser())
+ return -EACCES;
+ s->ch_params.tx_delay = bi.data.cp.tx_delay;
+ s->ch_params.tx_tail = bi.data.cp.tx_tail;
+ s->ch_params.slottime = bi.data.cp.slottime;
+ s->ch_params.ppersist = bi.data.cp.ppersist;
+ s->ch_params.fulldup = bi.data.cp.fulldup;
+ s->hdlctx.slotcnt = 1;
+ return 0;
+
+ case HDLCDRVCTL_GETMODEMPAR:
+ bi.data.mp.iobase = dev->base_addr;
+ bi.data.mp.irq = dev->irq;
+ bi.data.mp.dma = dev->dma;
+ bi.data.mp.seriobase = s->ptt_out.seriobase;
+ bi.data.mp.pariobase = s->ptt_out.pariobase;
+ bi.data.mp.midiiobase = s->ptt_out.midiiobase;
+ break;
+
+ case HDLCDRVCTL_SETMODEMPAR:
+ if ((!suser()) || dev->start)
+ return -EACCES;
+ dev->base_addr = bi.data.mp.iobase;
+ dev->irq = bi.data.mp.irq;
+ dev->dma = bi.data.mp.dma;
+ s->ptt_out.seriobase = bi.data.mp.seriobase;
+ s->ptt_out.pariobase = bi.data.mp.pariobase;
+ s->ptt_out.midiiobase = bi.data.mp.midiiobase;
+ return 0;
+
+ case HDLCDRVCTL_GETSTAT:
+ bi.data.cs.ptt = hdlcdrv_ptt(s);
+ bi.data.cs.dcd = s->hdlcrx.dcd;
+ bi.data.cs.ptt_keyed = s->ptt_keyed;
+ bi.data.cs.stats = s->stats;
+ break;
+
+ case HDLCDRVCTL_CALIBRATE:
+ s->hdlctx.calibrate = bi.data.calibrate * s->ops->bitrate / 16;
+ return 0;
+
+ case HDLCDRVCTL_GETSAMPLES:
+#ifndef HDLCDRV_DEBUG
+ return -EPERM;
+#else /* HDLCDRV_DEBUG */
+ if (s->bitbuf_channel.rd == s->bitbuf_channel.wr)
+ return -EAGAIN;
+ bi.data.bits =
+ s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
+ s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
+ sizeof(s->bitbuf_channel.buffer);
+ break;
+#endif /* HDLCDRV_DEBUG */
+
+ case HDLCDRVCTL_GETBITS:
+#ifndef HDLCDRV_DEBUG
+ return -EPERM;
+#else /* HDLCDRV_DEBUG */
+ if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr)
+ return -EAGAIN;
+ bi.data.bits =
+ s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
+ s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
+ sizeof(s->bitbuf_hdlc.buffer);
+ break;
+#endif /* HDLCDRV_DEBUG */
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Fill in the MAC-level header
+ */
+
+#ifdef CONFIG_AX25
+static int hdlcdrv_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len)
+{
+ return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Rebuild the MAC-level header
+ */
+
+static int hdlcdrv_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr,
+ struct sk_buff *skb)
+{
+ return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+#endif /* CONFIG_AX25 */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static int hdlcdrv_probe(struct device *dev)
+{
+ struct hdlcdrv_channel_params dflt_ch_params = { 20, 2, 10, 40, 0 };
+ int i;
+ struct hdlcdrv_state *s;
+
+ if (!dev)
+ return -ENXIO;
+ /*
+ * not a real probe! only initialize data structures
+ */
+ s = (struct hdlcdrv_state *)dev->priv;
+ /*
+ * initialize the hdlcdrv_state struct
+ */
+ s->ch_params = dflt_ch_params;
+ s->ptt_keyed = 0;
+
+ s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+ s->hdlcrx.in_hdlc_rx = 0;
+ s->hdlcrx.rx_state = 0;
+
+ s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+ s->hdlctx.in_hdlc_tx = 0;
+ s->hdlctx.tx_state = 1;
+ s->hdlctx.numflags = 0;
+ s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+ s->hdlctx.ptt = 0;
+ s->hdlctx.slotcnt = s->ch_params.slottime;
+ s->hdlctx.calibrate = 0;
+
+#ifdef HDLCDRV_DEBUG
+ s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
+ s->bitbuf_channel.shreg = 0x80;
+
+ s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
+ s->bitbuf_hdlc.shreg = 0x80;
+#endif /* HDLCDRV_DEBUG */
+
+ /*
+ * initialize the device struct
+ */
+ dev->open = hdlcdrv_open;
+ dev->stop = hdlcdrv_close;
+ dev->do_ioctl = hdlcdrv_ioctl;
+ dev->hard_start_xmit = hdlcdrv_send_packet;
+ dev->get_stats = hdlcdrv_get_stats;
+
+ /* Fill in the fields of the device structure */
+ for (i=0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ skb_queue_head_init(&s->send_queue);
+
+#ifdef CONFIG_AX25
+ dev->hard_header = hdlcdrv_header;
+ dev->rebuild_header = hdlcdrv_rebuild_header;
+#else /* CONFIG_AX25 */
+ dev->hard_header = NULL;
+ dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 */
+ dev->set_mac_address = hdlcdrv_set_mac_address;
+
+ dev->type = ARPHRD_AX25; /* AF_AX25 device */
+ dev->hard_header_len = 73; /* We do digipeaters now */
+ dev->mtu = 1500; /* eth_mtu is the default */
+ dev->addr_len = 7; /* sizeof an ax.25 address */
+ memcpy(dev->broadcast, ax25_bcast, 7);
+ memcpy(dev->dev_addr, ax25_test, 7);
+
+ /* New style flags */
+ dev->flags = 0;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+ unsigned int privsize, char *ifname,
+ unsigned int baseaddr, unsigned int irq,
+ unsigned int dma)
+{
+ struct hdlcdrv_state *s;
+
+ if (!dev || !ops)
+ return -EACCES;
+ if (privsize < sizeof(struct hdlcdrv_state))
+ privsize = sizeof(struct hdlcdrv_state);
+ memset(dev, 0, sizeof(struct device));
+ if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL)))
+ return -ENOMEM;
+ /*
+ * initialize part of the hdlcdrv_state struct
+ */
+ memset(s, 0, privsize);
+ s->magic = HDLCDRV_MAGIC;
+ strncpy(s->ifname, ifname, sizeof(s->ifname));
+ s->ops = ops;
+ /*
+ * initialize part of the device struct
+ */
+ dev->name = s->ifname;
+ dev->if_port = 0;
+ dev->init = hdlcdrv_probe;
+ dev->start = 0;
+ dev->tbusy = 1;
+ dev->base_addr = baseaddr;
+ dev->irq = irq;
+ dev->dma = dma;
+ if (register_netdev(dev)) {
+ printk(KERN_WARNING "hdlcdrv: cannot register net "
+ "device %s\n", s->ifname);
+ kfree(dev->priv);
+ return -ENXIO;
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_unregister_hdlcdrv(struct device *dev)
+{
+ struct hdlcdrv_state *s;
+
+ if (!dev)
+ return -EINVAL;
+ if (!(s = (struct hdlcdrv_state *)dev->priv))
+ return -EINVAL;
+ if (s->magic != HDLCDRV_MAGIC)
+ return -EINVAL;
+ if (dev->start && s->ops->close)
+ s->ops->close(dev);
+ unregister_netdev(dev);
+ kfree(s);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct symbol_table hdlcdrv_syms = {
+#include <linux/symtab_begin.h>
+ X(hdlcdrv_receiver),
+ X(hdlcdrv_transmitter),
+ X(hdlcdrv_arbitrate),
+ X(hdlcdrv_register_hdlcdrv),
+ X(hdlcdrv_unregister_hdlcdrv),
+#include <linux/symtab_end.h>
+};
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ printk(KERN_INFO "hdlcdrv: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+ printk(KERN_INFO "hdlcdrv: compiled %s %s\n", __TIME__, __DATE__);
+
+ register_symtab(&hdlcdrv_syms);
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ printk(KERN_INFO "hdlcdrv: cleanup\n");
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
-#define RCS_ID "$Id: scc.c,v 1.63 1996/10/09 16:45:47 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.64 1996/10/30 18:58:26 jreuter Exp jreuter $"
#define VERSION "3.0"
#define BANNER "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
* Source moved to drivers/net/
* Includes Z8530 defines from drivers/net/z8530.h
* Uses sk_buffer memory management
- * Doesn't have own queues anymore
* Reduced overhead of /proc/net/z8530drv output
* Streamlined quite a lot things
* Invents brand new bugs... ;-)
The move to version number 3.0 reflects theses changes.
- You can use version 2.4 if you need a KISS TNC emulator.
+ You can use version 2.4a if you need a KISS TNC emulator.
Thanks to all who contributed to this driver with ideas and bug
reports!
#define MAXSCC 4 /* number of max. supported chips */
#define BUFSIZE 384 /* must not exceed 4096 */
+#define MAXQUEUE 8 /* number of buffers we queue ourself */
#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */
/* enable_irq()/disable_irq() */
#undef SCC_DEBUG
/* ----------------------------------------------------------------------- */
#include <linux/module.h>
-#include <linux/config.h>
-
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/if_arp.h>
#include <linux/socket.h>
+#include <linux/config.h> /* for CONFIG_PROC_FS */
+
#include <linux/scc.h>
#include "z8530.h"
static __inline__ void
scc_unlock_dev(struct scc_channel *scc)
{
- scc->tx_next_buff = NULL;
scc->dev->tbusy = 0;
}
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);
}
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)
{
Outb(scc->ctrl, RES_Tx_P);
return;
}
-
- scc_unlock_dev(scc);
scc->stat.tx_state = TXS_ACTIVE;
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. */
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);
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
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)
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;
--- /dev/null
+#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 */
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * soundmodem.c -- soundcard radio modem driver.
+ *
+ * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please note that the GPL allows you to use the driver, NOT the radio.
+ * In order to use the radio, you need a license from the communications
+ * authority of your country.
+ *
+ *
+ * Command line options (insmod command line)
+ *
+ * hardware hardware type; 0=sbc, 1=wss, any other value invalid
+ * mode mode type; 0=1200 baud AFSK, 1=9600 baud FSK, any other
+ * value invalid
+ * iobase base address of the soundcard; common values are 0x220 for sbc,
+ * 0x530 for wss
+ * irq interrupt number; common values are 7 or 5 for sbc, 11 for wss
+ * dma dma number; common values are 0 or 1
+ *
+ *
+ * History:
+ * 0.1 21.09.96 Started
+ * 18.10.96 Changed to new user space access routines (copy_{to,from}_user)
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <limits.h>
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+#define SM_DEBUG
+
+#define ENABLE_SBC
+#define ENABLE_WSS
+#undef ENABLE_WSSFDX
+
+#define ENABLE_AFSK1200
+#define ENABLE_FSK9600
+
+/* --------------------------------------------------------------------- */
+
+#include "sm_tables.h"
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+ int hardware, mode, iobase, irq, dma, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = {
+{ SM_HARDWARE_INVALID, SM_MODE_INVALID, -1, 0, 0, -1, -1, -1 },
+};
+
+/* --------------------------------------------------------------------- */
+/*
+ * the sbc converter's registers
+ */
+#define DSP_RESET(iobase) (iobase+0x6)
+#define DSP_READ_DATA(iobase) (iobase+0xa)
+#define DSP_WRITE_DATA(iobase) (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase) (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase) (iobase+0x4)
+#define DSP_MIXER_DATA(iobase) (iobase+0x5)
+#define SBC_EXTENT 16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+#define SBC_OUTPUT 0x14
+#define SBC_INPUT 0x24
+#define SBC_HISPEED 0x48
+#define SBC_HI_OUTPUT 0x91
+#define SBC_HI_INPUT 0x99
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT 0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90
+#define SBC_HI_INPUT_AUTOINIT 0x98
+#define SBC_IMMED_INT 0xf2
+#define SBC_GET_REVISION 0xe1
+#define ESS_GET_REVISION 0xe7
+#define SBC_SPEAKER_ON 0xd1
+#define SBC_SPEAKER_OFF 0xd3
+#define SBC_DMA_ON 0xd0
+#define SBC_DMA_OFF 0xd4
+#define SBC_SAMPLE_RATE 0x40
+#define SBC_MONO_8BIT 0xa0
+#define SBC_MONO_16BIT 0xa4
+#define SBC_STEREO_8BIT 0xa8
+#define SBC_STEREO_16BIT 0xac
+
+#define DMA_MODE_AUTOINIT 0x10
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase) (iobase+0)
+#define WSS_STATUS(iobase) (iobase+3)
+#define WSS_CODEC_IA(iobase) (iobase+4)
+#define WSS_CODEC_ID(iobase) (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase) (iobase+7)
+
+#define WSS_EXTENT 8
+
+/* --------------------------------------------------------------------- */
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase) (iobase+0)
+#define LPT_STATUS(iobase) (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE 0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase) (iobase)
+#define MIDI_STATUS(iobase) (iobase+1)
+#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+/* ---------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY 1
+#define PARAM_PERSIST 2
+#define PARAM_SLOTTIME 3
+#define PARAM_TXTAIL 4
+#define PARAM_FULLDUP 5
+#define PARAM_HARDWARE 6
+#define PARAM_RETURN 255
+
+#define SP_SER 1
+#define SP_PAR 2
+#define SP_MIDI 4
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct sm_state {
+ struct hdlcdrv_state hdrv;
+
+ struct config {
+ int hardware;
+ int mode;
+ } config;
+
+ struct modem_state {
+ unsigned char revhi, revlo;
+
+ unsigned int shreg;
+
+ unsigned char last_sample;
+ unsigned int bit_pll;
+ unsigned int dcd_shreg;
+ int dcd_sum0, dcd_sum1, dcd_sum2;
+ unsigned int dcd_time;
+ unsigned char last_rxbit;
+ unsigned char tx_bit;
+
+ signed char filt[9];
+
+ unsigned long descram;
+ unsigned long scram;
+
+ unsigned char *dmabufr;
+ unsigned char *dmabufw;
+ unsigned char dmabufidx;
+ unsigned char oldptt;
+ } modem;
+
+#define DIAGDATALEN 64
+ struct diag_data {
+ unsigned int mode;
+ unsigned int flags;
+ volatile int ptr;
+ short data[DIAGDATALEN];
+ } diag;
+
+#ifdef SM_DEBUG
+ struct debug_vals {
+ unsigned long last_jiffies;
+ unsigned cur_intcnt;
+ unsigned last_intcnt;
+ int cur_pllcorr;
+ int last_pllcorr;
+ } debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+struct modem_info {
+ struct hdlcdrv_ops hops;
+ unsigned int samplerate;
+ unsigned char sbcmix;
+ unsigned char sperbit;
+ char *mode_name; /* used for request_{region,irq,dma} */
+ /*
+ * low level chip informations
+ */
+ unsigned char data_fmt;
+ unsigned int dmabuflen;
+ void (*modulator)(struct sm_state *, unsigned char *, int);
+ void (*demodulator)(struct sm_state *, unsigned char *, int);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline sm_int_freq(struct sm_state *sm)
+{
+#ifdef SM_DEBUG
+ unsigned long cur_jiffies = jiffies;
+ /*
+ * measure the interrupt frequency
+ */
+ sm->debug_vals.cur_intcnt++;
+ if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
+ sm->debug_vals.last_jiffies = cur_jiffies;
+ sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
+ sm->debug_vals.cur_intcnt = 0;
+ }
+#endif /* SM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+static int inline check_lpt(unsigned int iobase)
+{
+ unsigned char b1,b2;
+ int i;
+
+ if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+ return 0;
+ if (check_region(iobase, LPT_EXTENT))
+ return 0;
+ b1 = inb(LPT_DATA(iobase));
+ b2 = inb(LPT_CONTROL(iobase));
+ outb(0xaa, LPT_DATA(iobase));
+ i = inb(LPT_DATA(iobase)) == 0xaa;
+ outb(0x55, LPT_DATA(iobase));
+ i &= inb(LPT_DATA(iobase)) == 0x55;
+ outb(0x0a, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+ outb(0x05, LPT_CONTROL(iobase));
+ i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+ outb(b1, LPT_DATA(iobase));
+ outb(b2, LPT_CONTROL(iobase));
+ return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+ c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+ { "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart inline check_uart(unsigned int iobase)
+{
+ unsigned char b1,b2,b3;
+ enum uart u;
+ enum uart uart_tab[] =
+ { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+ if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+ return c_uart_unknown;
+ if (check_region(iobase, SER_EXTENT))
+ return c_uart_unknown;
+ b1 = inb(UART_MCR(iobase));
+ outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */
+ b2 = inb(UART_MSR(iobase));
+ outb(0x1a, UART_MCR(iobase));
+ b3 = inb(UART_MSR(iobase)) & 0xf0;
+ outb(b1, UART_MCR(iobase)); /* restore old values */
+ outb(b2, UART_MSR(iobase));
+ if (b3 != 0x90)
+ return c_uart_unknown;
+ inb(UART_RBR(iobase));
+ inb(UART_RBR(iobase));
+ outb(0x01, UART_FCR(iobase)); /* enable FIFOs */
+ u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+ if (u == c_uart_16450) {
+ outb(0x5a, UART_SCR(iobase));
+ b1 = inb(UART_SCR(iobase));
+ outb(0xa5, UART_SCR(iobase));
+ b2 = inb(UART_SCR(iobase));
+ if ((b1 != 0x5a) || (b2 != 0xa5))
+ u = c_uart_8250;
+ }
+ return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline check_midi(unsigned int iobase)
+{
+ unsigned long timeout;
+ unsigned long flags;
+ unsigned char b;
+
+ if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+ return 0;
+ if (check_region(iobase, MIDI_EXTENT))
+ return 0;
+ timeout = jiffies + (HZ / 100);
+ while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+ if ((signed)(jiffies - timeout) > 0)
+ return 0;
+ save_flags(flags);
+ cli();
+ outb(0xff, MIDI_DATA(iobase));
+ b = inb(MIDI_STATUS(iobase));
+ restore_flags(flags);
+ if (!(b & MIDI_WRITE_EMPTY))
+ return 0;
+ while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+ if ((signed)(jiffies - timeout) > 0)
+ return 0;
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline output_status(struct sm_state *sm)
+{
+ int invert_dcd = 0;
+ int invert_ptt = 0;
+
+ int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt;
+ int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
+
+ if (sm->hdrv.ptt_out.flags & SP_SER) {
+ outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
+ outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
+ }
+ if (sm->hdrv.ptt_out.flags & SP_PAR) {
+ outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
+ }
+ if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
+ outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_open(struct sm_state *sm)
+{
+ enum uart u = c_uart_unknown;
+
+ sm->hdrv.ptt_out.flags = 0;
+ if (sm->hdrv.ptt_out.seriobase > 0 &&
+ sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+ ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
+ sm->hdrv.ptt_out.flags |= SP_SER;
+ request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
+ outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
+ /* 5 bits, 1 stop, no parity, no break, Div latch access */
+ outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
+ outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
+ outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
+ /* LCR and MCR set by output_status */
+ }
+ if (sm->hdrv.ptt_out.pariobase > 0 &&
+ sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+ check_lpt(sm->hdrv.ptt_out.pariobase)) {
+ sm->hdrv.ptt_out.flags |= SP_PAR;
+ request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
+ }
+ if (sm->hdrv.ptt_out.midiiobase > 0 &&
+ sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+ check_midi(sm->hdrv.ptt_out.midiiobase)) {
+ sm->hdrv.ptt_out.flags |= SP_MIDI;
+ request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
+ "sm midi ptt");
+ }
+ output_status(sm);
+
+ printk(KERN_INFO "sm: ptt output:");
+ if (sm->hdrv.ptt_out.flags & SP_SER)
+ printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+ uart_str[u]);
+ if (sm->hdrv.ptt_out.flags & SP_PAR)
+ printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
+ if (sm->hdrv.ptt_out.flags & SP_MIDI)
+ printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
+ if (!sm->hdrv.ptt_out.flags)
+ printk(" none");
+ printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_close(struct sm_state *sm)
+{
+ /* release regions used for PTT output */
+ sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+ output_status(sm);
+ if (sm->hdrv.ptt_out.flags & SP_SER)
+ release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
+ if (sm->hdrv.ptt_out.flags & SP_PAR)
+ release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
+ if (sm->hdrv.ptt_out.flags & SP_MIDI)
+ release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
+ sm->hdrv.ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+static inline void diag_trigger(struct sm_state *sm)
+{
+ if (sm->diag.ptr < 0)
+ if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+ sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+{
+ int val;
+
+ if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+ sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+ sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+ return;
+ val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
+ /* clip */
+ if (val > SHRT_MAX)
+ val = SHRT_MAX;
+ if (val < SHRT_MIN)
+ val = SHRT_MIN;
+ sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_one(struct sm_state *sm, int val)
+{
+ if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+ sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+ sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+ return;
+ /* clip */
+ if (val > SHRT_MAX)
+ val = SHRT_MAX;
+ if (val < SHRT_MIN)
+ val = SHRT_MIN;
+ sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== modem routines 1200 baud =========================
+ */
+
+static inline unsigned int hweight32(unsigned int w)
+ __attribute__ ((unused));
+static inline unsigned int hweight16(unsigned short w)
+ __attribute__ ((unused));
+static inline unsigned int hweight8(unsigned char w)
+ __attribute__ ((unused));
+
+static inline unsigned int hweight32(unsigned int w)
+{
+ unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+ res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+ return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+static inline unsigned int hweight16(unsigned short w)
+{
+ unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+ res = (res & 0x3333) + ((res >> 2) & 0x3333);
+ res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+ return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+static inline unsigned int hweight8(unsigned char w)
+{
+ unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_AFSK1200
+
+static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ static const int dds_inc[2] = { 8192, 15019 };
+ int j, k;
+
+ for (; buflen >= 8; buflen -= 8) {
+ if (sm->modem.shreg <= 1)
+ sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ sm->modem.tx_bit = (sm->modem.tx_bit ^
+ (!(sm->modem.shreg & 1))) & 1;
+ sm->modem.shreg >>= 1;
+ k = dds_inc[sm->modem.tx_bit & 1];
+ for (j = 0; j < 8; j++) {
+ *buf++ = sinetab[(sm->modem.bit_pll >> 10) & 0x3f];
+ sm->modem.bit_pll += k;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ int j;
+ signed char *fp;
+ const signed char *coeffp;
+ int sum1, sum2;
+ unsigned char newsample;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sm->modem.filt[8] = (*buf - 128);
+ for (sum1 = j = 0, fp = sm->modem.filt+1, coeffp = tx_lo_i;
+ j < 8; j++, fp++, coeffp++) {
+ sum1 += (*coeffp) * (*fp);
+ fp[-1] = fp[0];
+ }
+ sum1 >>= 7;
+ sum2 = sum1 * sum1;
+ for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_lo_q;
+ j < 8; j++, fp++, coeffp++)
+ sum1 += (*coeffp) * (*fp);
+ sum1 >>= 7;
+ sum2 += sum1 * sum1;
+ for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_i;
+ j < 8; j++, fp++, coeffp++)
+ sum1 += (*coeffp) * (*fp);
+ sum1 >>= 7;
+ sum2 -= sum1 * sum1;
+ for (sum1 = j = 0, fp = sm->modem.filt, coeffp = tx_hi_q;
+ j < 8; j++, fp++, coeffp++)
+ sum1 += (*coeffp) * (*fp);
+ sum1 >>= 7;
+ sum2 -= sum1 * sum1;
+ sm->modem.dcd_shreg <<= 1;
+ sm->modem.bit_pll += 0x2000;
+ newsample = (sum2 > 0);
+ if (sm->modem.last_sample ^ newsample) {
+ sm->modem.last_sample = newsample;
+ sm->modem.dcd_shreg |= 1;
+ sm->modem.bit_pll += pll_corr
+ [sm->modem.bit_pll < 0x9000];
+ j = 4 * hweight8(sm->modem.dcd_shreg & 0x38)
+ - hweight16(sm->modem.dcd_shreg & 0x7c0);
+ sm->modem.dcd_sum0 += j;
+ }
+ hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
+ if ((--sm->modem.dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 +
+ sm->modem.dcd_sum1 +
+ sm->modem.dcd_sum2) < 0);
+ sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
+ sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
+ sm->modem.dcd_sum0 = 2; /* slight bias */
+ sm->modem.dcd_time = 120;
+ }
+ if (sm->modem.bit_pll >= 0x10000) {
+ sm->modem.bit_pll &= 0xffff;
+ sm->modem.shreg >>= 1;
+ sm->modem.shreg |= (!(sm->modem.last_rxbit ^
+ sm->modem.last_sample)) << 16;
+ sm->modem.last_rxbit = sm->modem.last_sample;
+ diag_trigger(sm);
+ if (sm->modem.shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
+ sm->modem.shreg = 0x10000;
+ }
+ }
+ diag_add(sm, sm->modem.filt[7] << 8, sum2);
+ }
+}
+
+#endif /* ENABLE_AFSK1200 */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== modem routines 9600 baud =========================
+ */
+
+#ifdef ENABLE_FSK9600
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_SBC
+
+static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ int j;
+ const unsigned char *cp;
+
+ for (; buflen >= 4; buflen -= 4) {
+ if (sm->modem.shreg <= 1)
+ sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ sm->modem.scram = ((sm->modem.scram << 1) |
+ (sm->modem.scram & 1));
+ sm->modem.scram ^= (!(sm->modem.shreg & 1));
+ sm->modem.shreg >>= 1;
+ if (sm->modem.scram & (SCRAM_TAP1 << 1))
+ sm->modem.scram ^= (SCRAM_TAPN << 1);
+ sm->modem.tx_bit = (sm->modem.tx_bit << 1) |
+ (!!(sm->modem.scram & (SCRAM_TAP1 << 2)));
+ cp = tx_filter_9k6_4 + (sm->modem.tx_bit & 0xff);
+ for (j = 0; j < 4; j++) {
+ *buf++ = *cp;
+ cp += 0x100;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ unsigned char curbit;
+ unsigned int descx;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sm->modem.dcd_shreg <<= 1;
+ sm->modem.bit_pll += 0x4000;
+ curbit = (*buf >= 0x80);
+ if (sm->modem.last_sample ^ curbit) {
+ sm->modem.dcd_shreg |= 1;
+ sm->modem.bit_pll += pll_corr
+ [sm->modem.bit_pll < 0xa000];
+ sm->modem.dcd_sum0 += 8 *
+ hweight8(sm->modem.dcd_shreg & 0x0c) -
+ !!(sm->modem.dcd_shreg & 0x10);
+ }
+ sm->modem.last_sample = curbit;
+ hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
+ if ((--sm->modem.dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 +
+ sm->modem.dcd_sum1 +
+ sm->modem.dcd_sum2) < 0);
+ sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
+ sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
+ sm->modem.dcd_sum0 = 2; /* slight bias */
+ sm->modem.dcd_time = 240;
+ }
+ if (sm->modem.bit_pll >= 0x10000) {
+ sm->modem.bit_pll &= 0xffff;
+ sm->modem.descram = (sm->modem.descram << 1) | curbit;
+ descx = sm->modem.descram ^ (sm->modem.descram >> 1);
+ descx ^= ((descx >> DESCRAM_TAPSH1) ^
+ (descx >> DESCRAM_TAPSH2));
+ sm->modem.shreg >>= 1;
+ sm->modem.shreg |= (!(descx & 1)) << 16;
+ if (sm->modem.shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
+ sm->modem.shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+ }
+}
+
+#endif /* ENABLE_SBC */
+
+/* --------------------------------------------------------------------- */
+
+#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
+
+static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ int j;
+ const unsigned char *cp;
+
+ for (; buflen >= 5; buflen -= 5) {
+ if (sm->modem.shreg <= 1)
+ sm->modem.shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+ sm->modem.scram = ((sm->modem.scram << 1) |
+ (sm->modem.scram & 1));
+ sm->modem.scram ^= (!(sm->modem.shreg & 1));
+ sm->modem.shreg >>= 1;
+ if (sm->modem.scram & (SCRAM_TAP1 << 1))
+ sm->modem.scram ^= (SCRAM_TAPN << 1);
+ sm->modem.tx_bit = (sm->modem.tx_bit << 1) |
+ (!!(sm->modem.scram & (SCRAM_TAP1 << 2)));
+ cp = tx_filter_9k6_5 + (sm->modem.tx_bit & 0xff);
+ for (j = 0; j < 5; j++) {
+ *buf++ = *cp;
+ cp += 0x100;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+ static const int pll_corr[2] = { -0x1000, 0x1000 };
+ unsigned char curbit;
+ unsigned int descx;
+
+ for (; buflen > 0; buflen--, buf++) {
+ sm->modem.dcd_shreg <<= 1;
+ sm->modem.bit_pll += 0x3333;
+ curbit = (*buf >= 0x80);
+ if (sm->modem.last_sample ^ curbit) {
+ sm->modem.dcd_shreg |= 1;
+ sm->modem.bit_pll += pll_corr
+ [sm->modem.bit_pll < 0x9999];
+ sm->modem.dcd_sum0 += 16 *
+ hweight8(sm->modem.dcd_shreg & 0x0c) -
+ hweight8(sm->modem.dcd_shreg & 0x70);
+ }
+ sm->modem.last_sample = curbit;
+ hdlcdrv_channelbit(&sm->hdrv, sm->modem.last_sample);
+ if ((--sm->modem.dcd_time) <= 0) {
+ hdlcdrv_setdcd(&sm->hdrv, (sm->modem.dcd_sum0 +
+ sm->modem.dcd_sum1 +
+ sm->modem.dcd_sum2) < 0);
+ sm->modem.dcd_sum2 = sm->modem.dcd_sum1;
+ sm->modem.dcd_sum1 = sm->modem.dcd_sum0;
+ sm->modem.dcd_sum0 = 2; /* slight bias */
+ sm->modem.dcd_time = 240;
+ }
+ if (sm->modem.bit_pll >= 0x10000) {
+ sm->modem.bit_pll &= 0xffff;
+ sm->modem.descram = (sm->modem.descram << 1) | curbit;
+ descx = sm->modem.descram ^ (sm->modem.descram >> 1);
+ descx ^= ((descx >> DESCRAM_TAPSH1) ^
+ (descx >> DESCRAM_TAPSH2));
+ sm->modem.shreg >>= 1;
+ sm->modem.shreg |= (!(descx & 1)) << 16;
+ if (sm->modem.shreg & 1) {
+ hdlcdrv_putbits(&sm->hdrv, sm->modem.shreg >> 1);
+ sm->modem.shreg = 0x10000;
+ }
+ diag_trigger(sm);
+ }
+ diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+ }
+}
+
+#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
+#endif /* ENABLE_FSK9600 */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== soundblaster specific routines ===================
+ */
+
+#ifdef ENABLE_SBC
+
+static int inline reset_dsp(struct device *dev)
+{
+ int i;
+
+ outb(1, DSP_RESET(dev->base_addr));
+ for (i = 0; i < 0x100; i++)
+ SLOW_DOWN_IO;
+ outb(0, DSP_RESET(dev->base_addr));
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
+ if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
+ return 1;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline write_dsp(struct device *dev, unsigned char data)
+{
+ int i;
+
+ for (i = 0; i < 0xffff; i++)
+ if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
+ outb(data, DSP_WRITE_DATA(dev->base_addr));
+ return;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline read_dsp(struct device *dev, unsigned char *data)
+{
+ int i;
+
+ if (!data)
+ return 0;
+ for (i = 0; i < 0xffff; i++)
+ if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
+ *data = inb(DSP_READ_DATA(dev->base_addr));
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack(struct device *dev)
+{
+ inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_dsp(struct device *dev, int send)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned long flags;
+ static const unsigned char sbcmode[2][2] = {
+ { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT },
+ { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }};
+ static const unsigned char dmamode[2] =
+ { DMA_MODE_READ | DMA_MODE_AUTOINIT,
+ DMA_MODE_WRITE | DMA_MODE_AUTOINIT};
+ static const unsigned char sbcskr[2] =
+ { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
+ unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr);
+
+ send = !!send;
+ if (!reset_dsp(dev)) {
+ printk(KERN_ERR "sm: cannot reset sb dsp\n");
+ return;
+ }
+ if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000)
+ panic("sm: DMA buffer violates DMA boundary!");
+ save_flags(flags);
+ cli();
+ sbc_int_ack(dev);
+ write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+ write_dsp(dev, mi->data_fmt);
+ write_dsp(dev, sbcskr[send]);
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, dmamode[send]);
+ set_dma_addr(dev->dma, dmabufaddr);
+ set_dma_count(dev->dma, mi->dmabuflen);
+ enable_dma(dev->dma);
+ sbc_int_ack(dev);
+ write_dsp(dev, SBC_HISPEED);
+ write_dsp(dev, ((mi->dmabuflen >> 1) - 1) & 0xff);
+ write_dsp(dev, ((mi->dmabuflen >> 1) - 1) >> 8);
+ write_dsp(dev, sbcmode[mi->samplerate >= 13000][send]);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+#if 0
+static int probe_int(struct device *dev, struct sm_state *sm)
+{
+ unsigned long irqs;
+ int irq;
+
+ irqs = probe_irq_on();
+ setup_dma_dsp(dev, virt_to_bus(sm->modem.dmabufr), 4, 256-77, 0);
+ udelay(2000);
+ irq = probe_irq_off(irqs);
+ disable_dma(dev->dma);
+ return irq;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned char new_ptt;
+ unsigned char *buf;
+
+ if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ new_ptt = hdlcdrv_ptt(&sm->hdrv);
+ sbc_int_ack(dev);
+ buf = sm->modem.dmabufr;
+ if (sm->modem.dmabufidx)
+ buf += mi->dmabuflen/2;
+ sm->modem.dmabufidx = !sm->modem.dmabufidx;
+ if (sm->modem.oldptt != new_ptt) {
+ disable_dma(dev->dma);
+ sti();
+ sm->modem.dmabufidx = 0;
+ if (!new_ptt) {
+ setup_dma_dsp(dev, 0);
+ goto endint;
+ }
+ mi->demodulator(sm, buf, mi->dmabuflen/2);
+ mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2);
+ setup_dma_dsp(dev, 1);
+ mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2,
+ mi->dmabuflen/2);
+ goto endint;
+ }
+ sm_int_freq(sm);
+ sti();
+ /*
+ * check if transmitter active
+ */
+ if (new_ptt)
+ mi->modulator(sm, buf, mi->dmabuflen/2);
+ else {
+ mi->demodulator(sm, buf, mi->dmabuflen/2);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ }
+ endint:
+ sm->modem.oldptt = new_ptt;
+ output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_open(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned char revreq = (mi->samplerate >= 13000) ? 3 : 2;
+
+ if (!dev || !sm)
+ return -ENXIO;
+ if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT ||
+ dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+ return -ENXIO;
+ if (check_region(dev->base_addr, SBC_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (!reset_dsp(dev))
+ return -ENODEV;
+ write_dsp(dev, SBC_GET_REVISION);
+ if (!read_dsp(dev, &sm->modem.revhi) ||
+ !read_dsp(dev, &sm->modem.revlo))
+ return -ENODEV;
+ if (sm->modem.revhi < revreq) {
+ printk(KERN_ERR "sm: sbc io 0x%lx: DSP rev %d.%02d too "
+ "old, at least %d.00 required\n", dev->base_addr,
+ sm->modem.revhi, sm->modem.revlo, revreq);
+ return -ENODEV;
+ }
+ /*
+ * initialize some variables
+ */
+ if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ sm->modem.shreg = sm->modem.last_sample = 0;
+ sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
+ sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
+ sm->modem.dmabufidx = sm->modem.oldptt = 0;
+ sm->modem.dmabufw = NULL;
+ sm->modem.dcd_time = 120;
+ sm->modem.dcd_sum0 = 2;
+#if 0
+ if (!dev->irq) {
+ int irq = probe_int(dev, sm);
+ if (irq < 0) {
+ printk(KERN_ERR "sm: irq autoprobe failed\n");
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ return -EBUSY;
+ }
+ dev->irq = irq;
+ }
+#endif
+ if (request_dma(dev->dma, mi->mode_name)) {
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT,
+ mi->mode_name, dev)) {
+ free_dma(dev->dma);
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, SBC_EXTENT, mi->mode_name);
+ setup_dma_dsp(dev, 0);
+ output_open(sm);
+ printk(KERN_INFO "sm: sbc at iobase 0x%lx irq %u dma "
+ "%u DSP revision %u.%02u\n", dev->base_addr, dev->irq,
+ dev->dma, (unsigned int)sm->modem.revhi,
+ (unsigned int)sm->modem.revlo);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_close(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (!dev || !sm)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ disable_dma(dev->dma);
+ reset_dsp(dev);
+ free_irq(dev->irq, dev);
+ free_dma(dev->dma);
+ release_region(dev->base_addr, SBC_EXTENT);
+ kfree(sm->modem.dmabufr);
+ output_close(sm);
+ printk(KERN_INFO "sm: close sbc at iobase 0x%lx irq %u dma %u\n",
+ dev->base_addr, dev->irq, dev->dma);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#endif /* ENABLE_SBC */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== Windows Sound System specific routines ==========
+ */
+
+#if defined(ENABLE_WSS) || defined(ENABLE_WSSFDX)
+
+static void write_codec(struct device *dev, unsigned char idx,
+ unsigned char data)
+{
+ int timeout = 900000;
+
+ /* wait until codec ready */
+ while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+ timeout--;
+ outb(idx, WSS_CODEC_IA(dev->base_addr));
+ outb(data, WSS_CODEC_ID(dev->base_addr));
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct device *dev, unsigned char idx)
+{
+ int timeout = 900000;
+
+ /* wait until codec ready */
+ while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+ timeout--;
+ outb(idx & 0xf, WSS_CODEC_IA(dev->base_addr));
+ return inb(WSS_CODEC_ID(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline wss_ack_int(struct device *dev)
+{
+ outb(0, WSS_CODEC_STATUS(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct device *dev, unsigned char sdc,
+ unsigned char src_l, unsigned char src_r,
+ int igain_l, int igain_r,
+ int ogain_l, int ogain_r)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+
+ unsigned char tmp, reg0, reg1, reg6, reg7;
+ static const signed char irqtab[16] =
+ { -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
+ -1, -1 };
+ static const signed char dmatab[4] = { 1, 2, -1, 3 };
+ unsigned long time;
+
+ tmp = inb(WSS_STATUS(dev->base_addr));
+ if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 &&
+ (tmp & 0x3f) != 0x0f) {
+ printk(KERN_ERR "sm: WSS card not found, address 0x%lx, ID "
+ "register 0x%02x\n", dev->base_addr, (int)tmp);
+ return -1;
+ }
+ if ((tmp & 0x80) && ((dev->dma == 0) || ((dev->irq >= 8) &&
+ (dev->irq != 9)))) {
+ printk(KERN_ERR "sm: WSS: DMA0 and/or IRQ8..IRQ15 (except "
+ "IRQ9) cannot be used on an 8bit card\n");
+ return -1;
+ }
+ if (dev->irq > 15 || irqtab[dev->irq] == -1) {
+ printk(KERN_ERR "sm: WSS: invalid interrupt %d\n",
+ (int)dev->irq);
+ return -1;
+ }
+ if (dev->dma > 3 || dmatab[dev->dma] == -1) {
+ printk(KERN_ERR "sm: WSS: invalid dma channel %d\n",
+ (int)dev->dma);
+ return -1;
+ }
+ tmp = irqtab[dev->irq] | dmatab[dev->dma];
+ outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); /* irq probe */
+ if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
+ outb(0, WSS_CONFIG(dev->base_addr));
+ printk(KERN_ERR "sm: WSS: IRQ%d is not free!\n", dev->irq);
+ }
+ outb(tmp, WSS_CONFIG(dev->base_addr));
+ /*
+ * initialize the codec
+ */
+ if (igain_l < 0)
+ igain_l = 0;
+ if (igain_r < 0)
+ igain_r = 0;
+ if (ogain_l > 0)
+ ogain_l = 0;
+ if (ogain_r > 0)
+ ogain_r = 0;
+ reg0 = (src_l << 6) & 0xc0;
+ reg1 = (src_r << 6) & 0xc0;
+ if (reg0 == 0x80 && igain_l >= 20) {
+ reg0 |= 0x20;
+ igain_l -= 20;
+ }
+ if (reg1 == 0x80 && igain_r >= 20) {
+ reg1 |= 0x20;
+ igain_r -= 20;
+ }
+ if (igain_l > 23)
+ igain_l = 23;
+ if (igain_r > 23)
+ igain_r = 23;
+ reg0 |= igain_l * 2 / 3;
+ reg1 |= igain_r * 2 / 3;
+ reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
+ reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
+#if 1
+ write_codec(dev, 9, 0);
+ write_codec(dev, 15, 0xaa);
+ write_codec(dev, 14, 0x55);
+ if ((read_codec(dev, 15) != 0xaa) || (read_codec(dev, 14) != 0x55))
+ goto codec_err;
+#endif
+ write_codec(dev, 0x48, mi->data_fmt); /* Clock and data format register */
+ write_codec(dev, 0x49, sdc ? 0xc : 0x8); /* MCE and interface config reg */
+ /* single DMA channel, disable both DMA */
+ /* clear MCE and wait until ACI set */
+ time = jiffies + HZ/4;
+ while (!(read_codec(dev, 0x0b) & 0x20) &&
+ ((signed)(jiffies - time) < 0));
+ /* wait until ACI cleared */
+ while ((read_codec(dev, 0x0b) & 0x20) &&
+ ((signed)(jiffies - time) < 0));
+ if ((signed)(jiffies - time) >= 0) {
+ printk(KERN_WARNING "sm: ad1848 auto calibration timed out\n");
+ goto codec_err;
+ }
+ write_codec(dev, 0, reg0); /* left input control */
+ write_codec(dev, 1, reg1); /* right input control */
+ write_codec(dev, 2, 0x80); /* left aux#1 input control */
+ write_codec(dev, 3, 0x80); /* right aux#1 input control */
+ write_codec(dev, 4, 0x80); /* left aux#2 input control */
+ write_codec(dev, 5, 0x80); /* right aux#2 input control */
+ write_codec(dev, 6, reg6); /* left dac control */
+ write_codec(dev, 7, reg7); /* right dac control */
+ write_codec(dev, 0xa, 0x2); /* pin control register */
+ write_codec(dev, 0xd, 0x0); /* digital mix control */
+ sm->modem.revhi = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
+ sm->modem.revlo = read_codec(dev, 0xc) & 0xf;
+ /*
+ * print revisions
+ */
+ printk(KERN_INFO "sm: WSS revision %d, CODEC revision %d\n",
+ (int)sm->modem.revhi, (int)sm->modem.revlo);
+ return 0;
+ codec_err:
+ outb(0, WSS_CONFIG(dev->base_addr));
+ printk(KERN_ERR "sm: no WSS soundcard found at address 0x%lx\n",
+ dev->base_addr);
+ return -1;
+}
+
+#endif /* defined(ENABLE_WSS) || defined(ENABLE_WSSFDX) */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_WSS
+
+static void setup_dma_wss(struct device *dev, int flg)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned long flags;
+ static const unsigned char codecmode[2] = { 0x0e, 0x0d };
+ static const unsigned char dmamode[2] =
+ { DMA_MODE_READ | DMA_MODE_AUTOINIT,
+ DMA_MODE_WRITE | DMA_MODE_AUTOINIT};
+ unsigned char oldcodecmode, codecdma;
+ long abrt;
+ unsigned long dmabufaddr = virt_to_bus(sm->modem.dmabufr);
+
+ if ((dmabufaddr & 0xffff) + mi->dmabuflen > 0x10000)
+ panic("sm: DMA buffer violates DMA boundary!");
+ flg = !!flg;
+ save_flags(flags);
+ cli();
+ /*
+ * perform the final DMA sequence to disable the codec request
+ */
+ oldcodecmode = read_codec(dev, 9);
+ write_codec(dev, 9, 0xc); /* disable codec */
+ wss_ack_int(dev);
+ if ((codecdma = read_codec(dev, 11)) & 0x10) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]);
+ set_dma_addr(dev->dma, dmabufaddr);
+ set_dma_count(dev->dma, mi->dmabuflen);
+ enable_dma(dev->dma);
+ abrt = 0;
+ while (((codecdma = read_codec(dev, 11)) & 0x10) ||
+ ((++abrt) >= 0x10000));
+ }
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, dmamode[flg]);
+ set_dma_addr(dev->dma, dmabufaddr);
+ set_dma_count(dev->dma, mi->dmabuflen);
+ enable_dma(dev->dma);
+ write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff);
+ write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8);
+ write_codec(dev, 9, codecmode[flg]);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned char new_ptt;
+ unsigned char *buf;
+ unsigned long flags;
+ int dmares;
+
+ if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ new_ptt = hdlcdrv_ptt(&sm->hdrv);
+ save_flags(flags);
+ cli();
+ wss_ack_int(dev);
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ dmares = get_dma_residue(dev->dma);
+ enable_dma(dev->dma);
+ if (dmares <= 0)
+ dmares = mi->dmabuflen;
+ buf = sm->modem.dmabufr;
+ if (dmares > mi->dmabuflen/2)
+ buf += mi->dmabuflen/2;
+ if (dmares > mi->dmabuflen/2)
+ dmares -= mi->dmabuflen/2;
+#ifdef SM_DEBUG
+ if (!sm->debug_vals.last_pllcorr ||
+ dmares < sm->debug_vals.last_pllcorr)
+ sm->debug_vals.last_pllcorr = dmares;
+#endif /* SM_DEBUG */
+ dmares--;
+ write_codec(dev, 15, dmares & 0xff);
+ write_codec(dev, 14, dmares >> 8);
+ restore_flags(flags);
+ if (sm->modem.oldptt != new_ptt) {
+ disable_dma(dev->dma);
+ sti();
+ sm->modem.dmabufidx = 0;
+ if (!new_ptt) {
+ setup_dma_wss(dev, 0);
+ goto endint;
+ }
+ mi->demodulator(sm, buf, mi->dmabuflen/2);
+ mi->modulator(sm, sm->modem.dmabufr, mi->dmabuflen/2);
+ setup_dma_wss(dev, 1);
+ mi->modulator(sm, sm->modem.dmabufr + mi->dmabuflen/2,
+ mi->dmabuflen/2);
+ goto endint;
+ }
+ sm_int_freq(sm);
+ sti();
+ /*
+ * check if transmitter active
+ */
+ if (new_ptt)
+ mi->modulator(sm, buf, mi->dmabuflen/2);
+ else {
+ mi->demodulator(sm, buf, mi->dmabuflen/2);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ }
+ endint:
+ sm->modem.oldptt = new_ptt;
+ output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_open(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+
+ if (!dev || !sm)
+ return -ENXIO;
+ if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT ||
+ dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+ return -ENXIO;
+ if (check_region(dev->base_addr, WSS_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (wss_init_codec(dev, 1, 1, 1, 0, 0, -45, -45))
+ return -ENODEV;
+ /*
+ * initialize some variables
+ */
+ if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ sm->modem.shreg = sm->modem.last_sample = 0;
+ sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
+ sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
+ sm->modem.dmabufidx = sm->modem.oldptt = 0;
+ sm->modem.dmabufw = NULL;
+ sm->modem.dcd_time = 120;
+ sm->modem.dcd_sum0 = 2;
+ if (request_dma(dev->dma, mi->mode_name)) {
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT,
+ mi->mode_name, dev)) {
+ free_dma(dev->dma);
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, WSS_EXTENT, mi->mode_name);
+ setup_dma_wss(dev, 0);
+ output_open(sm);
+ printk(KERN_INFO "sm: wss at iobase 0x%lx irq %u dma "
+ "%u\n", dev->base_addr, dev->irq, dev->dma);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (!dev || !sm)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ disable_dma(dev->dma);
+ write_codec(dev, 9, 0xc); /* disable codec */
+ free_irq(dev->irq, dev);
+ free_dma(dev->dma);
+ release_region(dev->base_addr, WSS_EXTENT);
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ output_close(sm);
+ printk(KERN_INFO "sm: close wss at iobase 0x%lx irq %u"
+ " dma %u\n", dev->base_addr, dev->irq, dev->dma);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#endif /* ENABLE_WSS */
+
+/* --------------------------------------------------------------------- */
+/*
+ * =========== Windows Sound System Fullduplex specific routines ==========
+ */
+
+/*
+ * This does _not_ work on my hardware
+ */
+
+#ifdef ENABLE_WSSFDX
+
+static void setup_dma_wssfdx(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned long flags;
+ unsigned char codecdma;
+ long abrt;
+ unsigned long dmabufraddr = virt_to_bus(sm->modem.dmabufr);
+ unsigned long dmabufwaddr = virt_to_bus(sm->modem.dmabufw);
+
+ if (((dmabufraddr & 0xffff) + mi->dmabuflen > 0x10000) ||
+ ((dmabufwaddr & 0xffff) + mi->dmabuflen > 0x10000))
+ panic("sm: DMA buffer violates DMA boundary!");
+ save_flags(flags);
+ cli();
+ /*
+ * perform the final DMA sequence to disable the codec request
+ */
+ write_codec(dev, 9, 0x8); /* disable codec */
+ wss_ack_int(dev);
+ if ((codecdma = read_codec(dev, 11)) & 0x10) {
+ disable_dma(dev->dma);
+ disable_dma(!dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->dma, dmabufwaddr);
+ set_dma_count(dev->dma, mi->dmabuflen);
+ set_dma_mode(!dev->dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(!dev->dma, dmabufraddr);
+ set_dma_count(!dev->dma, mi->dmabuflen);
+ enable_dma(dev->dma);
+ enable_dma(!dev->dma);
+ abrt = 0;
+ while (((codecdma = read_codec(dev, 11)) & 0x10) ||
+ ((++abrt) >= 0x10000));
+ }
+ disable_dma(dev->dma);
+ disable_dma(!dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+ set_dma_addr(dev->dma, dmabufwaddr);
+ set_dma_count(dev->dma, mi->dmabuflen);
+ set_dma_mode(!dev->dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+ set_dma_addr(!dev->dma, dmabufraddr);
+ set_dma_count(!dev->dma, mi->dmabuflen);
+ enable_dma(dev->dma);
+ enable_dma(!dev->dma);
+ write_codec(dev, 15, ((mi->dmabuflen >> 1) - 1) & 0xff);
+ write_codec(dev, 14, ((mi->dmabuflen >> 1) - 1) >> 8);
+ write_codec(dev, 9, 0x0b);
+ restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int msgcnt = 10;
+
+static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+ unsigned char new_ptt;
+ unsigned char *bufr;
+ unsigned char *bufw;
+ unsigned long flags;
+ int dmares, dmares2, i;
+
+ if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+ return;
+ new_ptt = hdlcdrv_ptt(&sm->hdrv);
+ save_flags(flags);
+ cli();
+ wss_ack_int(dev);
+ disable_dma(dev->dma);
+ disable_dma(!dev->dma);
+ clear_dma_ff(dev->dma);
+ dmares = get_dma_residue(dev->dma);
+ dmares2 = get_dma_residue(!dev->dma);
+ enable_dma(dev->dma);
+ enable_dma(!dev->dma);
+ if (dmares <= 0)
+ dmares = mi->dmabuflen;
+ if (dmares2 <= 0)
+ dmares2 = mi->dmabuflen;
+ bufw = sm->modem.dmabufw;
+ if (dmares > mi->dmabuflen/2)
+ bufw += mi->dmabuflen/2;
+ bufr = sm->modem.dmabufr;
+ if (dmares2 > mi->dmabuflen/2)
+ bufr += mi->dmabuflen/2;
+ if ((i = dmares) > mi->dmabuflen/2)
+ i -= mi->dmabuflen/2;
+#ifdef SM_DEBUG
+ if (!sm->debug_vals.last_pllcorr ||
+ i < sm->debug_vals.last_pllcorr)
+ sm->debug_vals.last_pllcorr = i;
+#endif /* SM_DEBUG */
+ i--;
+ write_codec(dev, 15, i & 0xff);
+ write_codec(dev, 14, i >> 8);
+ restore_flags(flags);
+ sm_int_freq(sm);
+ sti();
+
+ if (msgcnt > 0) {
+ msgcnt--;
+ printk(KERN_DEBUG "sm: DMA residue: playback: %d "
+ "capture: %d\n", dmares, dmares2);
+ }
+
+ /*
+ * check if transmitter active
+ */
+ if (new_ptt)
+ mi->modulator(sm, bufw, mi->dmabuflen/2);
+ else
+ memset(bufw, 0x80, mi->dmabuflen/2);
+ mi->demodulator(sm, bufr, mi->dmabuflen/2);
+ hdlcdrv_arbitrate(dev, &sm->hdrv);
+ sm->modem.oldptt = new_ptt;
+ output_status(sm);
+ hdlcdrv_transmitter(dev, &sm->hdrv);
+ hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+ struct modem_info *mi = (struct modem_info *)sm->hdrv.ops;
+
+ if (!dev || !sm)
+ return -ENXIO;
+ if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT ||
+ dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+ return -ENXIO;
+ if (check_region(dev->base_addr, WSS_EXTENT))
+ return -EACCES;
+ /*
+ * check if a card is available
+ */
+ if (wss_init_codec(dev, 0, 1, 1, 0, 0, -45, -45))
+ return -ENODEV;
+ /*
+ * initialize some variables
+ */
+ if (!(sm->modem.dmabufr = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA)))
+ return -ENOMEM;
+ if (!(sm->modem.dmabufw = kmalloc(mi->dmabuflen, GFP_KERNEL | GFP_DMA))) {
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ return -ENOMEM;
+ }
+ sm->modem.shreg = sm->modem.last_sample = 0;
+ sm->modem.bit_pll = sm->modem.dcd_shreg = sm->modem.dcd_sum1 = 0;
+ sm->modem.dcd_sum2 = sm->modem.last_rxbit = sm->modem.tx_bit = 0;
+ sm->modem.dmabufidx = sm->modem.oldptt = 0;
+ sm->modem.dcd_time = 120;
+ sm->modem.dcd_sum0 = 2;
+ if (request_dma(dev->dma, mi->mode_name)) {
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ kfree_s(sm->modem.dmabufw, mi->dmabuflen);
+ return -EBUSY;
+ }
+ if (request_dma(!dev->dma, mi->mode_name)) {
+ free_dma(dev->dma);
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ kfree_s(sm->modem.dmabufw, mi->dmabuflen);
+ return -EBUSY;
+ }
+ if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT,
+ mi->mode_name, dev)) {
+ free_dma(dev->dma);
+ free_dma(!dev->dma);
+ kfree_s(sm->modem.dmabufr, mi->dmabuflen);
+ kfree_s(sm->modem.dmabufw, mi->dmabuflen);
+ return -EBUSY;
+ }
+ request_region(dev->base_addr, WSS_EXTENT, mi->mode_name);
+ setup_dma_wssfdx(dev);
+ output_open(sm);
+ printk(KERN_INFO "sm: wss fdx at iobase 0x%lx irq %u dma1 "
+ "%u dma2 %u\n", dev->base_addr, dev->irq, dev->dma, !dev->dma);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_close(struct device *dev)
+{
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (!dev || !sm)
+ return -EINVAL;
+ /*
+ * disable interrupts
+ */
+ disable_dma(dev->dma);
+ disable_dma(!dev->dma);
+ write_codec(dev, 9, 0x8); /* disable codec */
+ free_irq(dev->irq, dev);
+ free_dma(dev->dma);
+ free_dma(!dev->dma);
+ release_region(dev->base_addr, WSS_EXTENT);
+ kfree(sm->modem.dmabufr);
+ kfree(sm->modem.dmabufw);
+ output_close(sm);
+ printk(KERN_INFO "sm: close wss fdx at iobase 0x%lx irq %u"
+ " dma %u\n", dev->base_addr, dev->irq, dev->dma);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#endif /* ENABLE_WSSFDX */
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+
+#define SBC1200_SRATE (256-104) /* the SBC sampling rate, 256-(1E6/srate) */
+#define SBC1200_DMABUFLEN 192 /* DMA buffer duration exactly 20ms */
+
+#define SBC9600_SRATE (256-26) /* the SBC sampling rate, 256-(1E6/srate) */
+#define SBC9600_DMABUFLEN 768 /* DMA buffer duration exactly 20ms */
+
+#define WSS1200_DATAFMT 0x0e /* 8bit unsigned PCM, Mono, XTAL1, 9.6kHz */
+#define WSS1200_DMABUFLEN 192 /* DMA buffer duration exactly 20ms */
+
+#define WSS9600_DATAFMT 0x0c /* 8bit unsigned PCM, Mono, XTAL1, 48kHz */
+#define WSS9600_DMABUFLEN 960 /* DMA buffer duration exactly 20ms */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_SBC
+
+static const struct modem_info sbc1200_ops = {
+{ 1200, sbc_open, sbc_close, sm_ioctl }, 9600, 1, 8, "sbc_1200",
+SBC1200_SRATE, SBC1200_DMABUFLEN,
+modulator_1200, demodulator_1200
+};
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info sbc9600_ops = {
+{ 9600, sbc_open, sbc_close, sm_ioctl }, 38400, 1, 4, "sbc_9600",
+SBC9600_SRATE, SBC9600_DMABUFLEN,
+modulator_9600_4, demodulator_9600_4
+};
+
+#endif /* ENABLE_SBC */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_WSS
+
+static const struct modem_info wss1200_ops = {
+{ 1200, wss_open, wss_close, sm_ioctl }, 9600, 0, 8, "wss_1200",
+WSS1200_DATAFMT, WSS1200_DMABUFLEN,
+modulator_1200, demodulator_1200
+};
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info wss9600_ops = {
+{ 9600, wss_open, wss_close, sm_ioctl }, 48000, 0, 5, "wss_9600",
+WSS9600_DATAFMT, WSS9600_DMABUFLEN,
+modulator_9600_5, demodulator_9600_5
+};
+
+#endif /* ENABLE_WSS */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef ENABLE_WSSFDX
+
+static const struct modem_info wss1200fdx_ops = {
+{ 1200, wssfdx_open, wssfdx_close, sm_ioctl }, 9600, 0, 8, "wss_fdx_1200",
+WSS1200_DATAFMT, WSS1200_DMABUFLEN,
+modulator_1200, demodulator_1200
+};
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info wss9600fdx_ops = {
+{ 9600, wssfdx_open, wssfdx_close, sm_ioctl }, 48000, 0, 5, "wss_fdx_9600",
+WSS9600_DATAFMT, WSS9600_DMABUFLEN,
+modulator_9600_5, demodulator_9600_5
+};
+
+#endif /* ENABLE_WSSFDX */
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info dummy_ops = {
+{ 0, NULL, NULL, sm_ioctl }, 0, 0, 0, "none",
+0, 0,
+NULL, NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_info *ops_tab[3][2] = {
+#ifdef ENABLE_SBC
+{ &sbc1200_ops, &sbc9600_ops },
+#else /* ENABLE_SBC */
+{ &dummy_ops, &dummy_ops },
+#endif /* ENABLE_SBC */
+#ifdef ENABLE_WSS
+{ &wss1200_ops, &wss9600_ops },
+#else /* ENABLE_WSS */
+{ &dummy_ops, &dummy_ops },
+#endif /* ENABLE_WSS */
+#ifdef ENABLE_WSSFDX
+{ &wss1200fdx_ops, &wss9600fdx_ops }
+#else /* ENABLE_WSSFDX */
+{ &dummy_ops, &dummy_ops }
+#endif /* ENABLE_WSSFDX */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int i;
+ struct sm_state *sm;
+ struct sm_ioctl bi;
+ unsigned long flags;
+ unsigned int newdiagmode;
+ unsigned int newdiagflags;
+
+ if (!dev || !dev->priv ||
+ ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+ printk(KERN_ERR "sm_ioctl: invalid device struct\n");
+ return -EINVAL;
+ }
+ sm = (struct sm_state *)dev->priv;
+
+ if (cmd != SIOCDEVPRIVATE)
+ return -ENOIOCTLCMD;
+ if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+ return -EFAULT;
+
+ switch (bi.cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+ case SMCTL_GETMODEMTYPE:
+ bi.data.cfg.hardware = sm->config.hardware;
+ bi.data.cfg.mode = sm->config.mode;
+ break;
+
+ case SMCTL_SETMODEMTYPE:
+ if (!suser() || dev->start)
+ return -EACCES;
+ if (bi.data.cfg.hardware < SM_HARDWARE_INVALID ||
+ bi.data.cfg.hardware > SM_HARDWARE_WSSFDX ||
+ bi.data.cfg.mode < SM_MODE_INVALID ||
+ bi.data.cfg.mode > SM_MODE_FSK9600)
+ return -EINVAL;
+ sm->config.hardware = bi.data.cfg.hardware;
+ sm->config.mode = bi.data.cfg.mode;
+ if (bi.data.cfg.hardware == SM_HARDWARE_INVALID ||
+ bi.data.cfg.mode == SM_MODE_INVALID)
+ sm->hdrv.ops = &dummy_ops.hops;
+ else {
+ sm->hdrv.ops = &ops_tab[sm->config.hardware][sm->config.mode]->hops;
+ if (!((struct modem_info *)sm->hdrv.ops)->samplerate)
+ return -ENODEV;
+ }
+ return 0;
+
+#ifdef SM_DEBUG
+ case SMCTL_GETDEBUG:
+ bi.data.dbg.debug1 = sm->hdrv.ptt_keyed;
+ bi.data.dbg.debug2 = sm->debug_vals.last_intcnt;
+ bi.data.dbg.debug3 = sm->debug_vals.last_pllcorr;
+ break;
+#endif /* SM_DEBUG */
+
+ case SMCTL_GETMIXER:
+ i = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(bi));
+ if (i)
+ return i;
+ i = 0;
+ bi.data.mix.sample_rate = ((struct modem_info *)sm->hdrv.ops)->samplerate;
+ bi.data.mix.bit_rate = sm->hdrv.ops->bitrate;
+ if (((struct modem_info *)sm->hdrv.ops)->sbcmix) {
+ switch (sm->modem.revhi) {
+ case 2:
+ bi.data.mix.mixer_type = SM_MIXER_CT1335;
+ break;
+ case 3:
+ bi.data.mix.mixer_type = SM_MIXER_CT1345;
+ break;
+ case 4:
+ bi.data.mix.mixer_type = SM_MIXER_CT1745;
+ break;
+ }
+ if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
+ bi.data.mix.reg < 0x80) {
+ save_flags(flags);
+ cli();
+ outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+ bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
+ restore_flags(flags);
+ i = 1;
+ }
+ } else {
+ bi.data.mix.mixer_type = SM_MIXER_AD1848;
+ if ((0x20ff >> bi.data.mix.reg) & 1) {
+ bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
+ i = 1;
+ }
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return i;
+
+ case SMCTL_SETMIXER:
+ if (!suser())
+ return -EACCES;
+ if (((struct modem_info *)sm->hdrv.ops)->sbcmix) {
+ switch (sm->modem.revhi) {
+ case 2:
+ if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
+ return -EINVAL;
+ break;
+ case 3:
+ if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
+ return -EINVAL;
+ break;
+ case 4:
+ if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
+ return -EINVAL;
+ break;
+ default:
+ return -ENODEV;
+ }
+ if (bi.data.mix.reg >= 0x80)
+ return -EACCES;
+ save_flags(flags);
+ cli();
+ outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+ outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
+ restore_flags(flags);
+ return 0;
+ } else {
+ if (bi.data.mix.mixer_type != SM_MIXER_AD1848)
+ return -EINVAL;
+ if (!((0x20ff >> bi.data.mix.reg) & 1))
+ return -EACCES;
+ write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
+ return 0;
+ }
+
+ case SMCTL_DIAGNOSE:
+ newdiagmode = bi.data.diag.mode;
+ newdiagflags = bi.data.diag.flags;
+ if (newdiagmode > SM_DIAGMODE_DEMOD)
+ return -EINVAL;
+ bi.data.diag.mode = sm->diag.mode;
+ bi.data.diag.flags = sm->diag.flags;
+ bi.data.diag.samplesperbit = ((struct modem_info *)sm->hdrv.ops)->sperbit;
+ if (sm->diag.mode != newdiagmode) {
+ save_flags(flags);
+ cli();
+ sm->diag.ptr = -1;
+ sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+ sm->diag.mode = newdiagmode;
+ restore_flags(flags);
+ break;
+ }
+ if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF)
+ break;
+ if (bi.data.diag.datalen > DIAGDATALEN)
+ bi.data.diag.datalen = DIAGDATALEN;
+ if (sm->diag.ptr < bi.data.diag.datalen)
+ break;
+ i = verify_area(VERIFY_WRITE, bi.data.diag.data,
+ bi.data.diag.datalen *
+ sizeof(short));
+ if (i)
+ return i;
+ if (copy_to_user(bi.data.diag.data, sm->diag.data,
+ bi.data.diag.datalen * sizeof(short)))
+ return -EFAULT;
+ bi.data.diag.flags |= SM_DIAGFLAG_VALID;
+ save_flags(flags);
+ cli();
+ sm->diag.ptr = -1;
+ sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+ sm->diag.mode = newdiagmode;
+ restore_flags(flags);
+ break;
+
+ }
+ if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+ return -EFAULT;
+ return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static
+#endif /* MODULE */
+int sm_init(void)
+{
+ int i, j, found = 0;
+ char set_hw = 1;
+ struct sm_state *sm;
+ char ifname[HDLCDRV_IFNAMELEN];
+
+ printk(KERN_INFO "sm: compiled %s %s\n", __TIME__, __DATE__);
+ /*
+ * register net devices
+ */
+ for (i = 0; i < NR_PORTS; i++) {
+ struct device *dev = sm_device+i;
+ sprintf(ifname, "sm%d", i);
+
+ if (sm_ports[i].hardware < SM_HARDWARE_INVALID ||
+ sm_ports[i].hardware > SM_HARDWARE_WSSFDX ||
+ sm_ports[i].mode < SM_MODE_INVALID ||
+ sm_ports[i].mode > SM_MODE_FSK9600)
+ set_hw = 0;
+ if (set_hw) {
+ j = hdlcdrv_register_hdlcdrv(dev, &ops_tab[sm_ports[i].hardware]
+ [sm_ports[i].mode]->hops,
+ sizeof(struct sm_state), ifname,
+ sm_ports[i].iobase,
+ sm_ports[i].irq,
+ sm_ports[i].dma);
+ if (!j) {
+ sm = (struct sm_state *)dev->priv;
+ sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+ sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+ sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+ }
+ } else
+ j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops.hops,
+ sizeof(struct sm_state),
+ ifname, 0, 0, 0);
+ if (j) {
+ printk(KERN_WARNING "sm: cannot register net "
+ "device\n");
+ } else
+ found++;
+ }
+ if (!found)
+ return -ENXIO;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+int hardware = SM_HARDWARE_INVALID;
+int mode = SM_MODE_INVALID;
+int iobase = -1;
+int irq = -1;
+int dma = -1;
+int seriobase = 0;
+int pariobase = 0;
+int midiiobase = 0;
+
+int init_module(void)
+{
+ printk(KERN_INFO "sm: v0.1 (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+
+ if (hardware != SM_HARDWARE_INVALID) {
+ if (iobase == -1)
+ iobase = (hardware == SM_HARDWARE_SBC) ? 0x220 : 0x530;
+ if (irq == -1)
+ irq = (hardware == SM_HARDWARE_SBC) ? 5 : 11;
+ if (dma == -1)
+ dma = 1;
+ }
+ sm_ports[0].hardware = hardware;
+ sm_ports[0].mode = mode;
+ sm_ports[0].iobase = iobase;
+ sm_ports[0].irq = irq;
+ sm_ports[0].dma = dma;
+ sm_ports[0].seriobase = seriobase;
+ sm_ports[0].pariobase = pariobase;
+ sm_ports[0].midiiobase = midiiobase;
+ sm_ports[1].hardware = SM_HARDWARE_INVALID;
+
+ return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+ int i;
+
+ printk(KERN_INFO "sm: cleanup_module called\n");
+
+ for(i = 0; i < NR_PORTS; i++) {
+ struct device *dev = sm_device+i;
+ struct sm_state *sm = (struct sm_state *)dev->priv;
+
+ if (sm) {
+ if (sm->hdrv.magic != HDLCDRV_MAGIC)
+ printk(KERN_ERR "sm: invalid magic in "
+ "cleanup_module\n");
+ else
+ hdlcdrv_unregister_hdlcdrv(dev);
+ }
+ }
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: sm=hw,mode,io,irq,dma,serio,pario[,hw,mode,io,irq,dma,serio,pario]
+ * hw=0: SBC, hw=1: WSS; mode=0: AFSK1200, mode=1: FSK9600
+ */
+
+void sm_setup(char *str, int *ints)
+{
+ int i;
+
+ for (i = 0; i < NR_PORTS; i++)
+ if (ints[0] >= 8*(i+1)) {
+ sm_ports[i].hardware = ints[8*i+1];
+ sm_ports[i].mode = ints[8*i+2];
+ sm_ports[i].iobase = ints[8*i+3];
+ sm_ports[i].irq = ints[8*i+4];
+ sm_ports[i].dma = ints[8*i+5];
+ sm_ports[i].seriobase = ints[8*i+6];
+ sm_ports[i].pariobase = ints[8*i+7];
+ sm_ports[i].midiiobase = ints[8*i+8];
+ } else
+ sm_ports[i].hardware = SM_HARDWARE_INVALID;
+
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
} 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);
+ }
}
}
if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n");
for(i=0; i<SCpnt->use_sg; i++)
{
- cptr[i].dataptr = (long) sgpnt[i].address;
cptr[i].datalen = sgpnt[i].length;
+ cptr[i].dataptr = virt_to_bus(sgpnt[i].address);
}
ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
- ecb[ecbno].dataptr = (long) cptr;
+ ecb[ecbno].dataptr = virt_to_bus(cptr);
#ifdef DEBUG
printk("cptr %x: ",cptr);
ptr = (unsigned char *) cptr;
{
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
#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,
#ifdef CONFIG_SCSI_NCR53C8XX
NCR53C8XX,
#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+ EATA_DMA,
+#endif
+#ifdef CONFIG_SCSI_EATA_PIO
+ EATA_PIO,
+#endif
#ifdef CONFIG_SCSI_7000FASST
WD7000,
#endif
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",
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);
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;
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
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
#include <linux/malloc.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/string.h>
#include <linux/ncp.h>
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));
#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;
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, \
0, 0, 0, \
0, 0, 0, \
0, \
- { 0 } \
+ 0 \
}
#define alloc_kernel_stack() __get_free_page(GFP_KERNEL)
-#ifndef _ASM_SEGMENT_H
-#define _ASM_SEGMENT_H
+#ifndef __ALPHA_UACCESS_H
+#define __ALPHA_UACCESS_H
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+
/*
* The fs value determines whether argument validity checking should be
* For historical reasons, these macros are grossly misnamed.
*/
-#define KERNEL_DS 0
-#define USER_DS 1
+#define KERNEL_DS (0UL)
+#define USER_DS (-0x40000000000UL)
#define VERIFY_READ 0
#define VERIFY_WRITE 1
-#define get_fs() (current->tss.flags & 0x1)
-#define set_fs(x) (current->tss.flags = (current->tss.flags & ~0x1) | ((x) & 0x1))
-
-static inline unsigned long get_ds(void)
-{
- return 0;
-}
+#define get_fs() (current->tss.fs)
+#define set_fs(x) (current->tss.fs = (x))
+#define get_ds() (KERNEL_DS)
/*
* Is a address valid? This does a straighforward calculation rather
* - 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.
* (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; \
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; \
})
({ \
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);
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; \
/* 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 */
*/
#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.
*/
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 }
_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)
* User space memory access functions
*/
#include <linux/sched.h>
-
#include <asm/segment.h>
#define VERIFY_READ 0
* The fs value determines whether argument validity checking should be
* performed or not. If get_fs() == USER_DS, checking is performed, with
* get_fs() == KERNEL_DS, checking is bypassed.
- *
+ *
* For historical reasons, these macros are grossly misnamed.
*/
-
+
#define get_fs() (current->tss.segment)
#define set_fs(x) (current->tss.segment = (x))
#define get_ds() (KERNEL_DS)
#define __user_ok(addr,size) \
-((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size))
+ ((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size))
#define __kernel_ok \
-(get_fs() == KERNEL_DS)
+ (get_fs() == KERNEL_DS)
extern int __verify_write(const void *, unsigned long);
#if CPU > 386
#define __access_ok(type,addr,size) \
-(__kernel_ok || __user_ok(addr,size))
+ (__kernel_ok || __user_ok(addr,size))
#else
#define __access_ok(type,addr,size) \
-(__kernel_ok || (__user_ok(addr,size) && \
- ((type) == VERIFY_READ || wp_works_ok || __verify_write((void *)(addr),(size)))))
+ (__kernel_ok || (__user_ok(addr,size) && \
+ ((type) == VERIFY_READ || wp_works_ok || \
+ __verify_write((void *)(addr),(size)))))
#endif /* CPU */
#define access_ok(type,addr,size) \
-__access_ok((type),(unsigned long)(addr),(size))
+ __access_ok((type),(unsigned long)(addr),(size))
+
+extern inline int verify_area(int type, const void * addr, unsigned long size)
+{
+ return access_ok(type,addr,size) ? 0 : -EFAULT;
+}
+
/*
- * Uh, these should become the main single-value transfer routines..
- * They automatically use the right size if we just have the right
- * pointer type..
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue. No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path. This means when everything is well,
+ * we don't even have to jump over them. Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+
+struct exception_table_entry
+{
+ unsigned long insn, fixup;
+};
+
+/* Returns 0 if exception not found and fixup otherwise. */
+extern unsigned long search_exception_table(unsigned long);
+
+
+/*
+ * These are the main single-value transfer routines. They automatically
+ * use the right size if we just have the right pointer type.
*
* This gets kind of ugly. We want to return _two_ values in "get_user()"
* and yet we don't want to do any pointers, because that is too much
* with a separate "access_ok()" call (this is used when we do multiple
* accesses to the same area of user memory).
*/
+#define get_user(x,ptr) \
+ __get_user_check((x),(ptr),sizeof(*(ptr)))
#define put_user(x,ptr) \
-__do_put_user((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr))))
+ __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+
+#define __get_user(x,ptr) \
+ __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
#define __put_user(x,ptr) \
-__do_put_user_nocheck((unsigned long)((__typeof__(*(ptr)))(x)),(ptr),(sizeof(*(ptr))))
+ __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr)))
+
+
+extern long __put_user_bad(void);
+
+#define __put_user_nocheck(x,ptr,size) \
+({ \
+ long __pu_err; \
+ __put_user_size((x),(ptr),(size),__pu_err); \
+ __pu_err; \
+})
+
+#define __put_user_check(x,ptr,size) \
+({ \
+ long __pu_err = -EFAULT; \
+ __typeof__(*(ptr)) *__pu_addr = (ptr); \
+ if (access_ok(VERIFY_WRITE,__pu_addr,size)) \
+ __put_user_size((x),__pu_addr,(size),__pu_err); \
+ __pu_err; \
+})
+
+#define __put_user_size(x,ptr,size,retval) \
+do { \
+ retval = 0; \
+ switch (size) { \
+ case 1: __put_user_asm(x,ptr,retval,"b","b","iq"); break; \
+ case 2: __put_user_asm(x,ptr,retval,"w","w","ir"); break; \
+ case 4: __put_user_asm(x,ptr,retval,"l","","ir"); break; \
+ default: __put_user_bad(); \
+ } \
+} while (0)
struct __large_struct { unsigned long buf[100]; };
#define __m(x) (*(struct __large_struct *)(x))
-#define __put_user_asm(x,addr,ret,bwl,reg,rtype) \
-__asm__ __volatile__( \
- "movl $1f,%0\n\t" \
- "incl %3\n\t" \
- "mov" #bwl " %" reg "1,%2\n\t" \
- "xorl %0,%0\n\t" \
- "decl %3\n1:" \
-:"=&d" (ret) \
-:#rtype (x), "m" (__m(addr)),"m" (current->tss.ex.count))
-
-extern int __put_user_bad(void);
-
-#define __put_user_size(x,ptr,size,retval) \
-switch (size) { \
-case 1: __put_user_asm(x,ptr,retval,b,"b","iq"); break; \
-case 2: __put_user_asm(x,ptr,retval,w,"w","ir"); break; \
-case 4: __put_user_asm(x,ptr,retval,l,"","ir"); break; \
-default: retval = __put_user_bad(); }
-
-static inline int __do_put_user(unsigned long x, void * ptr, int size)
-{
- int retval = -EFAULT;
- if (access_ok(VERIFY_WRITE, ptr, size))
- __put_user_size(x,ptr,size,retval);
- return retval;
-}
-
-#define __do_put_user_nocheck(x, ptr, size) \
-({ int retval; __put_user_size(x,ptr,size,retval); retval; })
+/*
+ * Tell gcc we read from memory instead of writing: this is because
+ * we do not write to any memory gcc knows about, so there are no
+ * aliasing issues.
+ */
+#define __put_user_asm(x, addr, err, itype, rtype, ltype) \
+ __asm__ __volatile__( \
+ "1: mov"itype" %"rtype"1,%2\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: movl %3,%0\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b,3b\n" \
+ ".text" \
+ : "=r"(err) \
+ : ltype (x), "m"(__m(addr)), "i"(-EFAULT), "0"(err))
+
+
+#define __get_user_nocheck(x,ptr,size) \
+({ \
+ long __gu_err, __gu_val; \
+ __get_user_size(__gu_val,(ptr),(size),__gu_err); \
+ (x) = (__typeof__(*(ptr)))__gu_val; \
+ __gu_err; \
+})
+
+#define __get_user_check(x,ptr,size) \
+({ \
+ long __gu_err = -EFAULT, __gu_val = 0; \
+ const __typeof__(*(ptr)) *__gu_addr = (ptr); \
+ if (access_ok(VERIFY_READ,__gu_addr,size)) \
+ __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \
+ (x) = (__typeof__(*(ptr)))__gu_val; \
+ __gu_err; \
+})
+
+extern long __get_user_bad(void);
+
+#define __get_user_size(x,ptr,size,retval) \
+do { \
+ retval = 0; \
+ switch (size) { \
+ case 1: __get_user_asm(x,ptr,retval,"b","b","=q"); break; \
+ case 2: __get_user_asm(x,ptr,retval,"w","w","=r"); break; \
+ case 4: __get_user_asm(x,ptr,retval,"l","","=r"); break; \
+ default: (x) = __get_user_bad(); \
+ } \
+} while (0)
+
+#define __get_user_asm(x, addr, err, itype, rtype, ltype) \
+ __asm__ __volatile__( \
+ "1: mov"itype" %2,%"rtype"1\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: movl %3,%0\n" \
+ " xor"itype" %"rtype"1,%"rtype"1\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b,3b\n" \
+ ".text" \
+ : "=r"(err), ltype (x) \
+ : "m"(__m(addr)), "i"(-EFAULT), "0"(err))
-#define get_user(x,ptr) \
-__do_get_user((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
-#define __get_user(x,ptr) \
-__do_get_user_nocheck((x),(unsigned long)(ptr),sizeof(*(ptr)),__typeof__(*(ptr)))
-
-#define __do_get_user(x,ptr,size,type) ({ \
-unsigned long __gu_addr = ptr; \
-int __gu_ret = -EFAULT; \
-unsigned long __gu_val = 0; \
-if (access_ok(VERIFY_READ,__gu_addr,size)) { \
-switch (size) { \
-case 1: __do_get_user_8(__gu_val,__gu_addr,__gu_ret); break; \
-case 2: __do_get_user_16(__gu_val,__gu_addr,__gu_ret); break; \
-case 4: __do_get_user_32(__gu_val,__gu_addr,__gu_ret); break; \
-default: __gu_ret = __do_get_user_bad(); break; \
-} } x = (type) __gu_val; __gu_ret; })
-
-#define __do_get_user_nocheck(x,ptr,size,type) ({ \
-int __gu_ret; \
-unsigned long __gu_val; \
-switch (size) { \
-case 1: __do_get_user_8(__gu_val,ptr,__gu_ret); break; \
-case 2: __do_get_user_16(__gu_val,ptr,__gu_ret); break; \
-case 4: __do_get_user_32(__gu_val,ptr,__gu_ret); break; \
-default: __gu_ret = __do_get_user_bad(); __gu_val = 0; break; \
-} x = (type) __gu_val; __gu_ret; })
-
-#define __do_get_user_asm(x,addr,ret,bwl,reg,rtype) \
-__asm__ __volatile__( \
- "movl $1f,%0\n\t" \
- "incl %3\n\t" \
- "mov" #bwl " %2,%" reg "1\n\t" \
- "xorl %0,%0\n\t" \
- "decl %3\n1:" \
-:"=&d" (ret), #rtype (x) \
-:"m" (__m(addr)),"m" (current->tss.ex.count))
-
-#define __do_get_user_8(x,addr,ret) \
-__do_get_user_asm(x,addr,ret,b,"b","=&q")
-#define __do_get_user_16(x,addr,ret) \
-__do_get_user_asm(x,addr,ret,w,"w","=&r")
-#define __do_get_user_32(x,addr,ret) \
-__do_get_user_asm(x,addr,ret,l,"","=&r")
-
-extern int __do_get_user_bad(void);
-
-#define __copy_user(to,from,size) \
-__asm__ __volatile__( \
- "shrl $2,%1\n\t" \
- "movl $3f,%0\n\t" \
- "incl %3\n\t" \
- "rep; movsl\n\t" \
- "testl $2,%2\n\t" \
- "je 1f\n\t" \
- "movsw\n\t" \
- "subl $2,%2\n" \
- "1:\t" \
- "testl $1,%2\n\t" \
- "je 2f\n\t" \
- "movsb\n\t" \
- "decl %2\n" \
- "2:\t" \
- "decl %3\n" \
- "3:\tlea 0(%2,%1,4),%0" \
- :"=&d" (size) \
- :"c" (size), "r" (size & 3), "m" (current->tss.ex), \
- "D" (to), "S" (from) \
- :"cx","di","si","memory");
-
-static inline unsigned long __constant_copy_user(void * to, const void * from, unsigned long size)
-{
- unsigned long result;
-
- switch (size & 3) {
- default:
- __asm__ __volatile__(
- "movl $1f,%0\n\t"
- "incl %1\n\t"
- "rep ; movsl\n\t"
- "decl %1\n"
- "1:\tlea 0(,%%ecx,4),%0"
- :"=&d" (result)
- :"m" (current->tss.ex),
- "S" (from),"D" (to),"c" (size/4)
- :"cx","di","si","memory");
- break;
- case 1:
- __asm__ __volatile__(
- "movl $1f,%0\n\t"
- "incl %3\n\t"
- "rep ; movsl\n\t"
- "movsb\n\t"
- "decl %1\n\t"
- "decl %3\n"
- "1:\tlea 0(%1,%%ecx,4),%0"
- :"=&d" (result)
- :"ab" (1),"m" (current->tss.ex),
- "S" (from),"D" (to), "c" (size/4)
- :"cx","di","si","memory");
- break;
- case 2:
- __asm__ __volatile__(
- "movl $1f,%0\n\t"
- "incl %2\n\t"
- "rep ; movsl\n\t"
- "movsw\n\t"
- "subl $2,%1\n\t"
- "decl %2\n"
- "1:\tlea 0(%1,%%ecx,4),%0"
- :"=&d" (result)
- :"ab" (2),"m" (current->tss.ex),
- "S" (from),"D" (to),"c" (size/4)
- :"cx","di","si","memory");
- break;
- case 3:
- __asm__ __volatile__(
- "movl $1f,%0\n\t"
- "incl %2\n\t"
- "rep ; movsl\n\t"
- "movsw\n\t"
- "subl $2,%1\n\t"
- "movsb\n\t"
- "decl %1\n\t"
- "decl %2\n"
- "1:\tlea 0(%1,%%ecx,4),%0"
- :"=&d" (result)
- :"ab" (3),"m" (current->tss.ex),
- "S" (from),"D" (to),"c" (size/4)
- :"cx","di","si","memory");
- break;
- }
- return result;
-}
+/*
+ * Copy To/From Userspace
+ */
-static inline unsigned long __generic_copy_to_user(void *to, const void *from, unsigned long n)
+/* Generic arbitrary sized copy. */
+#define __copy_user(to,from,size) \
+ __asm__ __volatile__( \
+ "0: rep; movsl\n" \
+ " movl %1,%0\n" \
+ "1: rep; movsb\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: lea 0(%1,%0,4),%0\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ " .long 1b,2b\n" \
+ ".text" \
+ : "=c"(size) \
+ : "r"(size & 3), "0"(size / 4), "D"(to), "S"(from) \
+ : "di", "si", "memory")
+
+/* Optimize just a little bit when we know the size of the move. */
+#define __constant_copy_user(to, from, size) \
+do { \
+ switch (size & 3) { \
+ default: \
+ __asm__ __volatile__( \
+ "0: rep; movsl\n" \
+ "1:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "2: shl $2,%0\n" \
+ " jmp 1b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,2b\n" \
+ ".text" \
+ : "=c"(size) \
+ : "S"(from), "D"(to), "0"(size/4) \
+ : "di", "si", "memory"); \
+ break; \
+ case 1: \
+ __asm__ __volatile__( \
+ "0: rep; movsl\n" \
+ "1: movsb\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: shl $2,%0\n" \
+ "4: incl %0\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ " .long 1b,4b\n" \
+ ".text" \
+ : "=c"(size) \
+ : "S"(from), "D"(to), "0"(size/4) \
+ : "di", "si", "memory"); \
+ break; \
+ case 2: \
+ __asm__ __volatile__( \
+ "0: rep; movsl\n" \
+ "1: movsw\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: shl $2,%0\n" \
+ "4: addl $2,%0\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ " .long 1b,4b\n" \
+ ".text" \
+ : "=c"(size) \
+ : "S"(from), "D"(to), "0"(size/4) \
+ : "di", "si", "memory"); \
+ break; \
+ case 3: \
+ __asm__ __volatile__( \
+ "0: rep; movsl\n" \
+ "1: movsw\n" \
+ "2: movsb\n" \
+ "3:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "4: shl $2,%0\n" \
+ "5: addl $2,%0\n" \
+ "6: incl %0\n" \
+ " jmp 3b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,4b\n" \
+ " .long 1b,5b\n" \
+ " .long 2b,6b\n" \
+ ".text" \
+ : "=c"(size) \
+ : "S"(from), "D"(to), "0"(size/4) \
+ : "di", "si", "memory"); \
+ break; \
+ } \
+} while (0)
+
+static inline unsigned long
+__generic_copy_to_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
__copy_user(to,from,n);
return n;
}
-static inline unsigned long __constant_copy_to_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__constant_copy_to_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
- n = __constant_copy_user(to,from,n);
+ __constant_copy_user(to,from,n);
return n;
}
-static inline unsigned long __generic_copy_from_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__generic_copy_from_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
__copy_user(to,from,n);
return n;
}
-static inline unsigned long __constant_copy_from_user(void *to, const void *from, unsigned long n)
+static inline unsigned long
+__constant_copy_from_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
- n = __constant_copy_user(to,from,n);
+ __constant_copy_user(to,from,n);
return n;
}
-#define copy_to_user(to,from,n) \
-(__builtin_constant_p(n) ? \
- __constant_copy_to_user((to),(from),(n)) : \
- __generic_copy_to_user((to),(from),(n)))
-
-#define copy_from_user(to,from,n) \
-(__builtin_constant_p(n) ? \
- __constant_copy_from_user((to),(from),(n)) : \
- __generic_copy_from_user((to),(from),(n)))
-
-#define __clear_user(addr,size) \
-__asm__ __volatile__( \
- "movl $3f,%0\n\t" \
- "incl %2\n\t" \
- "rep; stosl\n\t" \
- "testl $2,%3\n\t" \
- "je 1f\n\t" \
- "stosw\n\t" \
- "subl $2,%3\n" \
- "1:\t" \
- "testl $1,%3\n\t" \
- "je 2f\n\t" \
- "stosb\n\t" \
- "decl %3\n" \
- "2:\t" \
- "decl %2\n" \
- "3:\tlea 0(%3,%1,4),%0" \
- :"=&d" (size) \
- :"c" (size >> 2), "m" (current->tss.ex), "r" (size & 3), \
- "D" (addr), "a" (0) \
- :"cx","di","memory");
-
-#define clear_user(addr,n) ({ \
-void * __cl_addr = (addr); \
-unsigned long __cl_size = (n); \
-if (__cl_size && __access_ok(VERIFY_WRITE, ((unsigned long)(__cl_addr)), __cl_size)) \
-__clear_user(__cl_addr, __cl_size); \
-__cl_size; })
-
-#define __strncpy_from_user(dst,src,count,res) \
-__asm__ __volatile__( \
- "cld\n\t" \
- "movl $3f,%0\n\t" \
- "incl %2\n" \
- "1:\tdecl %1\n\t" \
- "js 2f\n\t" \
- "lodsb\n\t" \
- "stosb\n\t" \
- "testb %%al,%%al\n\t" \
- "jne 1b\n" \
- "2:\t" \
- "incl %1\n\t" \
- "xorl %0,%0\n\t" \
- "decl %2\n" \
- "3:" \
- :"=&d" (res), "=r" (count) \
- :"m" (current->tss.ex), "1" (count), "S" (src),"D" (dst) \
- :"si","di","ax","memory")
-
-#define strncpy_from_user(dest,src,count) ({ \
-const void * __sc_src = (src); \
-unsigned long __sc_count = (count); \
-long __sc_res = -EFAULT; \
-if (__access_ok(VERIFY_READ, ((unsigned long)(__sc_src)), __sc_count)) { \
- unsigned long __sc_residue = __sc_count; \
- __strncpy_from_user(dest,__sc_src,__sc_count,__sc_res); \
- if (!__sc_res) __sc_res = __sc_residue - __sc_count; \
-} __sc_res; })
+#define copy_to_user(to,from,n) \
+ (__builtin_constant_p(n) ? \
+ __constant_copy_to_user((to),(from),(n)) : \
+ __generic_copy_to_user((to),(from),(n)))
-extern inline int verify_area(int type, const void * addr, unsigned long size)
+#define copy_from_user(to,from,n) \
+ (__builtin_constant_p(n) ? \
+ __constant_copy_from_user((to),(from),(n)) : \
+ __generic_copy_from_user((to),(from),(n)))
+
+
+/*
+ * Zero Userspace
+ */
+
+#define __clear_user(addr,size) \
+ __asm__ __volatile__( \
+ "0: rep; stosl\n" \
+ " movl %1,%0\n" \
+ "1: rep; stosb\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: lea 0(%1,%0,4),%0\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ " .long 1b,2b\n" \
+ ".text" \
+ : "=c"(size) \
+ : "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0) \
+ : "di")
+
+static inline unsigned long
+clear_user(void *to, unsigned long n)
{
- return access_ok(type,addr,size)?0:-EFAULT;
+ if (access_ok(VERIFY_WRITE, to, n))
+ __clear_user(to, n);
+ return n;
}
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+#define __strncpy_from_user(dst,src,count,res) \
+ __asm__ __volatile__( \
+ " testl %1,%1\n" \
+ " jz 2f\n" \
+ "0: lodsb\n" \
+ " stosb\n" \
+ " testb %%al,%%al\n" \
+ " jz 1f\n" \
+ " decl %1\n" \
+ " jnz 0b\n" \
+ "1: subl %1,%0\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: movl %2,%0\n" \
+ " jmp 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 0b,3b\n" \
+ ".text" \
+ : "=r"(res), "=r"(count) \
+ : "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \
+ : "si", "di", "ax", "memory")
+
+static inline long
+strncpy_from_user(char *dst, const char *src, long count)
+{
+ long res = -EFAULT;
+ if (access_ok(VERIFY_READ, src, 1))
+ __strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+
#endif /* __i386_UACCESS_H */
#ifndef _BAYCOM_H
#define _BAYCOM_H
-#include <linux/ioctl.h>
-#undef BAYCOM_DEBUG
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
-struct baycom_statistics {
- unsigned long rx_packets, tx_packets;
- unsigned long ptt_keyed;
- unsigned long rx_bufferoverrun, tx_bufferoverrun;
+struct baycom_debug_data {
+ unsigned long debug1;
+ unsigned long debug2;
+ long debug3;
};
-struct baycom_params {
- int modem_type;
- int iobase;
- int irq;
- int options;
- int tx_delay; /* the transmitter keyup delay in 10ms units */
- int tx_tail; /* the transmitter keyoff delay in 10ms units */
- int slottime; /* the slottime in 10ms; usually 10 = 100ms */
- int ppersist; /* the p-persistence 0..255 */
- int fulldup; /* the driver does not support full duplex, setting */
- /* this just makes the driver send even if DCD is on */
-};
-
-/* -------------------------------------------------------------------- */
-
-#define BAYCOM_MAJOR 51
-
-/* maximum packet length, excluding CRC */
-#define BAYCOM_MAXFLEN 400
-
-/* the ioctl type of this driver */
-#define BAYCOM_IOCTL_TYPE 'B'
-
-#define KISS_FEND ((unsigned char)0300)
-#define KISS_FESC ((unsigned char)0333)
-#define KISS_TFEND ((unsigned char)0334)
-#define KISS_TFESC ((unsigned char)0335)
+struct baycom_modem_type {
+ unsigned char modem_type;
+ unsigned int options;
+};
-#define KISS_CMD_DATA 0
-#define KISS_CMD_TXDELAY 1
-#define KISS_CMD_PPERSIST 2
-#define KISS_CMD_SLOTTIME 3
-#define KISS_CMD_TXTAIL 4
-#define KISS_CMD_FULLDUP 5
+struct baycom_ioctl {
+ int cmd;
+ union {
+ struct baycom_modem_type mt;
+ struct baycom_debug_data dbg;
+ } data;
+};
-/*
- * use bottom halves? (HDLC processing done with interrupts on or off)
- */
-#define BAYCOM_USE_BH
+/* -------------------------------------------------------------------- */
/*
* modem types
*/
-
#define BAYCOM_MODEM_INVALID 0
#define BAYCOM_MODEM_SER12 1
#define BAYCOM_MODEM_PAR96 2
*/
#define BAYCOM_OPTIONS_SOFTDCD 1
-
-/*
- * ioctl constants
- */
-#define BAYCOMCTL_GETDCD _IOR(BAYCOM_IOCTL_TYPE, 0, unsigned char)
-#define BAYCOMCTL_GETPTT _IOR(BAYCOM_IOCTL_TYPE, 1, unsigned char)
-#define BAYCOMCTL_PARAM_TXDELAY _IO(BAYCOM_IOCTL_TYPE, 2)
-#define BAYCOMCTL_PARAM_PPERSIST _IO(BAYCOM_IOCTL_TYPE, 3)
-#define BAYCOMCTL_PARAM_SLOTTIME _IO(BAYCOM_IOCTL_TYPE, 4)
-#define BAYCOMCTL_PARAM_TXTAIL _IO(BAYCOM_IOCTL_TYPE, 5)
-#define BAYCOMCTL_PARAM_FULLDUP _IO(BAYCOM_IOCTL_TYPE, 6)
-
-#define BAYCOMCTL_GETSTAT _IOR(BAYCOM_IOCTL_TYPE, 7, \
- struct baycom_statistics)
-
-#define BAYCOMCTL_GETPARAMS _IOR(BAYCOM_IOCTL_TYPE, 8, \
- struct baycom_params)
-#define BAYCOMCTL_SETPARAMS _IOR(BAYCOM_IOCTL_TYPE, 9, \
- struct baycom_params)
-
-#define BAYCOMCTL_CALIBRATE _IO(BAYCOM_IOCTL_TYPE, 10)
-
-#ifdef BAYCOM_DEBUG
/*
- * these are mainly for debugging purposes
+ * ioctl values change for baycom_net
*/
-#define BAYCOMCTL_GETSAMPLES _IOR(BAYCOM_IOCTL_TYPE, 16, unsigned char)
-#define BAYCOMCTL_GETBITS _IOR(BAYCOM_IOCTL_TYPE, 17, unsigned char)
-
-#define BAYCOMCTL_DEBUG1 _IOR(BAYCOM_IOCTL_TYPE, 18, unsigned long)
-#define BAYCOMCTL_DEBUG2 _IOR(BAYCOM_IOCTL_TYPE, 19, unsigned long)
-#define BAYCOMCTL_DEBUG3 _IOR(BAYCOM_IOCTL_TYPE, 20, unsigned long)
-#endif /* BAYCOM_DEBUG */
+#define BAYCOMCTL_GETMODEMTYPE 0x90
+#define BAYCOMCTL_SETMODEMTYPE 0x91
+#define BAYCOMCTL_GETDEBUG 0x92
/* -------------------------------------------------------------------- */
#endif /* _BAYCOM_H */
/* --------------------------------------------------------------------- */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
--- /dev/null
+/*
+ * hdlcdrv.h -- HDLC packet radio network driver.
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _HDLCDRV_H
+#define _HDLCDRV_H
+
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct hdlcdrv_params {
+ int iobase;
+ int irq;
+ int dma;
+ int seriobase;
+ int pariobase;
+ int midiiobase;
+};
+
+struct hdlcdrv_channel_params {
+ int tx_delay; /* the transmitter keyup delay in 10ms units */
+ int tx_tail; /* the transmitter keyoff delay in 10ms units */
+ int slottime; /* the slottime in 10ms; usually 10 = 100ms */
+ int ppersist; /* the p-persistence 0..255 */
+ int fulldup; /* some driver do not support full duplex, setting */
+ /* this just makes them send even if DCD is on */
+};
+
+struct hdlcdrv_channel_state {
+ int ptt;
+ int dcd;
+ int ptt_keyed;
+ struct enet_statistics stats;
+};
+
+struct hdlcdrv_ioctl {
+ int cmd;
+ union {
+ struct hdlcdrv_params mp;
+ struct hdlcdrv_channel_params cp;
+ struct hdlcdrv_channel_state cs;
+ unsigned int calibrate;
+ unsigned char bits;
+ } data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * ioctl values
+ */
+#define HDLCDRVCTL_GETMODEMPAR 0
+#define HDLCDRVCTL_SETMODEMPAR 1
+#define HDLCDRVCTL_GETCHANNELPAR 2
+#define HDLCDRVCTL_SETCHANNELPAR 3
+#define HDLCDRVCTL_GETSTAT 4
+#define HDLCDRVCTL_CALIBRATE 5
+
+/*
+ * these are mainly for debugging purposes
+ */
+#define HDLCDRVCTL_GETSAMPLES 10
+#define HDLCDRVCTL_GETBITS 11
+
+/* -------------------------------------------------------------------- */
+
+#ifdef __KERNEL__
+
+#define HDLCDRV_MAGIC 0x5ac6e778
+#define HDLCDRV_IFNAMELEN 6
+#define HDLCDRV_HDLCBUFFER 16 /* should be a power of 2 for speed reasons */
+#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */
+#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */
+#undef HDLCDRV_DEBUG
+
+/* maximum packet length, excluding CRC */
+#define HDLCDRV_MAXFLEN 400
+
+
+struct hdlcdrv_hdlcbuffer {
+ unsigned rd, wr;
+ unsigned short buf[HDLCDRV_HDLCBUFFER];
+};
+
+#ifdef HDLCDRV_DEBUG
+struct hdlcdrv_bitbuffer {
+ unsigned int rd;
+ unsigned int wr;
+ unsigned int shreg;
+ unsigned char buffer[HDLCDRV_BITBUFFER];
+};
+
+extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf,
+ unsigned int bit)
+{
+ unsigned char new;
+
+ new = buf->shreg & 1;
+ buf->shreg >>= 1;
+ buf->shreg |= (!!bit) << 7;
+ if (new) {
+ buf->buffer[buf->wr] = buf->shreg;
+ buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+ buf->shreg = 0x80;
+ }
+}
+
+extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf,
+ unsigned int bits)
+{
+ buf->buffer[buf->wr] = bits & 0xff;
+ buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+ buf->buffer[buf->wr] = (bits >> 8) & 0xff;
+ buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+
+}
+#endif /* HDLCDRV_DEBUG */
+
+/* -------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each driver.
+ */
+
+struct hdlcdrv_ops {
+ /*
+ * first some informations needed by the hdlcdrv routines
+ */
+ int bitrate;
+ /*
+ * the routines called by the hdlcdrv routines
+ */
+ int (*open)(struct device *);
+ int (*close)(struct device *);
+ int (*ioctl)(struct device *, struct ifreq *, int);
+};
+
+struct hdlcdrv_state {
+ int magic;
+
+ char ifname[HDLCDRV_IFNAMELEN];
+
+ const struct hdlcdrv_ops *ops;
+
+ struct hdlcdrv_pttoutput {
+ int seriobase;
+ int pariobase;
+ int midiiobase;
+ unsigned int flags;
+ } ptt_out;
+
+ struct hdlcdrv_channel_params ch_params;
+
+ struct hdlcdrv_hdlcrx {
+ struct hdlcdrv_hdlcbuffer hbuf;
+ int in_hdlc_rx;
+ /* 0 = sync hunt, != 0 receiving */
+ int rx_state;
+ unsigned int bitstream;
+ unsigned int bitbuf;
+ int numbits;
+ unsigned char dcd;
+
+ int len;
+ unsigned char *bp;
+ unsigned char buffer[HDLCDRV_MAXFLEN+2];
+ } hdlcrx;
+
+ struct hdlcdrv_hdlctx {
+ struct hdlcdrv_hdlcbuffer hbuf;
+ int in_hdlc_tx;
+ /*
+ * 0 = send flags
+ * 1 = send txtail (flags)
+ * 2 = send packet
+ */
+ int tx_state;
+ int numflags;
+ unsigned int bitstream;
+ unsigned char ptt;
+ int calibrate;
+ int slotcnt;
+
+ unsigned int bitbuf;
+ int numbits;
+
+ int len;
+ unsigned char *bp;
+ unsigned char buffer[HDLCDRV_MAXFLEN+2];
+ } hdlctx;
+
+#ifdef HDLCDRV_DEBUG
+ struct hdlcdrv_bitbuffer bitbuf_channel;
+ struct hdlcdrv_bitbuffer bitbuf_hdlc;
+#endif /* HDLCDRV_DEBUG */
+
+ struct enet_statistics stats;
+ int ptt_keyed;
+
+ struct sk_buff_head send_queue; /* Packets awaiting transmission */
+};
+
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb)
+{
+ return !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr)
+ % HDLCDRV_HDLCBUFFER);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
+{
+ return hb->rd == hb->wr;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
+{
+ unsigned newr;
+ unsigned short val;
+ unsigned long flags;
+
+ if (hb->rd == hb->wr)
+ return 0;
+ save_flags(flags);
+ cli();
+ newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
+ val = hb->buf[hb->rd];
+ hb->rd = newr;
+ restore_flags(flags);
+ return val;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb,
+ unsigned short val)
+{
+ unsigned newp;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER;
+ if (newp != hb->rd) {
+ hb->buf[hb->wr] = val & 0xffff;
+ hb->wr = newp;
+ }
+ restore_flags(flags);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
+{
+ hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
+}
+
+extern unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
+{
+ unsigned int ret;
+
+ if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) {
+ if (s->hdlctx.calibrate > 0)
+ s->hdlctx.calibrate--;
+ else
+ s->hdlctx.ptt = 0;
+ ret = 0;
+ } else
+ ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf);
+#ifdef HDLCDRV_LOOPBACK
+ hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret);
+#endif /* HDLCDRV_LOOPBACK */
+ return ret;
+}
+
+extern void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
+{
+#ifdef HDLCDRV_DEBUG
+ hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
+#endif /* HDLCDRV_DEBUG */
+}
+
+extern void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
+{
+ s->hdlcrx.dcd = !!dcd;
+}
+
+extern int hdlcdrv_ptt(struct hdlcdrv_state *s)
+{
+ return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
+}
+
+/* -------------------------------------------------------------------- */
+
+void hdlcdrv_receiver(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_transmitter(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_arbitrate(struct device *, struct hdlcdrv_state *);
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+ unsigned int privsize, char *ifname,
+ unsigned int baseaddr, unsigned int irq,
+ unsigned int dma);
+int hdlcdrv_unregister_hdlcdrv(struct device *dev);
+
+/* -------------------------------------------------------------------- */
+
+
+
+#endif /* __KERNEL__ */
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _HDLCDRV_H */
+
+/* -------------------------------------------------------------------- */
DIGI_BH,
SERIAL_BH,
RISCOM8_BH,
- BAYCOM_BH,
NET_BH,
IMMEDIATE_BH,
KEYBOARD_BH,
#ifndef _MD_H
#define _MD_H
-#include <asm/segment.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/ioctl.h>
-/* $Id: scc.h,v 1.26 1996/10/09 16:35:56 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.28 1996/10/30 20:01:15 jreuter Exp jreuter $ */
#ifndef _SCC_H
#define _SCC_H
-#include <linux/if_ether.h>
-
/* selection of hardware types */
#define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */
#define DRSI 0x08 /* hardware type for DRSI PC*Packet card */
#define BAYCOM 0x10 /* hardware type for BayCom (U)SCC */
-/* Paranoia check... */
-
-#define SCC_PARANOIA_CHECK /* tell the user if something is going wrong */
-
/* DEV ioctl() commands */
#define SIOCSCCRESERVED (SIOCDEVPRIVATE+0)
#define TXS_WAIT 5 /* Waiting for Mintime to expire */
#define TXS_TIMEOUT 6 /* We had a transmission timeout */
-#define TX_ON 1 /* command for scc_key_trx() */
-#define TX_OFF 0 /* dto */
-
-/* Vector masks in RR2B */
-
-#define VECTOR_MASK 0x06
-#define TXINT 0x00
-#define EXINT 0x02
-#define RXINT 0x04
-#define SPINT 0x06
-
typedef unsigned long io_port; /* type definition for an 'io port address' */
-#ifdef SCC_DELAY
-#define Inb(port) inb_p(port)
-#define Outb(port, val) outb_p(val, port)
-#else
-#define Inb(port) inb(port)
-#define Outb(port, val) outb(val, port)
-#endif
-
-#define TIMER_OFF 65535U
-
-/* SCC channel control structure for KISS */
-
-struct scc_kiss {
- unsigned char txdelay; /* Transmit Delay 10 ms/cnt */
- unsigned char persist; /* Persistence (0-255) as a % */
- unsigned char slottime; /* Delay to wait on persistence hit */
- unsigned char tailtime; /* Delay after last byte written */
- unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
- unsigned char waittime; /* Waittime before any transmit attempt */
- unsigned int maxkeyup; /* Maximum time to transmit (seconds) */
- unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */
- unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */
- unsigned int maxdefer; /* Timer for CSMA channel busy limit */
- unsigned char tx_inhibit; /* Transmit is not allowed when set */
- unsigned char group; /* Group ID for AX.25 TX interlocking */
- unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */
- unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */
-};
-
-
/* SCC statistical information */
struct scc_stat {
};
+#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 {
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 */
};
int scc_init(void);
-#endif
+#endif /* defined(__KERNEL__) */
+#endif /* defined(_SCC_H) */
--- /dev/null
+/*
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _SOUNDMODEM_H
+#define _SOUNDMODEM_H
+
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct sm_debug_data {
+ unsigned long debug1;
+ unsigned long debug2;
+ long debug3;
+};
+
+struct sm_diag_data {
+ unsigned int mode;
+ unsigned int flags;
+ unsigned int samplesperbit;
+ unsigned int datalen;
+ short *data;
+};
+
+struct sm_mixer_data {
+ unsigned int mixer_type;
+ unsigned int sample_rate;
+ unsigned int bit_rate;
+ unsigned int reg;
+ unsigned int data;
+};
+
+struct sm_config {
+ int hardware;
+ int mode;
+};
+
+struct sm_ioctl {
+ int cmd;
+ union {
+ struct sm_config cfg;
+ struct sm_diag_data diag;
+ struct sm_mixer_data mix;
+ struct sm_debug_data dbg;
+ } data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * config: hardware
+ */
+#define SM_HARDWARE_INVALID -1
+#define SM_HARDWARE_SBC 0
+#define SM_HARDWARE_WSS 1
+#define SM_HARDWARE_WSSFDX 2 /* currently does not work! */
+
+/*
+ * config: mode
+ */
+#define SM_MODE_INVALID -1
+#define SM_MODE_AFSK1200 0
+#define SM_MODE_FSK9600 1
+
+/*
+ * diagnose modes
+ */
+#define SM_DIAGMODE_OFF 0
+#define SM_DIAGMODE_INPUT 1
+#define SM_DIAGMODE_DEMOD 2
+
+/*
+ * diagnose flags
+ */
+#define SM_DIAGFLAG_DCDGATE (1<<0)
+#define SM_DIAGFLAG_VALID (1<<1)
+
+/*
+ * mixer types
+ */
+#define SM_MIXER_INVALID 0
+#define SM_MIXER_AD1848 0x10
+#define SM_MIXER_CT1335 0x20
+#define SM_MIXER_CT1345 0x21
+#define SM_MIXER_CT1745 0x22
+
+/*
+ * ioctl values
+ */
+#define SMCTL_GETMODEMTYPE 0x80
+#define SMCTL_SETMODEMTYPE 0x81
+#define SMCTL_DIAGNOSE 0x82
+#define SMCTL_GETMIXER 0x83
+#define SMCTL_SETMIXER 0x84
+#define SMCTL_GETDEBUG 0x85
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _SOUNDMODEM_H */
+
+/* --------------------------------------------------------------------- */
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);
#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)
#endif
#ifdef CONFIG_BAYCOM
{ "baycom=", baycom_setup },
+#endif
+#ifdef CONFIG_SOUNDMODEM
+ { "soundmodem=", sm_setup },
#endif
{ 0, 0 }
};
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);
}
/*
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)
}
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;
}
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 = {
#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