summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pciradio.c1
-rw-r--r--wctdm24xxp.c462
-rw-r--r--zaptel.h7
-rw-r--r--ztcfg.c203
4 files changed, 612 insertions, 61 deletions
diff --git a/pciradio.c b/pciradio.c
index 20b41cc..8d058d7 100644
--- a/pciradio.c
+++ b/pciradio.c
@@ -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(&regs, 0, sizeof(regs));
+ for (x=0;x<0x32;x++)
+ regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x);
} else {
memset(&regs, 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;
diff --git a/zaptel.h b/zaptel.h
index 00c2e0a..c666c80 100644
--- a/zaptel.h
+++ b/zaptel.h
@@ -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 */
diff --git a/ztcfg.c b/ztcfg.c
index 660f28e..230ec8d 100644
--- a/ztcfg.c
+++ b/ztcfg.c
@@ -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 },
};