diff options
-rw-r--r-- | pciradio.c | 1 | ||||
-rw-r--r-- | wctdm24xxp.c | 462 | ||||
-rw-r--r-- | zaptel.h | 7 | ||||
-rw-r--r-- | ztcfg.c | 203 |
4 files changed, 612 insertions, 61 deletions
@@ -541,6 +541,7 @@ unsigned char byte1,byte2; if ((rad->encdec.lastcmd + 2) > jiffies) return; if (__pciradio_getcreg(rad,0xc) & 1) return; n = 0; + byte2 = 0; switch(rad->encdec.state) { case 0: diff --git a/wctdm24xxp.c b/wctdm24xxp.c index 1c5c949..80c9e97 100644 --- a/wctdm24xxp.c +++ b/wctdm24xxp.c @@ -2,9 +2,11 @@ * Wilcard TDM2400P TDM FXS/FXO Interface Driver for Zapata Telephony interface * * Written by Mark Spencer <markster@digium.com> + * Copyright (C) 2005,2006, Digium, Inc. + * All rights reserved. * - * Copyright (C) 2005, 2006, Digium, Inc. - * + * Sections for QRV cards written by Jim Dixon <jim@lambdatel.com> + * Copyright (C) 2006, Jim Dixon and QRV Communications * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -22,6 +24,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ +/* For QRV DRI cards, gain is signed short, expressed in hundredths of +db (in reference to 1v Peak @ 1000Hz) , as follows: + +Rx Gain: -11.99 to 15.52 db +Tx Gain - No Pre-Emphasis: -35.99 to 12.00 db +Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db +*/ #include <linux/kernel.h> #include <linux/errno.h> @@ -263,6 +272,7 @@ static struct fxo_mode { #define MOD_TYPE_FXO 2 #define MOD_TYPE_FXSINIT 3 #define MOD_TYPE_VPM 4 +#define MOD_TYPE_QRV 5 #define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ #define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ @@ -279,6 +289,7 @@ static struct fxo_mode { #define USER_COMMANDS 8 #define ISR_COMMANDS 2 +#define QRV_DEBOUNCETIME 20 #define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS) @@ -326,6 +337,19 @@ struct wctdm { unsigned char ctlreg; int cards; int cardflag; /* Bit-map of present cards */ + char qrvhook[NUM_CARDS]; + unsigned short qrvdebtime[NUM_CARDS]; + int radmode[NUM_CARDS]; +#define RADMODE_INVERTCOR 1 +#define RADMODE_IGNORECOR 2 +#define RADMODE_EXTTONE 4 +#define RADMODE_EXTINVERT 8 +#define RADMODE_IGNORECT 16 +#define RADMODE_PREEMP 32 +#define RADMODE_DEEMP 64 + unsigned short debouncetime[NUM_CARDS]; + signed short rxgain[NUM_CARDS]; + signed short txgain[NUM_CARDS]; spinlock_t reglock; wait_queue_head_t regq; /* FXO Stuff */ @@ -454,6 +478,11 @@ static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writech ecval = ecval % EC_SIZE; #endif + /* if a QRV card, map it to its first channel */ + if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) + { + return; + } /* Skip audio */ writechunk += 24; spin_lock_irqsave(&wc->reglock, flags); @@ -477,6 +506,8 @@ static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writech curcmd = CMD_RD(64); else if (wc->modtype[card] == MOD_TYPE_FXO) curcmd = CMD_RD(12); + else if (wc->modtype[card] == MOD_TYPE_QRV) + curcmd = CMD_RD(3); else if (wc->modtype[card] == MOD_TYPE_VPM) { #ifdef FANCY_ECHOCAN if (wc->blinktimer >= 0xf) { @@ -522,6 +553,22 @@ static inline void cmd_dequeue(struct wctdm *wc, volatile unsigned char *writech writechunk[CMD_BYTE(card, 1)] = (curcmd >> 8) & 0xff; writechunk[CMD_BYTE(card, 2)] = curcmd & 0xff; #endif + } else if (wc->modtype[card] == MOD_TYPE_QRV) { + + writechunk[CMD_BYTE(card, 0)] = 0x00; + if (!curcmd) + { + writechunk[CMD_BYTE(card, 1)] = 0x00; + writechunk[CMD_BYTE(card, 2)] = 0x00; + } + else + { + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 1)] = 0x40 | ((curcmd >> 8) & 0x3f); + else + writechunk[CMD_BYTE(card, 1)] = 0xc0 | ((curcmd >> 8) & 0x3f); + writechunk[CMD_BYTE(card, 2)] = curcmd & 0xff; + } } else if (wc->modtype[card] == MOD_TYPE_NONE) { writechunk[CMD_BYTE(card, 0)] = 0x00; writechunk[CMD_BYTE(card, 1)] = 0x00; @@ -545,6 +592,11 @@ static inline void cmd_decifer(struct wctdm *wc, volatile unsigned char *readchu unsigned char ident; int x; + /* if a QRV card, map it to its first channel */ + if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) + { + return; + } /* Skip audio */ readchunk += 24; spin_lock_irqsave(&wc->reglock, flags); @@ -588,6 +640,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(68); /* Hook state */ } else if (wc->modtype[card] == MOD_TYPE_FXO) { wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */ + } else if (wc->modtype[card] == MOD_TYPE_QRV) { + wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */ #ifdef VPM_SUPPORT } else if (wc->modtype[card] == MOD_TYPE_VPM) { wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(0xb9); /* DTMF interrupt */ @@ -603,6 +657,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) #endif } else if (wc->modtype[card] == MOD_TYPE_FXO) { wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ + } else if (wc->modtype[card] == MOD_TYPE_QRV) { + wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */ #ifdef VPM_SUPPORT } else if (wc->modtype[card] == MOD_TYPE_VPM) { wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(0xbd); /* DTMF interrupt */ @@ -686,6 +742,12 @@ static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int va unsigned long flags; int hit=0; int ret; + + /* if a QRV card, use only its first channel */ + if (wc->modtype[card] == MOD_TYPE_QRV) + { + if (card & 3) return(0); + } do { spin_lock_irqsave(&wc->reglock, flags); hit = empty_slot(wc, card); @@ -717,6 +779,12 @@ static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) unsigned long flags; int hit; int ret=0; + + /* if a QRV card, use only its first channel */ + if (wc->modtype[card] == MOD_TYPE_QRV) + { + if (card & 3) return(0); + } do { spin_lock_irqsave(&wc->reglock, flags); hit = empty_slot(wc, card); @@ -1084,6 +1152,60 @@ static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) #endif } +static inline void wctdm_qrvdri_check_hook(struct wctdm *wc, int card) +{ + signed char b,b1; + int qrvcard = card & 0xfc; + + + if (wc->qrvdebtime[card] >= 2) wc->qrvdebtime[card]--; + b = wc->cmdq[qrvcard].isrshadow[0]; /* Hook/Ring state */ + b &= 0xcc; /* use bits 3-4 and 6-7 only */ + + if (wc->radmode[qrvcard] & RADMODE_IGNORECOR) b &= ~4; + else if (!(wc->radmode[qrvcard] & RADMODE_INVERTCOR)) b ^= 4; + if (wc->radmode[qrvcard + 1] | RADMODE_IGNORECOR) b &= ~0x40; + else if (!(wc->radmode[qrvcard + 1] | RADMODE_INVERTCOR)) b ^= 0x40; + + if ((wc->radmode[qrvcard] & RADMODE_IGNORECT) || + (!(wc->radmode[qrvcard] & RADMODE_EXTTONE))) b &= ~8; + else if (!(wc->radmode[qrvcard] & RADMODE_EXTINVERT)) b ^= 8; + if ((wc->radmode[qrvcard + 1] & RADMODE_IGNORECT) || + (!(wc->radmode[qrvcard + 1] & RADMODE_EXTTONE))) b &= ~0x80; + else if (!(wc->radmode[qrvcard + 1] & RADMODE_EXTINVERT)) b ^= 0x80; + /* now b & MASK should be zero, if its active */ + /* check for change in chan 0 */ + if ((!(b & 0xc)) != wc->qrvhook[qrvcard + 2]) + { + wc->qrvdebtime[qrvcard] = wc->debouncetime[qrvcard]; + wc->qrvhook[qrvcard + 2] = !(b & 0xc); + } + /* if timed-out and ready */ + if (wc->qrvdebtime[qrvcard] == 1) + { + b1 = wc->qrvhook[qrvcard + 2]; +if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard,wc->qrvhook[qrvcard + 2]); + zt_hooksig(&wc->chans[qrvcard], + (b1) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); + wc->qrvdebtime[card] = 0; + } + /* check for change in chan 1 */ + if ((!(b & 0xc0)) != wc->qrvhook[qrvcard + 3]) + { + wc->qrvdebtime[qrvcard + 1] = QRV_DEBOUNCETIME; + wc->qrvhook[qrvcard + 3] = !(b & 0xc0); + } + if (wc->qrvdebtime[qrvcard + 1] == 1) + { + b1 = wc->qrvhook[qrvcard + 3]; +if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvhook[qrvcard + 3]); + zt_hooksig(&wc->chans[qrvcard + 1], + (b1) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); + wc->qrvdebtime[card] = 0; + } + return; +} + static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) { unsigned char res; @@ -1311,6 +1433,7 @@ static int wctdm_echocan(struct zt_chan *chan, int eclen) static inline void wctdm_isr_misc(struct wctdm *wc) { int x; + for (x=0;x<wc->cards;x++) { if (wc->cardflag & (1 << x)) { if (wc->modtype[x] == MOD_TYPE_FXS) { @@ -1342,6 +1465,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc) } } else if (wc->modtype[x] == MOD_TYPE_FXO) { wctdm_voicedaa_check_hook(wc, x); + } else if (wc->modtype[x] == MOD_TYPE_QRV) { + wctdm_qrvdri_check_hook(wc, x); } } } @@ -1751,6 +1876,8 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, unsigned char reg16=0, reg26=0, reg30=0, reg31=0; long newjiffies; + if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + wc->modtype[card] = MOD_TYPE_NONE; /* Wait just a bit */ wait_just_a_bit(HZ/10); @@ -1841,6 +1968,8 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int x; int fxsmode=0; + if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + /* By default, don't send on hook */ wc->mods[card].fxs.idletxhookstate = 1; @@ -2035,6 +2164,137 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, return 0; } +static int wctdm_init_qrvdri(struct wctdm *wc, int card) +{ +unsigned char x,y; +unsigned long endjif; + + /* have to set this, at least for now */ + wc->modtype[card] = MOD_TYPE_QRV; + if (!(card & 3)) /* if at base of card, reset and write it */ + { + wctdm_setreg(wc,card,0,0x80); + wctdm_setreg(wc,card,0,0x55); + wctdm_setreg(wc,card,1,0x69); + wc->qrvhook[card] = wc->qrvhook[card + 1] = 0; + wc->qrvhook[card + 2] = wc->qrvhook[card + 3] = 0xff; + wc->debouncetime[card] = wc->debouncetime[card + 1] = QRV_DEBOUNCETIME; + wc->qrvdebtime[card] = wc->qrvdebtime[card + 1] = 0; + wc->radmode[card] = wc->radmode[card + 1] = 0; + wc->txgain[card] = wc->txgain[card + 1] = 3599; + wc->rxgain[card] = wc->rxgain[card + 1] = 1199; + } else { /* channel is on same card as base, no need to test */ + if (wc->modtype[card & 0x7c] == MOD_TYPE_QRV) + { + /* only lower 2 are valid */ + if (!(card & 2)) return 0; + } + wc->modtype[card] = MOD_TYPE_NONE; + return 1; + } + x = wctdm_getreg(wc,card,0); + y = wctdm_getreg(wc,card,1); + /* if not a QRV card, return as such */ + if ((x != 0x55) || (y != 0x69)) + { + wc->modtype[card] = MOD_TYPE_NONE; + return 1; + } + for(x = 0; x < 0x30; x++) + { + if ((x >= 0x1c) && (x <= 0x1e)) wctdm_setreg(wc,card,x,0xff); + else wctdm_setreg(wc,card,x,0); + } + wctdm_setreg(wc,card,0,0x80); + endjif = jiffies + (HZ/10); + while(endjif > jiffies); + wctdm_setreg(wc,card,0,0x10); + wctdm_setreg(wc,card,0,0x10); + endjif = jiffies + (HZ/10); + while(endjif > jiffies); + /* set up modes */ + wctdm_setreg(wc,card,0,0x1c); + /* set up I/O directions */ + wctdm_setreg(wc,card,1,0x33); + wctdm_setreg(wc,card,2,0x0f); + wctdm_setreg(wc,card,5,0x0f); + /* set up I/O to quiescent state */ + wctdm_setreg(wc,card,3,0x11); /* D0-7 */ + wctdm_setreg(wc,card,4,0xa); /* D8-11 */ + wctdm_setreg(wc,card,7,0); /* CS outputs */ + /* set up timeslots */ + wctdm_setreg(wc,card,0x13,card + 0x80); /* codec 2 tx, ts0 */ + wctdm_setreg(wc,card,0x17,card + 0x80); /* codec 0 rx, ts0 */ + wctdm_setreg(wc,card,0x14,card + 0x81); /* codec 1 tx, ts1 */ + wctdm_setreg(wc,card,0x18,card + 0x81); /* codec 1 rx, ts1 */ + /* set up for max gains */ + wctdm_setreg(wc,card,0x26,0x24); + wctdm_setreg(wc,card,0x27,0x24); + wctdm_setreg(wc,card,0x0b,0x01); /* "Transmit" gain codec 0 */ + wctdm_setreg(wc,card,0x0c,0x01); /* "Transmit" gain codec 1 */ + wctdm_setreg(wc,card,0x0f,0xff); /* "Receive" gain codec 0 */ + wctdm_setreg(wc,card,0x10,0xff); /* "Receive" gain codec 1 */ + return 0; +} + +static void qrv_dosetup(struct zt_chan *chan,struct wctdm *wc) +{ +int qrvcard; +unsigned char r; +long l; + + /* actually do something with the values */ + qrvcard = (chan->chanpos - 1) & 0xfc; + if (debug) printk("@@@@@ radmodes: %d,%d rxgains: %d,%d txgains: %d,%d\n", + wc->radmode[qrvcard],wc->radmode[qrvcard + 1], + wc->rxgain[qrvcard],wc->rxgain[qrvcard + 1], + wc->txgain[qrvcard],wc->txgain[qrvcard + 1]); + r = 0; + if (wc->radmode[qrvcard] & RADMODE_DEEMP) r |= 4; + if (wc->radmode[qrvcard + 1] & RADMODE_DEEMP) r |= 8; + if (wc->rxgain[qrvcard] < 1200) r |= 1; + if (wc->rxgain[qrvcard + 1] < 1200) r |= 2; + wctdm_setreg(wc, qrvcard, 7, r); + if (debug) printk("@@@@@ setting reg 7 to %02x hex\n",r); + r = 0; + if (wc->radmode[qrvcard] & RADMODE_PREEMP) r |= 3; + else if (wc->txgain[qrvcard] >= 3600) r |= 1; + else if (wc->txgain[qrvcard] >= 1200) r |= 2; + if (wc->radmode[qrvcard + 1] & RADMODE_PREEMP) r |= 0xc; + else if (wc->txgain[qrvcard + 1] >= 3600) r |= 4; + else if (wc->txgain[qrvcard + 1] >= 1200) r |= 8; + wctdm_setreg(wc, qrvcard, 4, r); + if (debug) printk("@@@@@ setting reg 4 to %02x hex\n",r); + r = 0; + if (wc->rxgain[qrvcard] >= 2400) r |= 1; + if (wc->rxgain[qrvcard + 1] >= 2400) r |= 2; + wctdm_setreg(wc, qrvcard, 0x25, r); + if (debug) printk("@@@@@ setting reg 0x25 to %02x hex\n",r); + r = 0; + if (wc->txgain[qrvcard] < 2400) r |= 1; else r |= 4; + if (wc->txgain[qrvcard + 1] < 2400) r |= 8; else r |= 0x20; + wctdm_setreg(wc, qrvcard, 0x26, r); + if (debug) printk("@@@@@ setting reg 0x26 to %02x hex\n",r); + l = ((long)(wc->rxgain[qrvcard] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + if (wc->rxgain[qrvcard] >= 2400) l += 181; + wctdm_setreg(wc, qrvcard, 0x0b, (unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x0b to %02x hex\n",(unsigned char)l); + l = ((long)(wc->rxgain[qrvcard + 1] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + if (wc->rxgain[qrvcard + 1] >= 2400) l += 181; + wctdm_setreg(wc, qrvcard, 0x0c, (unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x0c to %02x hex\n",(unsigned char)l); + l = ((long)(wc->txgain[qrvcard] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + wctdm_setreg(wc, qrvcard, 0x0f, (unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x0f to %02x hex\n", (unsigned char)l); + l = ((long)(wc->txgain[qrvcard + 1] % 1200) * 10000) / 46875; + if (l == 0) l = 1; + wctdm_setreg(wc, qrvcard, 0x10,(unsigned char)l); + if (debug) printk("@@@@@ setting reg 0x10 to %02x hex\n",(unsigned char)l); + return; +} static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { @@ -2044,6 +2304,10 @@ static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long dat struct wctdm_echo_coefs echoregs; struct wctdm *wc = chan->pvt; int x; + union { + struct zt_radio_stat s; + struct zt_radio_param p; + } stack; #if 0 /* XXX */ @@ -2107,6 +2371,10 @@ static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long dat regs.indirect[x] = wctdm_proslic_getreg_indirect(wc, chan->chanpos -1, x); for (x=0;x<NUM_REGS;x++) regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { + memset(®s, 0, sizeof(regs)); + for (x=0;x<0x32;x++) + regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); } else { memset(®s, 0, sizeof(regs)); for (x=0;x<NUM_FXO_REGS;x++) @@ -2174,12 +2442,150 @@ static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long dat wc->dtmfmutemask &= ~(1 << (chan->chanpos - 1)); return 0; #endif + case ZT_RADIO_GETPARAM: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) + return -ENOTTY; + if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; + stack.p.data = 0; /* start with 0 value in output */ + switch(stack.p.radpar) { + case ZT_RADPAR_INVERTCOR: + if (wc->radmode[chan->chanpos - 1] & RADMODE_INVERTCOR) + stack.p.data = 1; + break; + case ZT_RADPAR_IGNORECOR: + if (wc->radmode[chan->chanpos - 1] & RADMODE_IGNORECOR) + stack.p.data = 1; + break; + case ZT_RADPAR_IGNORECT: + if (wc->radmode[chan->chanpos - 1] & RADMODE_IGNORECT) + stack.p.data = 1; + break; + case ZT_RADPAR_EXTRXTONE: + stack.p.data = 0; + if (wc->radmode[chan->chanpos - 1] & RADMODE_EXTTONE) + { + stack.p.data = 1; + if (wc->radmode[chan->chanpos - 1] & RADMODE_EXTINVERT) + { + stack.p.data = 2; + } + } + break; + case ZT_RADPAR_DEBOUNCETIME: + stack.p.data = wc->debouncetime[chan->chanpos - 1]; + break; + case ZT_RADPAR_RXGAIN: + stack.p.data = wc->rxgain[chan->chanpos - 1] - 1199; + break; + case ZT_RADPAR_TXGAIN: + stack.p.data = wc->txgain[chan->chanpos - 1] - 3599; + break; + case ZT_RADPAR_DEEMP: + stack.p.data = 0; + if (wc->radmode[chan->chanpos - 1] & RADMODE_DEEMP) + { + stack.p.data = 1; + } + break; + case ZT_RADPAR_PREEMP: + stack.p.data = 0; + if (wc->radmode[chan->chanpos - 1] & RADMODE_PREEMP) + { + stack.p.data = 1; + } + break; + default: + return -EINVAL; + } + if (copy_to_user((struct zt_radio_param *)data,&stack.p,sizeof(struct zt_radio_param))) return -EFAULT; + break; + case ZT_RADIO_SETPARAM: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_QRV) + return -ENOTTY; + if (copy_from_user(&stack.p,(struct zt_radio_param *)data,sizeof(struct zt_radio_param))) return -EFAULT; + switch(stack.p.radpar) { + case ZT_RADPAR_INVERTCOR: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_INVERTCOR; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_INVERTCOR; + return 0; + case ZT_RADPAR_IGNORECOR: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_IGNORECOR; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECOR; + return 0; + case ZT_RADPAR_IGNORECT: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_IGNORECT; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_IGNORECT; + return 0; + case ZT_RADPAR_EXTRXTONE: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_EXTTONE; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_EXTTONE; + if (stack.p.data > 1) + wc->radmode[chan->chanpos - 1] |= RADMODE_EXTINVERT; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_EXTINVERT; + return 0; + case ZT_RADPAR_DEBOUNCETIME: + wc->debouncetime[chan->chanpos - 1] = stack.p.data; + return 0; + case ZT_RADPAR_RXGAIN: + /* if out of range */ + if ((stack.p.data <= -1200) || (stack.p.data > 1552)) + { + return -EINVAL; + } + wc->rxgain[chan->chanpos - 1] = stack.p.data + 1199; + break; + case ZT_RADPAR_TXGAIN: + /* if out of range */ + if (wc->radmode[chan->chanpos -1] & RADMODE_PREEMP) + { + if ((stack.p.data <= -2400) || (stack.p.data > 0)) + { + return -EINVAL; + } + } + else + { + if ((stack.p.data <= -3600) || (stack.p.data > 1200)) + { + return -EINVAL; + } + } + wc->txgain[chan->chanpos - 1] = stack.p.data + 3599; + break; + case ZT_RADPAR_DEEMP: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_DEEMP; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_DEEMP; + wc->rxgain[chan->chanpos - 1] = 1199; + break; + case ZT_RADPAR_PREEMP: + if (stack.p.data) + wc->radmode[chan->chanpos - 1] |= RADMODE_PREEMP; + else + wc->radmode[chan->chanpos - 1] &= ~RADMODE_PREEMP; + wc->txgain[chan->chanpos - 1] = 3599; + break; + default: + return -EINVAL; + } + qrv_dosetup(chan,wc); + return 0; default: return -ENOTTY; } return 0; - } + static int wctdm_open(struct zt_chan *chan) { struct wctdm *wc = chan->pvt; @@ -2207,6 +2613,7 @@ static int wctdm_close(struct zt_chan *chan) { struct wctdm *wc = chan->pvt; int x; + signed char reg; wc->usecount--; #ifndef LINUX26 MOD_DEC_USE_COUNT; @@ -2216,6 +2623,23 @@ static int wctdm_close(struct zt_chan *chan) for (x=0;x<wc->cards;x++) { if (wc->modtype[x] == MOD_TYPE_FXS) wc->mods[x].fxs.idletxhookstate = 1; + if (wc->modtype[x] == MOD_TYPE_QRV) + { + int qrvcard = x & 0xfc; + + wc->qrvhook[x] = 0; + wc->qrvhook[x + 2] = 0xff; + wc->debouncetime[x] = QRV_DEBOUNCETIME; + wc->qrvdebtime[x] = 0; + wc->radmode[x] = 0; + wc->txgain[x] = 3599; + wc->rxgain[x] = 1199; + reg = 0; + if (!wc->qrvhook[qrvcard]) reg |= 1; + if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; + wc->sethook[qrvcard] = CMD_WR(3, reg); + qrv_dosetup(chan,wc); + } } /* If we're dead, release us now */ if (!wc->usecount && wc->dead) @@ -2226,8 +2650,26 @@ static int wctdm_close(struct zt_chan *chan) static int wctdm_hooksig(struct zt_chan *chan, zt_txsig_t txsig) { struct wctdm *wc = chan->pvt; - int reg=0; - if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + int reg=0,qrvcard; + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { + qrvcard = (chan->chanpos - 1) & 0xfc; + switch(txsig) { + case ZT_TXSIG_START: + case ZT_TXSIG_OFFHOOK: + wc->qrvhook[chan->chanpos - 1] = 1; + break; + case ZT_TXSIG_ONHOOK: + wc->qrvhook[chan->chanpos - 1] = 0; + break; + default: + printk("wctdm24xxp: Can't set tx state to %d\n", txsig); + } + reg = 0; + if (!wc->qrvhook[qrvcard]) reg |= 1; + if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; + wc->sethook[qrvcard] = CMD_WR(3, reg); + /* wctdm_setreg(wc, qrvcard, 3, reg); */ + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { switch(txsig) { case ZT_TXSIG_START: case ZT_TXSIG_OFFHOOK: @@ -2433,6 +2875,8 @@ static void wctdm_post_initialize(struct wctdm *wc) wc->chans[x].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; else if (wc->modtype[x] == MOD_TYPE_FXS) wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_QRV) + wc->chans[x].sigcap = ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; } } } @@ -2732,7 +3176,7 @@ static void wctdm_locate_modules(struct wctdm *wc) if (debug) { readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); printk("Proslic module %d loop current is %dmA\n",x, - ((readi*3)+20)); + ((readi*3)+20)); } printk("Port %d: Installed -- AUTO FXS/DPO\n", x + 1); } else { @@ -2744,7 +3188,7 @@ static void wctdm_locate_modules(struct wctdm *wc) if (debug) { readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); printk("Proslic module %d loop current is %dmA\n",x, - ((readi*3)+20)); + ((readi*3)+20)); } printk("Port %d: Installed -- MANUAL FXS\n",x + 1); } else { @@ -2753,6 +3197,9 @@ static void wctdm_locate_modules(struct wctdm *wc) } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { wc->cardflag |= (1 << x); printk("Port %d: Installed -- AUTO FXO (%s mode)\n",x + 1, fxo_modes[_opermode].name); + } else if (!wctdm_init_qrvdri(wc,x)) { + wc->cardflag |= 1 << x; + printk("Port %d: Installed -- QRV DRI card\n",x + 1); } else { printk("Port %d: Not installed\n", x + 1); wc->modtype[x] = MOD_TYPE_NONE; @@ -2955,6 +3402,7 @@ static int __init wctdm_init(void) { int res; int x; + for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { if (!strcmp(fxo_modes[x].name, opermode)) break; @@ -1601,5 +1601,12 @@ struct zt_radio_param { #define ZT_RADPAR_REMCOMMAND 17 /* Remote conrtol write data block & do cmd */ +#define ZT_RADPAR_DEEMP 18 /* Audio De-empahsis (on or off) */ + +#define ZT_RADPAR_PREEMP 19 /* Audio Pre-empahsis (on or off) */ + +#define ZT_RADPAR_RXGAIN 20 /* Audio (In to system) Rx Gain */ + +#define ZT_RADPAR_TXGAIN 21 /* Audio (Out from system) Tx Gain */ #endif /* _LINUX_ZAPTEL_H */ @@ -56,6 +56,7 @@ static char *filename=CONFIG_FILENAME; int rxtones[NUM_TONES + 1],rxtags[NUM_TONES + 1],txtones[NUM_TONES + 1]; int bursttime = 0, debouncetime = 0, invertcor = 0, exttone = 0, corthresh = 0; +int txgain = 0, rxgain = 0, deemp = 0, preemp = 0; int corthreshes[] = {3125,6250,9375,12500,15625,18750,21875,25000,0} ; @@ -177,6 +178,10 @@ static void clear_fields() debouncetime = 0; invertcor = 0; exttone = 0; + txgain = 0; + rxgain = 0; + deemp = 0; + preemp = 0; } static int error(char *fmt, ...) @@ -783,6 +788,86 @@ int burst_time(char *keyword, char *args) return 0; } +int tx_gain(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'txgain' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + txgain = val; + return 0; +} + +int rx_gain(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'rxgain' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + rxgain = val; + return 0; +} + +int de_emp(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'de-emp' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + deemp = val; + return 0; +} + +int pre_emp(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'pre_emp' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + preemp = val; + return 0; +} + int invert_cor(char *keyword, char *args) { static char *realargs[10]; @@ -934,55 +1019,57 @@ static int rad_chanconfig(char *keyword, char *args) if (chans[x]) { p.radpar = ZT_RADPAR_NUMTONES; if (ind_ioctl(x,fd,ZT_RADIO_GETPARAM,&p) == -1) - error("Cannot get number of tones for channel %d\n",x); - n = p.data; - p.radpar = ZT_RADPAR_INITTONE; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot init tones for channel %d\n",x); - if (!rxtones[0]) for(i = 1; i <= n; i++) + n = 0; else n = p.data; + if (n) { - if (rxtones[i]) - { - p.radpar = ZT_RADPAR_RXTONE; - p.index = i; - p.data = rxtones[i]; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set rxtone on channel %d\n",x); - } - if (rxtags[i]) + p.radpar = ZT_RADPAR_INITTONE; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot init tones for channel %d\n",x); + if (!rxtones[0]) for(i = 1; i <= n; i++) { - p.radpar = ZT_RADPAR_RXTONECLASS; - p.index = i; - p.data = rxtags[i]; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set rxtag on channel %d\n",x); + if (rxtones[i]) + { + p.radpar = ZT_RADPAR_RXTONE; + p.index = i; + p.data = rxtones[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set rxtone on channel %d\n",x); + } + if (rxtags[i]) + { + p.radpar = ZT_RADPAR_RXTONECLASS; + p.index = i; + p.data = rxtags[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set rxtag on channel %d\n",x); + } + if (txtones[i]) + { + p.radpar = ZT_RADPAR_TXTONE; + p.index = i; + p.data = txtones[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set txtone on channel %d\n",x); + } + } else { /* if we may have DCS receive */ + if (rxtones[0]) + { + p.radpar = ZT_RADPAR_RXTONE; + p.index = 0; + p.data = rxtones[0]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set DCS rxtone on channel %d\n",x); + } } - if (txtones[i]) + if (txtones[0]) { p.radpar = ZT_RADPAR_TXTONE; - p.index = i; - p.data = txtones[i]; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set txtone on channel %d\n",x); - } - } else { /* if we may have DCS receive */ - if (rxtones[0]) - { - p.radpar = ZT_RADPAR_RXTONE; p.index = 0; - p.data = rxtones[0]; + p.data = txtones[0]; if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set DCS rxtone on channel %d\n",x); + error("Cannot set default txtone on channel %d\n",x); } } - if (txtones[0]) - { - p.radpar = ZT_RADPAR_TXTONE; - p.index = 0; - p.data = txtones[0]; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set default txtone on channel %d\n",x); - } if (debouncetime) { p.radpar = ZT_RADPAR_DEBOUNCETIME; @@ -997,20 +1084,24 @@ static int rad_chanconfig(char *keyword, char *args) if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) error("Cannot set bursttime on channel %d\n",x); } - if (invertcor) - { - p.radpar = ZT_RADPAR_INVERTCOR; - p.data = invertcor; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set invertcor on channel %d\n",x); - } - if (exttone) - { - p.radpar = ZT_RADPAR_EXTRXTONE; - p.data = exttone; - if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) - error("Cannot set exttone on channel %d\n",x); - } + p.radpar = ZT_RADPAR_DEEMP; + p.data = deemp; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_PREEMP; + p.data = preemp; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_TXGAIN; + p.data = txgain; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_RXGAIN; + p.data = rxgain; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_INVERTCOR; + p.data = invertcor; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_EXTRXTONE; + p.data = exttone; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); if (corthresh) { p.radpar = ZT_RADPAR_CORTHRESH; @@ -1120,6 +1211,10 @@ static struct handler { { "exttone", ext_tone }, { "invertcor", invert_cor }, { "corthresh", cor_thresh }, + { "rxgain", rx_gain }, + { "txgain", tx_gain }, + { "deemp", de_emp }, + { "preemp", pre_emp }, { "channel", rad_chanconfig }, { "channels", rad_chanconfig }, }; |