summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2005-05-02 20:15:07 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2005-05-02 20:15:07 +0000
commitfa78643fc4a67112067ee2b090660c84886e9a04 (patch)
tree67d56dd508097931ed41b9a4092f3980a6f790ae
parent86952759dcdb07b5cada67f976d4d8eb14b55ebf (diff)
Add support for TE410P/TE405P 2nd gen features (TSI, ECHOCAN, NATIVELAYOUT, IRQ, DTMFDETECT)
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@634 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rwxr-xr-xwct4xxp.c817
-rwxr-xr-xzaptel.c105
-rwxr-xr-xzaptel.h18
3 files changed, 870 insertions, 70 deletions
diff --git a/wct4xxp.c b/wct4xxp.c
index 0f19ccc..6dc7046 100755
--- a/wct4xxp.c
+++ b/wct4xxp.c
@@ -1,12 +1,12 @@
/*
* TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02
*
- * Written by Mark Spencer <markster@linux-support.net>
+ * Written by Mark Spencer <markster@digium.com>
* Based on previous works, designs, and archetectures conceived and
* written by Jim Dixon <jim@lambdatel.com>.
*
* Copyright (C) 2001 Jim Dixon / Zapata Telephony.
- * Copyright (C) 2001, Linux Support Services, Inc.
+ * Copyright (C) 2001-2005, Digium, Inc.
*
* All rights reserved.
*
@@ -57,6 +57,16 @@
alarm status */
#define FANCY_ALARM
+/* Define to support Digium Voice Processing Module expansion card */
+#define VPM_SUPPORT
+
+#define DEBUG_MAIN (1 << 0)
+#define DEBUG_DTMF (1 << 1)
+#define DEBUG_REGS (1 << 2)
+#define DEBUG_TSI (1 << 3)
+#define DEBUG_ECHOCAN (1 << 4)
+#define DEBUG_RBS (1 << 5)
+
static int debug;
static int timingcable;
static int highestorder;
@@ -64,7 +74,12 @@ static int t1e1override = -1;
static int j1mode = 0;
static int loopback = 0;
static int alarmdebounce = 0;
+#ifdef VPM_SUPPORT
+static int vpmsupport = 1;
+#endif
static int noburst = 0;
+static int debugslips = 1;
+static int polling = 0;
#ifdef FANCY_ALARM
static int altab[] = {
@@ -83,14 +98,25 @@ static int altab[] = {
#define TYPE_E1 2 /* is an E1 card */
#define TYPE_J1 3 /* is a running J1 */
+#define FLAG_2NDGEN (1 << 3)
+
#define CANARY 0xc0de
+struct devtype {
+ char *desc;
+ unsigned int flags;
+};
+
+static struct devtype wct4xxp = { "Wildcard TE410P/TE405P (1st Gen)", 0 };
+static struct devtype wct4xxp2 = { "Wildcard TE410P/TE405P (2nd Gen)", FLAG_2NDGEN };
+
+
static int inirq = 0;
struct t4 {
/* This structure exists one per card */
struct pci_dev *dev; /* Pointer to PCI device */
- int intcount;
+ unsigned int intcount;
int num; /* Which card we are */
int t1e1; /* T1/E1 select pins */
int globalconfig; /* Whether global setup has been done */
@@ -132,6 +158,14 @@ struct t4 {
volatile unsigned int *writechunk; /* Double-word aligned write memory */
volatile unsigned int *readchunk; /* Double-word aligned read memory */
unsigned short canary;
+#ifdef VPM_SUPPORT
+ int vpm; /* VPM Present */
+ unsigned int dtmfactive[4];
+ unsigned int dtmfmask[4];
+ unsigned int dtmfmutemask[4];
+ short dtmfenergy[4][31];
+ short dtmfdigit[4][31];
+#endif
#ifdef ENABLE_TASKLETS
int taskletrun;
int taskletsched;
@@ -147,7 +181,12 @@ struct t4 {
int checktiming; /* Set >0 to cause the timing source to be checked */
};
+#define T4_VPM_PRESENT (1 << 28)
+
+#ifdef VPM_SUPPORT
+static void t4_vpm_init(struct t4 *wc);
+#endif
static void __set_clear(struct t4 *wc, int span);
static int t4_startup(struct zt_span *span);
static int t4_shutdown(struct zt_span *span);
@@ -155,7 +194,11 @@ static int t4_rbsbits(struct zt_chan *chan, int bits);
static int t4_maint(struct zt_span *span, int cmd);
static int t4_reset_dma(struct t4 *wc);
static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data);
+static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan);
+static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan);
static void __t4_set_timing_source(struct t4 *wc, int unit);
+static void __t4_check_alarms(struct t4 *wc, int span);
+static void __t4_check_sigbits(struct t4 *wc, int span);
#define WC_RDADDR 0
#define WC_WRADDR 1
@@ -186,6 +229,10 @@ static void t4_tasklet(unsigned long data);
static struct t4 *cards[MAX_T4_CARDS];
+
+#define MAX_TDM_CHAN 32
+#define MAX_DTMF_DET 16
+
static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
{
unsigned int tmp;
@@ -262,7 +309,7 @@ static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned
static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
{
unit &= 0x3;
- if (debug)
+ if (debug & DEBUG_REGS)
printk("Writing %02x to address %02x of unit %d\n", value, addr, unit);
__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
__t4_pci_out(wc, WC_LDATA, value);
@@ -271,7 +318,7 @@ static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int a
__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10));
__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
__t4_pci_out(wc, WC_LADDR, 0);
- if (debug) printk("Write complete\n");
+ if (debug & DEBUG_REGS) printk("Write complete\n");
#if 0
{ unsigned int tmp;
tmp = t4_framer_in(wc, unit, addr);
@@ -289,6 +336,133 @@ static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int add
spin_unlock_irqrestore(&wc->reglock, flags);
}
+#ifdef VPM_SUPPORT
+
+static inline void wait_a_little(void)
+{
+ unsigned long newjiffies=jiffies+2;
+ while(jiffies < newjiffies);
+}
+
+static inline unsigned int __t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr)
+{
+ unsigned int ret;
+ unit &= 0x7;
+ __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12));
+ __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12) | (1 << 11) | WC_LREAD);
+ ret = __t4_pci_in(wc, WC_LDATA);
+ __t4_pci_out(wc, WC_LADDR, 0);
+ return ret & 0xff;
+}
+
+static inline unsigned int t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr)
+{
+ unsigned long flags;
+ unsigned int ret;
+ spin_lock_irqsave(&wc->reglock, flags);
+ ret = __t4_vpm_in(wc, unit, addr);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ return ret;
+}
+
+static inline void __t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
+{
+ unit &= 0x7;
+ if (debug & DEBUG_REGS)
+ printk("Writing %02x to address %02x of ec unit %d\n", value, addr, unit);
+ __t4_pci_out(wc, WC_LADDR, (addr & 0xff));
+ __t4_pci_out(wc, WC_LDATA, value);
+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11));
+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11) | WC_LWRITE);
+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11));
+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff));
+ __t4_pci_out(wc, WC_LADDR, 0);
+ if (debug & DEBUG_REGS) printk("Write complete\n");
+
+
+#if 0
+ { unsigned int tmp;
+ tmp = t4_vpm_in(wc, unit, addr);
+ if (tmp != value) {
+ printk("Expected %d from unit %d echo register %d but got %d instead\n", value, unit, addr, tmp);
+ } }
+#endif
+}
+
+static inline void t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wc->reglock, flags);
+ __t4_vpm_out(wc, unit, addr, value);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static void __t4_check_vpm(struct t4 *wc, unsigned int newio)
+{
+ unsigned int digit, regval = 0;
+ int x, i;
+ short energy;
+ static unsigned int lastio = 0;
+
+ if (debug && (newio != lastio))
+ printk("Last was %08x, new is %08x\n", lastio, newio);
+
+ lastio = newio;
+
+ for(x = 0; x < 8; x++) {
+ if (newio & (1 << (7 - x)))
+ continue;
+ /* Start of DTMF detection process */
+ regval = __t4_vpm_in(wc, x, 0xb8);
+ __t4_vpm_out(wc, x, 0xb8, regval); /* Write 1 to clear */
+ regval = regval << 8;
+ regval |= __t4_vpm_in(wc, x, 0xb9);
+ __t4_vpm_out(wc, x, 0xb9, regval & 0xff);
+
+ for(i = 0; (i < MAX_DTMF_DET) && regval; i++) {
+ if(regval & 0x0001) {
+ int channel = (i << 1) + (x >> 2);
+ int base = channel - 1;
+ if (!wc->t1e1)
+ base -= 4;
+ digit = __t4_vpm_in(wc, x, 0xa8 + i);
+ if (digit < 10)
+ digit += '0';
+ else if (digit < 0xe)
+ digit += 'A' - 0xe;
+ else if (digit == 0xe)
+ digit = '*';
+ else if (digit == 0xf)
+ digit = '#';
+ energy = __t4_vpm_in(wc, x, 0x58 + channel);
+ energy = ZT_XLAW(energy, wc->spans[x%4].chans);
+ wc->dtmfactive[x % 4] |= (1 << base);
+ wc->dtmfenergy[x % 4][base] = energy;
+ wc->dtmfdigit[x % 4][base] = digit;
+ if (wc->dtmfmask[x % 4] & (1 << base))
+ zt_qevent_lock(&wc->spans[x%4].chans[base], (ZT_EVENT_DTMFDOWN | digit));
+ if (wc->dtmfmutemask[x % 4] & (1 << base)) {
+ /* Mute active receive buffer*/
+ unsigned long flags;
+ struct zt_chan *chan = &wc->spans[x % 4].chans[base];
+ int y;
+ spin_lock_irqsave(&chan->lock, flags);
+ for (y=0;y<chan->numbufs;y++) {
+ if (chan->readidx[y])
+ memset(chan->readbuf[chan->inreadbuf], ZT_XLAW(0, chan), chan->readidx[y]);
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ }
+ if (debug)
+ printk("Digit Seen: %d, Span: %d, channel: %d, energy: %02x, 'channel %d' chip %d\n", digit, x % 4, base + 1, energy, channel, x);
+
+ }
+ regval = regval >> 1;
+ }
+ }
+}
+#endif
+
static void __set_clear(struct t4 *wc, int span)
{
@@ -318,19 +492,90 @@ static void set_clear(struct t4 *wc, int span)
}
#endif
+static int t4_dacs(struct zt_chan *dst, struct zt_chan *src)
+{
+ struct t4 *wc;
+ wc = dst->pvt;
+ if (src && (src->pvt != dst->pvt)) {
+ if (wc->spanflags[0] & FLAG_2NDGEN)
+ t4_tsi_unassign(wc, dst->span->offset, dst->chanpos);
+ wc = src->pvt;
+ if (wc->spanflags[0] & FLAG_2NDGEN)
+ t4_tsi_unassign(wc, src->span->offset, src->chanpos);
+ if (debug)
+ printk("Unassigning %d/%d by default and...\n", src->span->offset, src->chanpos);
+ if (debug)
+ printk("Unassigning %d/%d by default\n", dst->span->offset, dst->chanpos);
+ return -1;
+ }
+ if (src) {
+ t4_tsi_assign(wc, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos);
+ if (debug)
+ printk("Assigning channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos);
+ } else {
+ t4_tsi_unassign(wc, dst->span->offset, dst->chanpos);
+ if (debug)
+ printk("Unassigning channel %d/%d!\n", dst->span->offset, dst->chanpos);
+ }
+ return 0;
+}
+
+static int t4_echocan(struct zt_chan *chan, int eclen)
+{
+ struct t4 *wc = chan->pvt;
+ int channel;
+ int unit;
+ if (!wc->vpm)
+ return -ENODEV;
+ unit = chan->span->offset;
+ if (wc->t1e1)
+ channel = chan->chanpos;
+ else
+ channel = chan->chanpos + 4;
+ if ((channel & 1))
+ unit += 4;
+ if(debug & DEBUG_ECHOCAN)
+ printk("echocan: Channel is %d, Span is %d, unit is %d, unit offset is %d length %d\n",
+ chan->chanpos, chan->span->offset, unit, channel, eclen);
+ if (eclen)
+ t4_vpm_out(wc,unit,channel,0x3e);
+ else
+ t4_vpm_out(wc,unit,channel,0x01);
+ return 0;
+}
+
static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
{
struct t4_regs regs;
- int x;
+ int j,x;
+ struct t4 * wc;
switch(cmd) {
case WCT4_GET_REGS:
+ wc = chan->pvt;
for (x=0;x<NUM_PCI;x++)
- regs.pci[x] = t4_pci_in(chan->pvt, x);
+ regs.pci[x] = t4_pci_in(wc, x);
for (x=0;x<NUM_REGS;x++)
- regs.regs[x] = t4_framer_in(chan->pvt, chan->span->offset, x);
+ regs.regs[x] = t4_framer_in(wc, chan->span->offset, x);
if (copy_to_user((struct t4_regs *)data, &regs, sizeof(regs)))
return -EFAULT;
break;
+#ifdef VPM_SUPPORT
+ case ZT_TONEDETECT:
+ if (get_user(j, (int *)data))
+ return -EFAULT;
+ wc = chan->pvt;
+ if (!wc->vpm)
+ return -ENOSYS;
+ if (j & ZT_TONEDETECT_ON)
+ wc->dtmfmask[chan->span->offset] |= (1 << (chan->chanpos - 1));
+ else
+ wc->dtmfmask[chan->span->offset] &= ~(1 << (chan->chanpos - 1));
+ if (j & ZT_TONEDETECT_MUTE)
+ wc->dtmfmutemask[chan->span->offset] |= (1 << (chan->chanpos - 1));
+ else
+ wc->dtmfmutemask[chan->span->offset] &= ~(1 << (chan->chanpos - 1));
+ return 0;
+#endif
default:
return -ENOTTY;
}
@@ -400,7 +645,7 @@ static int t4_rbsbits(struct zt_chan *chan, int bits)
struct t4 *wc = chan->pvt;
unsigned long flags;
- if(debug) printk("Setting bits to %d on channel %s\n", bits, chan->name);
+ if(debug & DEBUG_RBS) printk("Setting bits to %d on channel %s\n", bits, chan->name);
spin_lock_irqsave(&wc->reglock, flags);
k = chan->span->offset;
if (wc->spantype[k] == TYPE_E1) { /* do it E1 way */
@@ -441,7 +686,7 @@ static int t4_rbsbits(struct zt_chan *chan, int bits)
__t4_framer_out(wc,k,0x70 + b,c);
}
spin_unlock_irqrestore(&wc->reglock, flags);
- if (debug)
+ if (debug & DEBUG_RBS)
printk("Finished setting RBS bits\n");
return 0;
}
@@ -479,7 +724,7 @@ static int t4_shutdown(struct zt_span *span)
__t4_set_timing_source(wc,4);
} else wc->checktiming = 1;
spin_unlock_irqrestore(&wc->reglock, flags);
- if (debug)
+ if (debug & DEBUG_MAIN)
printk("Span %d (%s) shutdown\n", span->spanno, span->name);
return 0;
}
@@ -489,7 +734,7 @@ static int t4_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
int i;
struct t4 *wc = span->pvt;
- if (debug)
+ if (debug & DEBUG_MAIN)
printk("TE410P: Configuring span %d\n", span->spanno);
/* XXX We assume lineconfig is okay and shouldn't XXX */
span->lineconfig = lc->lineconfig;
@@ -527,7 +772,7 @@ static int t4_chanconfig(struct zt_chan *chan, int sigtype)
struct t4 *wc = chan->pvt;
alreadyrunning = wc->spans[chan->span->offset].flags & ZT_FLAG_RUNNING;
- if (debug) {
+ if (debug & DEBUG_MAIN) {
if (alreadyrunning)
printk("TE410P: Reconfigured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype);
else
@@ -566,6 +811,12 @@ static int t4_close(struct zt_chan *chan)
static void init_spans(struct t4 *wc)
{
int x,y,c;
+ int gen2;
+ int offset = 1;
+
+ gen2 = (wc->spanflags[0] & FLAG_2NDGEN);
+ if (!wc->t1e1)
+ offset += 4;
for (x=0;x<4;x++) {
sprintf(wc->spans[x].name, "TE4/%d/%d",
wc->num, x + 1);
@@ -589,6 +840,10 @@ static void init_spans(struct t4 *wc)
wc->spans[x].flags = ZT_FLAG_RBS;
wc->spans[x].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF;
wc->spans[x].ioctl = t4_ioctl;
+ if (gen2) {
+ wc->spans[x].echocan = t4_echocan;
+ wc->spans[x].dacs = t4_dacs;
+ }
wc->spans[x].pvt = wc;
wc->spans[x].offset = x;
init_waitqueue_head(&wc->spans[x].maintq);
@@ -600,6 +855,10 @@ static void init_spans(struct t4 *wc)
c = (x * wc->spans[x].channels) + y;
mychans->pvt = wc;
mychans->chanpos = y + 1;
+ if (gen2) {
+ mychans->writechunk = (void *)(wc->writechunk + (x * 32 + y + offset) * 2);
+ mychans->readchunk = (void *)(wc->readchunk + (x * 32 + y + offset) * 2);
+ }
}
}
}
@@ -624,11 +883,11 @@ static void t4_serial_setup(struct t4 *wc, int unit)
}
/* Configure interrupts */
- t4_framer_out(wc, unit, 0x46, 0x40); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */
+ t4_framer_out(wc, unit, 0x46, 0x00); /* GCR: Interrupt on Activation/Deactivation of each */
/* Configure system interface */
t4_framer_out(wc, unit, 0x3e, 0xc2); /* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */
- t4_framer_out(wc, unit, 0x3f, unit << 1); /* SIC2: No FFS, no center receive eliastic buffer, phase */
+ t4_framer_out(wc, unit, 0x3f, 0x20 | (unit << 1)); /* SIC2: No FFS, no center receive eliastic buffer, phase */
t4_framer_out(wc, unit, 0x40, 0x04); /* SIC3: Edges for capture */
t4_framer_out(wc, unit, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */
if (!wc->t1e1) { /* T1 mode */
@@ -639,7 +898,7 @@ static void t4_serial_setup(struct t4 *wc, int unit)
else
t4_framer_out(wc, unit, 0x24, 0x03); /* RC0: Just shy of 1023 */
t4_framer_out(wc, unit, 0x25, 0x84); /* RC1: The rest of RC0 */
- } else { /* E1 mode */
+ } else { /* E1 mode */
t4_framer_out(wc, unit, 0x22, 0x00); /* XC0: Normal operation of Sa-bits */
t4_framer_out(wc, unit, 0x23, 0x04); /* XC1: 0 offset */
t4_framer_out(wc, unit, 0x24, 0x04); /* RC0: Just shy of 1023 */
@@ -652,7 +911,7 @@ static void t4_serial_setup(struct t4 *wc, int unit)
t4_framer_out(wc, unit, 0x82, 0x65); /* PC3: Some unused stuff */
t4_framer_out(wc, unit, 0x83, 0x35); /* PC4: Some more unused stuff */
t4_framer_out(wc, unit, 0x84, 0x01); /* PC5: XMFS active low, SCLKR is input, RCLK is output */
- if (debug)
+ if (debug & DEBUG_MAIN)
printk("Successfully initialized serial bus for unit %d\n", unit);
}
@@ -666,10 +925,12 @@ static void __t4_set_timing_source(struct t4 *wc, int unit)
timing |= (unit << 6);
for (x=0;x<4;x++) /* set all 4 receive reference clocks to unit */
__t4_framer_out(wc, x, 0x44, timing);
- __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl | (1 << 29));
+ wc->dmactrl |= (1 << 29);
+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
} else {
for (x=0;x<4;x++) /* set each receive reference clock to itself */
__t4_framer_out(wc, x, 0x44, timing | (x << 6));
+ wc->dmactrl &= ~(1 << 29);
__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
}
wc->syncsrc = unit;
@@ -680,7 +941,7 @@ static void __t4_set_timing_source(struct t4 *wc, int unit)
for (x=0;x<4;x++)
wc->spans[x].syncsrc = unit;
} else {
- if (debug)
+ if (debug & DEBUG_MAIN)
printk("TE410P: Timing source already set to %d\n", unit);
}
#if 0
@@ -752,7 +1013,7 @@ static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlev
__t4_framer_out(wc, unit, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */
__t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */
__t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */
-
+
/* Generate pulse mask for T1 */
switch(mytxlevel) {
case 3:
@@ -777,6 +1038,23 @@ static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlev
__t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */
break;
}
+
+ __t4_framer_out(wc, unit, 0x14, 0xf7); /* IMR0: We care about CAS changes, etc */
+ __t4_framer_out(wc, unit, 0x15, 0xff); /* IMR1: We care about nothing */
+ __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */
+ if (debugslips) {
+ __t4_framer_out(wc, unit, 0x17, 0xf4); /* IMR3: We care about AIS and friends */
+ __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */
+ } else {
+ __t4_framer_out(wc, unit, 0x17, 0xf7); /* IMR3: We care about AIS and friends */
+ __t4_framer_out(wc, unit, 0x18, 0xff); /* IMR4: We don't care about slips on transmit */
+ }
+
+ if (!polling) {
+ __t4_check_alarms(wc, unit);
+ __t4_check_sigbits(wc, unit);
+ }
+
printk("TE410P: Span %d configured for %s/%s\n", unit + 1, framing, line);
}
@@ -846,6 +1124,21 @@ static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig)
__t4_framer_out(wc, unit, 0x26, 0x54); /* XPM0 */
__t4_framer_out(wc, unit, 0x27, 0x02); /* XPM1 */
__t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */
+
+ __t4_framer_out(wc, unit, 0x14, 0xf7); /* IMR0: We care about CRC errors, CAS changes, etc */
+ __t4_framer_out(wc, unit, 0x15, 0x3f); /* IMR1: We care about loopup / loopdown */
+ __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */
+ if (debugslips) {
+ __t4_framer_out(wc, unit, 0x17, 0xc4); /* IMR3: We care about AIS and friends */
+ __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */
+ } else {
+ __t4_framer_out(wc, unit, 0x17, 0xc7); /* IMR3: We care about AIS and friends */
+ __t4_framer_out(wc, unit, 0x18, 0xff); /* IMR4: We don't care about slips on transmit */
+ }
+ if (!polling) {
+ __t4_check_alarms(wc, unit);
+ __t4_check_sigbits(wc, unit);
+ }
printk("TE410P: Span %d configured for %s/%s%s\n", unit + 1, framing, line, crc4);
}
@@ -891,10 +1184,14 @@ static int t4_startup(struct zt_span *span)
wc->spansstarted++;
/* enable interrupts */
/* Start DMA, enabling DMA interrupts on read only */
- wc->dmactrl = 0xc0000003 | (1 << 29);
+ wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm;
if (noburst)
wc->dmactrl |= (1 << 26);
__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ if (!polling) {
+ __t4_check_alarms(wc, span->offset);
+ __t4_check_sigbits(wc, span->offset);
+ }
}
spin_unlock_irqrestore(&wc->reglock, flags);
@@ -902,6 +1199,12 @@ static int t4_startup(struct zt_span *span)
if (wc->syncs[1] == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno);
if (wc->syncs[2] == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno);
if (wc->syncs[3] == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno);
+#ifdef VPM_SUPPORT
+ if (!alreadyrunning && !wc->vpm) {
+ wait_a_little();
+ t4_vpm_init(wc);
+ }
+#endif
return 0;
}
@@ -919,7 +1222,7 @@ static inline void e1_check(struct t4 *wc, int span, int val)
/* Wait 1000 ms */
wc->e1recover = 1000 * 8;
memset(wc->e1check, 0, sizeof(wc->e1check));
- if (debug)
+ if (debug & DEBUG_MAIN)
printk("Detected loss of E1 alignment on span %d!\n", span);
t4_reset_dma(wc);
}
@@ -950,7 +1253,7 @@ static void t4_receiveprep(struct t4 *wc, int irq)
if (dbl) {
for (x=0;x<4;x++)
wc->spans[x].irqmisses++;
- if (debug)
+ if (debug & DEBUG_MAIN)
printk("TE410P: Double/missed interrupt detected\n");
}
for (x=0;x<ZT_CHUNKSIZE;x++) {
@@ -1004,6 +1307,42 @@ static void t4_receiveprep(struct t4 *wc, int irq)
}
}
+
+#if (ZT_CHUNKSIZE != 8)
+#error Sorry, nextgen doesn't support chunksize != 8
+#endif
+
+static void t4_receiveprep_gen2(struct t4 *wc)
+{
+ int x;
+ int y;
+ unsigned int merged;
+
+ for (x=0;x<4;x++) {
+ if (wc->spans[x].flags & ZT_FLAG_RUNNING) {
+ if ((merged = wc->dtmfactive[x] & wc->dtmfmutemask[x])) {
+ for (y=0;y<wc->spans[x].channels;y++) {
+ /* Mute any DTMFs which are supposed to be muted */
+ if (merged & (1 << y))
+ memset(wc->spans[x].chans[y].readchunk, ZT_XLAW(0, (wc->spans[x].chans + y)), ZT_CHUNKSIZE);
+ }
+ }
+ zt_ec_span(&wc->spans[x]);
+ zt_receive(&wc->spans[x]);
+ }
+ }
+}
+
+static void t4_transmitprep_gen2(struct t4 *wc)
+{
+ int x;
+ for (x=0;x<4;x++) {
+ if (wc->spans[x].flags & ZT_FLAG_RUNNING)
+ zt_transmit(&wc->spans[x]);
+ }
+}
+
+
static void t4_transmitprep(struct t4 *wc, int irq)
{
volatile unsigned int *writechunk;
@@ -1016,7 +1355,7 @@ static void t4_transmitprep(struct t4 *wc, int irq)
/* First part */
writechunk = wc->writechunk + 1;
} else {
- writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1;
+ writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1;
}
for (y=0;y<4;y++) {
if (wc->spans[y].flags & ZT_FLAG_RUNNING)
@@ -1057,6 +1396,7 @@ static void t4_transmitprep(struct t4 *wc, int irq)
static void __t4_check_sigbits(struct t4 *wc, int span)
{
int a,i,rxs;
+
if (!(wc->spans[span].flags & ZT_FLAG_RUNNING))
return;
if (wc->spantype[span] == TYPE_E1) {
@@ -1105,26 +1445,17 @@ static void __t4_check_sigbits(struct t4 *wc, int span)
/* Get high channel in low bits */
rxs = (a & 0xf);
if (!(wc->spans[span].chans[i+1].sig & ZT_SIG_CLEAR)) {
- if (wc->spans[span].chans[i+1].rxsig != rxs)
+ /* XXX Not really reset on every trans! XXX */
+ if (wc->spans[span].chans[i+1].rxsig != rxs) {
zt_rbsbits(&wc->spans[span].chans[i+1], rxs);
+ }
}
rxs = (a >> 4) & 0xf;
if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) {
- if (wc->spans[span].chans[i].rxsig != rxs)
+ /* XXX Not really reset on every trans! XXX */
+ if (wc->spans[span].chans[i].rxsig != rxs) {
zt_rbsbits(&wc->spans[span].chans[i], rxs);
- }
- }
- }
-}
-
-static void __t4_do_counters(struct t4 *wc)
-{
- int span;
- for (span=0;span<4;span++) {
- if (wc->alarmtimer[span]) {
- if (!--wc->alarmtimer[span]) {
- wc->spans[span].alarms &= ~(ZT_ALARM_RECOVER);
- zt_alarm_notify(&wc->spans[span]);
+ }
}
}
}
@@ -1251,6 +1582,21 @@ static void __t4_check_alarms(struct t4 *wc, int span)
zt_alarm_notify(&wc->spans[span]);
}
+static void __t4_do_counters(struct t4 *wc)
+{
+ int span;
+ for (span=0;span<4;span++) {
+ if (wc->alarmtimer[span]) {
+ if (!--wc->alarmtimer[span]) {
+ wc->spans[span].alarms &= ~(ZT_ALARM_RECOVER);
+ if (!polling)
+ __t4_check_alarms(wc, span);
+ zt_alarm_notify(&wc->spans[span]);
+ }
+ }
+ }
+}
+
static inline void __handle_leds(struct t4 *wc)
{
int x;
@@ -1405,6 +1751,199 @@ static void t4_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
}
+static inline void __t4_framer_interrupt(struct t4 *wc, int span)
+{
+ /* Check interrupts for a given span */
+ unsigned char gis, isr0=0, isr1=0, isr2=0, isr3=0, isr4;
+ gis = __t4_framer_in(wc, span, 0x6e);
+
+ if (wc->spantype[span] == TYPE_E1) {
+ /* E1 checks */
+ if (gis & 0x1)
+ isr0 = __t4_framer_in(wc, span, 0x68);
+ if (gis & 0x2)
+ isr1 = __t4_framer_in(wc, span, 0x69);
+ if (gis & 0x4)
+ isr2 = __t4_framer_in(wc, span, 0x6a);
+ if (gis & 0x8)
+ isr3 = __t4_framer_in(wc, span, 0x6b);
+
+
+ if (isr0)
+ __t4_check_sigbits(wc, span);
+ if ((isr3 & 0x38) || isr2 || isr1)
+ __t4_check_alarms(wc, span);
+ } else {
+ /* T1 checks */
+ if (gis & 0x1)
+ isr0 = __t4_framer_in(wc, span, 0x68);
+ if (gis & 0x4)
+ isr2 = __t4_framer_in(wc, span, 0x6a);
+ if (gis & 0x8)
+ isr3 = __t4_framer_in(wc, span, 0x6b);
+
+ if (isr0)
+ __t4_check_sigbits(wc, span);
+ if (isr2 || (isr3 & 0x08))
+ __t4_check_alarms(wc, span);
+ }
+ if (debugslips && !wc->spans[span].alarms) {
+ if (isr3 & 0x02)
+ printk("TE410P: RECEIVE slip NEGATIVE on span %d\n", span + 1);
+ if (isr3 & 0x01)
+ printk("TE410P: RECEIVE slip POSITIVE on span %d\n", span + 1);
+ if (gis & 0x10)
+ isr4 = __t4_framer_in(wc, span, 0x6c);
+ else
+ isr4 = 0;
+ if (isr4 & 0x80)
+ printk("TE410P: TRANSMIT slip POSITIVE on span %d\n", span + 1);
+ if (isr4 & 0x40)
+ printk("TE410P: TRANSMIT slip NEGATIVE on span %d\n", span + 1);
+ }
+}
+
+#ifdef LINUX26
+static irqreturn_t t4_interrupt_gen2(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void t4_interrupt_gen2(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+ struct t4 *wc = dev_id;
+ unsigned long flags;
+ unsigned char cis;
+ int x;
+
+ unsigned int status;
+#if 0
+ unsigned int status2;
+#endif
+
+#if 1
+ if (wc->intcount < 20)
+ printk("2G: Pre-interrupt\n");
+#endif
+
+ inirq = 1;
+ /* Make sure it's really for us */
+ status = t4_pci_in(wc, WC_INTR);
+
+ /* Ignore if it's not for us */
+ if (!(status & 0x2))
+#ifdef LINUX26
+ return IRQ_NONE;
+#else
+ return;
+#endif
+
+ if (!wc->spansstarted) {
+ printk("Not prepped yet!\n");
+#ifdef LINUX26
+ return IRQ_NONE;
+#else
+ return;
+#endif
+ }
+
+ wc->intcount++;
+#if 1
+ if (wc->intcount < 20)
+ printk("2G: Got interrupt, status = %08x, GIS = %04x\n", status, __t4_framer_in(wc, 0, 0x6f));
+#endif
+
+ if (status & 0x2) {
+ t4_receiveprep_gen2(wc);
+ t4_transmitprep_gen2(wc);
+ }
+
+ spin_lock_irqsave(&wc->reglock, flags);
+
+ __t4_do_counters(wc);
+
+ if (polling) {
+ x = wc->intcount & 15 /* 63 */;
+ switch(x) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ __t4_check_sigbits(wc, x);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ __t4_check_alarms(wc, x - 4);
+ break;
+ }
+ } else if (status & 0x1) {
+ cis = __t4_framer_in(wc, 0, 0x6f);
+ if (cis & 0x1)
+ __t4_framer_interrupt(wc, 0);
+ if (cis & 0x2)
+ __t4_framer_interrupt(wc, 1);
+ if (cis & 0x4)
+ __t4_framer_interrupt(wc, 2);
+ if (cis & 0x8)
+ __t4_framer_interrupt(wc, 3);
+ }
+#ifdef VPM_SUPPORT
+ if (wc->vpm) {
+ if (!(wc->intcount % 16)) {
+ /* Check DTMF events */
+ if (wc->vpm) {
+ int span = (wc->intcount >> 4) & 0x3;
+ int y;
+ short energy;
+ int offset = 1;
+ int chip;
+ int channel;
+ if (!wc->t1e1)
+ offset = 5;
+ if (wc->dtmfactive[span]) {
+ for (y = 0; y < wc->spans[span].channels; y++) {
+ if (wc->dtmfactive[span] & (1 << y)) {
+ channel = y + offset;
+ chip = span + ((channel & 0x1) << 2);
+ /* Have an active channel, check its energy! */
+ energy = __t4_vpm_in(wc, chip, 0x58 + channel);
+ energy = ZT_XLAW(energy, wc->spans[span].chans);
+ if (energy < (wc->dtmfenergy[span][y])) {
+ if (debug & DEBUG_DTMF)
+ printk("Finished digit on span %d, channel %d (energy = %02x < %02x) 'channel' %d, chip %d!\n", span, y + 1, energy, wc->dtmfenergy[span][y], channel, chip);
+ if (debug & DEBUG_DTMF)
+ printk("Finished digit '%c' on channel %d of span %d\n", wc->dtmfdigit[span][y], y + 1, span);
+ if (wc->dtmfmask[span] & (1 << y))
+ zt_qevent_lock(&wc->spans[span].chans[y], (ZT_EVENT_DTMFUP | wc->dtmfdigit[span][y]));
+ wc->dtmfenergy[span][y] = 0;
+ wc->dtmfdigit[span][y] = 0;
+ wc->dtmfactive[span] &= ~(1 << y);
+ } else if (energy > (wc->dtmfenergy[span][y])) {
+ if (debug & DEBUG_DTMF)
+ printk("Increasing digit energy on span %d, channel %d (energy = %02x > %02x)!\n", span, y + 1, energy, wc->dtmfenergy[span][y]);
+ wc->dtmfenergy[span][y] = energy;
+ }
+ }
+ }
+ }
+ }
+ } else if ((status & 0xff00) != 0xff00)
+ __t4_check_vpm(wc, (status & 0xff00) >> 8);
+ }
+#endif
+
+ __handle_leds(wc);
+
+ if (wc->checktiming > 0)
+ __t4_set_timing_source_auto(wc);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ t4_pci_out(wc, WC_INTR, 0);
+#ifdef LINUX26
+ return IRQ_RETVAL(1);
+#endif
+}
+
static int t4_reset_dma(struct t4 *wc)
{
/* Turn off DMA and such */
@@ -1419,13 +1958,172 @@ static int t4_reset_dma(struct t4 *wc)
t4_pci_out(wc, WC_WRADDR, wc->writedma);
t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2));
t4_pci_out(wc, WC_INTR, 0);
- wc->dmactrl = 0xc0000003 | (1 << 29);
+ wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm;
if (noburst)
wc->dmactrl |= (1 << 26);
t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
return 0;
}
+#ifdef VPM_SUPPORT
+static void t4_vpm_init(struct t4 *wc)
+{
+ unsigned char reg;
+ unsigned int mask;
+ unsigned int ver;
+ int i,x,y;
+ if (!vpmsupport) {
+ printk("VPM: Support Disabled\n");
+ return;
+ }
+
+ for (x=0;x<8;x++) {
+ ver = t4_vpm_in(wc, x, 0x1a0); /* revision */
+ if (ver != 0x26) {
+ if (x)
+ printk("VPM: Inopperable\n");
+ else
+ printk("VPM: Not Present\n");
+ return;
+ }
+
+ /* Setup GPIO's */
+ for (y=0;y<4;y++) {
+ t4_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
+ t4_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
+ t4_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
+ }
+
+ /* Setup TDM path - sets fsync and tdm_clk as inputs */
+ reg = t4_vpm_in(wc, x, 0x1a3); /* misc_con */
+ t4_vpm_out(wc, x, 0x1a3, reg & ~2);
+
+ /* Setup timeslots */
+ t4_vpm_out(wc, x, 0x02f, 0x20 | ((x%4) << 3));
+ if (x < 4)
+ mask = 0x55555555;
+ else
+ mask = 0xaaaaaaaa;
+
+ /* Setup Echo length (128 taps) */
+ t4_vpm_out(wc, x, 0x022, 0x00);
+ t4_vpm_out(wc, x, 0x023, 0x7f);
+
+ /* Setup the tdm channel masks for all LV's*/
+ for (i=0;i<4;i++)
+ t4_vpm_out(wc, x, 0x30+i, (mask >> (i << 3)) & 0xff);
+
+ /* Setup convergence rate */
+ reg = t4_vpm_in(wc,x,0x20);
+ reg &= 0xE0;
+ if (wc->spantype[x & 0x3] == TYPE_E1) {
+ if (x < 4)
+ printk("VPM: Span %d A-law mode\n", x & 0x3);
+ reg |= 0x01;
+ } else {
+ if (x < 4)
+ printk("VPM: Span %d U-law mode\n", x & 0x3);
+ reg &= ~0x01;
+ }
+ t4_vpm_out(wc,x,0x20,(reg | 0x20));
+
+ /* Initialize echo cans */
+ for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
+ if (mask & (0x00000001 << i))
+ t4_vpm_out(wc,x,i,0x00);
+ }
+
+ wait_a_little();
+
+ /* Put in bypass mode */
+ for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
+ if (mask & (0x00000001 << i)) {
+ t4_vpm_out(wc,x,i,0x01);
+ }
+ }
+
+ /* Enable bypass */
+ for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
+ if (mask & (0x00000001 << i))
+ t4_vpm_out(wc,x,0x78 + i,0x01);
+ }
+
+ /* Enable DTMF detectors */
+ for (i=0;i<MAX_DTMF_DET;i++) {
+ if(x < 4)
+ t4_vpm_out(wc, x, 0x98+i,(i*2)|0x40);
+ else
+ t4_vpm_out(wc, x, 0x98+i, ((i*2)+1)|0x40);
+ }
+ for (i=0xb8;i<0xbe;i++)
+ t4_vpm_out(wc, x, i, 0xff);
+ if(x < 4) {
+ for(i=0;i<4;i++)
+ t4_vpm_out(wc, x, 0xc0+i, 0x55);
+ } else {
+ for(i = 0; i < 4; i++)
+ t4_vpm_out(wc, x, 0xc0+i, 0xaa);
+ }
+ }
+ printk("VPM: Present and operational\n");
+ wc->vpm = T4_VPM_PRESENT;
+}
+
+#endif
+
+static void t4_tsi_reset(struct t4 *wc)
+{
+ int x;
+ for (x=0;x<128;x++) {
+ wc->dmactrl &= ~0x00007fff;
+ wc->dmactrl |= (0x00004000 | (x << 7));
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ }
+ wc->dmactrl &= ~0x00007fff;
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+}
+
+/* Note that channels here start from 1 */
+static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan)
+{
+ unsigned long flags;
+ int fromts, tots;
+
+ fromts = (fromspan << 5) |(fromchan);
+ tots = (tospan << 5) | (tochan);
+
+ if (!wc->t1e1) {
+ fromts += 4;
+ tots += 4;
+ }
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->dmactrl &= ~0x00007fff;
+ wc->dmactrl |= (0x00004000 | (tots << 7) | (fromts));
+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ wc->dmactrl &= ~0x00007fff;
+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan)
+{
+ unsigned long flags;
+ int tots;
+
+ tots = (tospan << 5) | (tochan);
+
+ if (!wc->t1e1)
+ tots += 4;
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->dmactrl &= ~0x00007fff;
+ wc->dmactrl |= (0x00004000 | (tots << 7));
+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ if (debug & DEBUG_TSI)
+ printk("Sending '%08x\n", wc->dmactrl);
+ wc->dmactrl &= ~0x00007fff;
+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
static int t4_hardware_init(struct t4 *wc)
{
int x;
@@ -1433,7 +2131,7 @@ static int t4_hardware_init(struct t4 *wc)
unsigned int falcver;
version = t4_pci_in(wc, WC_VERSION);
- printk("TE410P version %08x, burst %s\n", version, noburst ? "OFF" : "ON");
+ printk("TE410P version %08x, burst %s, slip debug: %s\n", version, noburst ? "OFF" : "ON", debugslips ? "ON" : "OFF");
/* Make sure DMA engine is not running and interrupts are acknowledged */
wc->dmactrl = 0x0;
@@ -1445,8 +2143,12 @@ static int t4_hardware_init(struct t4 *wc)
t4_pci_out(wc, WC_RDADDR, wc->readdma);
t4_pci_out(wc, WC_WRADDR, wc->writedma);
- /* Setup counters, interrupt flags */
- t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2));
+ /* Setup counters, interrupt flags (ignored in Gen2) */
+ if (wc->spanflags[0] & FLAG_2NDGEN) {
+ t4_tsi_reset(wc);
+ } else {
+ t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2));
+ }
/* Reset pending interrupts */
t4_pci_out(wc, WC_INTR, 0x00000000);
@@ -1512,6 +2214,8 @@ static int __devinit t4_launch(struct t4 *wc)
#ifdef ENABLE_TASKLETS
tasklet_init(&wc->t4_tlet, t4_tasklet, (unsigned long)wc);
#endif
+ t4_tsi_assign(wc, 0, 1, 0, 2);
+ t4_tsi_assign(wc, 0, 2, 0, 1);
return 0;
}
@@ -1519,6 +2223,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
{
int res;
struct t4 *wc;
+ struct devtype *dt;
int x,f;
#if 0
int y;
@@ -1533,7 +2238,10 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
if (wc) {
memset(wc, 0x0, sizeof(struct t4));
spin_lock_init(&wc->reglock);
- wc->variety = (char *)(ent->driver_data);
+ dt = (struct devtype *)(ent->driver_data);
+ for (x=0;x<4;x++)
+ wc->spanflags[x] |= dt->flags;
+ wc->variety = dt->desc;
wc->memaddr = pci_resource_start(pdev, 0);
wc->memlen = pci_resource_len(pdev, 0);
@@ -1566,7 +2274,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
wc->readdma = wc->writedma + ZT_CHUNKSIZE * 32 * 2 * 4;
/* Initialize Write/Buffers to all blank data */
- memset((void *)wc->writechunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4);
+ memset((void *)wc->writechunk,0x00,ZT_MAX_CHUNKSIZE * 2 * 32 * 4);
memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4);
#if 0
memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4);
@@ -1594,7 +2302,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
cards[x] = wc;
- if (request_irq(pdev->irq, t4_interrupt, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) {
+ if (request_irq(pdev->irq, (wc->spanflags[0] & FLAG_2NDGEN) ? t4_interrupt_gen2 :t4_interrupt, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) {
printk("t4xxp: Unable to request IRQ %d\n", pdev->irq);
kfree(wc);
return -EIO;
@@ -1657,7 +2365,11 @@ static int t4_hardware_stop(struct t4 *wc)
schedule_timeout((25 * HZ) / 1000);
/* Turn off counter, address, etc */
- t4_pci_out(wc, WC_COUNT, 0x000000);
+ if (wc->spanflags[0] & FLAG_2NDGEN) {
+ t4_tsi_reset(wc);
+ } else {
+ t4_pci_out(wc, WC_COUNT, 0x000000);
+ }
t4_pci_out(wc, WC_RDADDR, 0x0000000);
t4_pci_out(wc, WC_WRADDR, 0x0000000);
t4_pci_out(wc, WC_GPIO, 0x0000000);
@@ -1718,9 +2430,11 @@ static void __devexit t4_remove_one(struct pci_dev *pdev)
}
}
+
static struct pci_device_id t4_pci_tbl[] __devinitdata =
{
- { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)"Wildcard TE410P-Xilinx" },
+ { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp },
+ { 0x10ee, 0x0271, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp2 },
{ 0, }
};
@@ -1765,11 +2479,18 @@ module_param(timingcable, int, 0600);
module_param(t1e1override, int, 0600);
module_param(alarmdebounce, int, 0600);
module_param(j1mode, int, 0600);
+module_param(debugslips, int, 0600);
+module_param(polling, int, 0600);
#else
MODULE_PARM(debug, "i");
MODULE_PARM(loopback, "i");
MODULE_PARM(noburst, "i");
+MODULE_PARM(debugslips, "i");
+MODULE_PARM(polling, "i");
MODULE_PARM(timingcable, "i");
+#ifdef VPM_SUPPORT
+MODULE_PARM(vpmsupport,"i");
+#endif
MODULE_PARM(t1e1override, "i");
MODULE_PARM(alarmdebounce, "i");
MODULE_PARM(j1mode, "i");
diff --git a/zaptel.c b/zaptel.c
index fae7891..8385f29 100755
--- a/zaptel.c
+++ b/zaptel.c
@@ -140,6 +140,7 @@ EXPORT_SYMBOL(zt_hooksig);
EXPORT_SYMBOL(zt_alarm_notify);
EXPORT_SYMBOL(zt_set_dynamic_ioctl);
EXPORT_SYMBOL(zt_ec_chunk);
+EXPORT_SYMBOL(zt_ec_span);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_entries[ZT_MAX_SPANS];
@@ -958,6 +959,11 @@ static void close_channel(struct zt_chan *chan)
memset(chan->conflast1, 0, sizeof(chan->conflast1));
memset(chan->conflast2, 0, sizeof(chan->conflast2));
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
+ if (chan->span && chan->span->dacs && oldconf)
+ chan->span->dacs(chan, NULL);
+
spin_unlock_irqrestore(&chan->lock, flags);
if (rxgain)
@@ -1563,8 +1569,11 @@ static void zt_chan_unreg(struct zt_chan *chan)
(chans[x]->confmode == ZT_CONF_DIGITALMON))) {
/* Take them out of conference with us */
/* release conference resource if any */
- if (chans[x]->confna)
+ if (chans[x]->confna) {
zt_check_conf(chans[x]->confna);
+ if (chans[x]->span && chans[x]->span->dacs)
+ chans[x]->span->dacs(chans[x], NULL);
+ }
chans[x]->confna = 0;
chans[x]->_confn = 0;
chans[x]->confmode = 0;
@@ -2041,6 +2050,8 @@ static int initialize_channel(struct zt_chan *chan)
if ((chan->sig & __ZT_SIG_DACS) != __ZT_SIG_DACS) {
chan->confna = 0;
chan->confmode = 0;
+ if (chan->span && chan->span->dacs)
+ chan->span->dacs(chan, NULL);
}
chan->_confn = 0;
memset(chan->conflast, 0, sizeof(chan->conflast));
@@ -2078,6 +2089,8 @@ static int initialize_channel(struct zt_chan *chan)
chan->ringcadence[0] = chan->starttime;
chan->ringcadence[1] = ZT_RINGOFFTIME;
}
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
spin_unlock_irqrestore(&chan->lock, flags);
if (rxgain)
@@ -3138,7 +3151,14 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd
/* Setup conference properly */
chans[ch.chan]->confmode = ZT_CONF_DIGITALMON;
chans[ch.chan]->confna = ch.idlebits;
- }
+ if (chans[ch.chan]->span &&
+ chans[ch.chan]->span->dacs &&
+ chans[ch.idlebits] &&
+ chans[ch.chan]->span &&
+ (chans[ch.chan]->span->dacs == chans[ch.idlebits]->span->dacs))
+ chans[ch.chan]->span->dacs(chans[ch.chan], chans[ch.idlebits]);
+ } else if (chans[ch.chan]->span && chans[ch.chan]->span->dacs)
+ chans[ch.chan]->span->dacs(chans[ch.chan], NULL);
chans[ch.chan]->master = newmaster;
/* Note new slave if we are not our own master */
if (newmaster != chans[ch.chan]) {
@@ -3658,6 +3678,14 @@ static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsign
chans[i]->_confn = 0; /* Clear confn */
zt_check_conf(j);
zt_check_conf(stack.conf.confno);
+ if (chans[i]->span && chans[i]->span->dacs) {
+ if ((stack.conf.confmode == ZT_CONF_DIGITALMON) && chans[stack.conf.confno]->span &&
+ (chans[stack.conf.confno]->span->dacs == chans[i]->span->dacs)) {
+ chans[i]->span->dacs(chans[i], chans[stack.conf.confno]);
+ } else {
+ chans[i]->span->dacs(chans[i], NULL);
+ }
+ }
/* k will be non-zero if in a real conf */
k = stack.conf.confmode & (ZT_CONF_CONF | ZT_CONF_CONFANN | ZT_CONF_CONFMON | ZT_CONF_CONFANNMON | ZT_CONF_REALANDPSEUDO);
/* if we are going onto a conf */
@@ -3920,6 +3948,8 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm
/* initialize conference variables */
chan->_confn = 0;
chan->confna = 0;
+ if (chan->span && chan->span->dacs)
+ chan->span->dacs(chan, NULL);
chan->confmode = 0;
chan->confmute = 0;
memset(chan->conflast, 0, sizeof(chan->conflast));
@@ -3937,6 +3967,9 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm
chan->rxgain = defgain;
chan->txgain = defgain;
chan->gainalloc = 0;
+ /* Disable any native echo cancellation as well */
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
spin_unlock_irqrestore(&chan->lock, flags);
if (rxgain)
@@ -3988,6 +4021,8 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm
chan->gainalloc = 0;
chan->flags &= ~ZT_FLAG_AUDIO;
chan->flags |= (ZT_FLAG_PPP | ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
if (tec)
echo_can_free(tec);
} else
@@ -4034,27 +4069,38 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm
return -EINVAL;
get_user(j, (int *)data);
if (j) {
- if ((j == 32) ||
- (j == 64) ||
- (j == 128) ||
- (j == 256)) {
- /* Okay */
- } else {
- j = deftaps;
- }
- ec = echo_can_create(j, 0);
- if (!ec)
- return -ENOMEM;
spin_lock_irqsave(&chan->lock, flags);
/* If we had an old echo can, zap it now */
tec = chan->ec;
- chan->echocancel = j;
- chan->ec = ec;
- chan->echostate = ECHO_STATE_IDLE;
- chan->echolastupdate = 0;
- chan->echotimer = 0;
- echo_can_disable_detector_init(&chan->txecdis);
- echo_can_disable_detector_init(&chan->rxecdis);
+ chan->ec = NULL;
+ /* Attempt hardware native echo can */
+ if (chan->span && chan->span->echocan)
+ ret = chan->span->echocan(chan, j);
+ else
+ ret = -ENOTTY;
+ if (ret) {
+ /* Use built-in echo can */
+ if ((j == 32) ||
+ (j == 64) ||
+ (j == 128) ||
+ (j == 256)) {
+ /* Okay */
+ } else {
+ j = deftaps;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ ec = echo_can_create(j, 0);
+ if (!ec)
+ return -ENOMEM;
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->echocancel = j;
+ chan->ec = ec;
+ chan->echostate = ECHO_STATE_IDLE;
+ chan->echolastupdate = 0;
+ chan->echotimer = 0;
+ echo_can_disable_detector_init(&chan->txecdis);
+ echo_can_disable_detector_init(&chan->rxecdis);
+ }
spin_unlock_irqrestore(&chan->lock, flags);
if (tec)
echo_can_free(tec);
@@ -4066,6 +4112,9 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm
chan->echostate = ECHO_STATE_IDLE;
chan->echolastupdate = 0;
chan->echotimer = 0;
+ /* Attempt hardware native echo can */
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
spin_unlock_irqrestore(&chan->lock, flags);
if (tec)
echo_can_free(tec);
@@ -5228,7 +5277,7 @@ void zt_rbsbits(struct zt_chan *chan, int cursig)
spin_unlock_irqrestore(&chan->lock, flags);
}
-void zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk)
+static inline void __zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk)
{
short rxlin, txlin;
int x;
@@ -5279,6 +5328,20 @@ void zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char
spin_unlock_irqrestore(&ss->lock, flags);
}
+void zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk)
+{
+ __zt_ec_chunk(ss, rxchunk, txchunk);
+}
+
+void zt_ec_span(struct zt_span *span)
+{
+ int x;
+ for (x = 0; x < span->channels; x++) {
+ if (span->chans[x].ec)
+ __zt_ec_chunk(&span->chans[x], span->chans[x].readchunk, span->chans[x].writechunk);
+ }
+}
+
/* return 0 if nothing detected, 1 if lack of tone, 2 if presence of tone */
/* modifies buffer pointed to by 'amp' with notched-out values */
static inline int sf_detect (sf_detect_state_t *s,
diff --git a/zaptel.h b/zaptel.h
index 6d790ad..175279f 100755
--- a/zaptel.h
+++ b/zaptel.h
@@ -618,6 +618,11 @@ void *data;
*/
#define ZT_DYNAMIC_DESTROY _IOW (ZT_CODE, 81, struct zt_dynamic_span)
+/*
+ * Enable tone detection -- implemented by low level driver
+ */
+#define ZT_TONEDETECT _IOW (ZT_CODE, 91, int)
+
/*
* Startup or Shutdown a span
*/
@@ -644,6 +649,9 @@ void *data;
#define ZT_MAX_CADENCE 16
+#define ZT_TONEDETECT_ON (1 << 0) /* Detect tones */
+#define ZT_TONEDETECT_MUTE (1 << 1) /* Mute audio in received channel */
+
struct zt_ring_cadence {
int ringcadence [ZT_MAX_CADENCE];
};
@@ -792,7 +800,8 @@ struct zt_tone_def { /* Structure for zone programming */
#define ZT_EVENT_POLARITY 17
#define ZT_EVENT_PULSEDIGIT (1 << 16) /* This is OR'd with the digit received */
-#define ZT_EVENT_DTMFDIGIT (1 << 17) /* Ditto for DTMF */
+#define ZT_EVENT_DTMFDOWN (1 << 17) /* Ditto for DTMF key down event */
+#define ZT_EVENT_DTMFUP (1 << 18) /* Ditto for DTMF key up event */
/* Flag Value for IOMUX, read avail */
#define ZT_IOMUX_READ 1
@@ -1242,6 +1251,9 @@ struct zt_span {
/* Opt: IOCTL */
int (*ioctl)(struct zt_chan *chan, unsigned int cmd, unsigned long data);
+ /* Opt: Native echo cancellation */
+ int (*echocan)(struct zt_chan *chan, int ecval);
+
/* Okay, now we get to the signalling. You have several options: */
/* Option 1: If you're a T1 like interface, you can just provide a
@@ -1261,6 +1273,9 @@ struct zt_span {
/* Option 3: If you can't use sig bits, you can write a function
which handles the individual hook states */
int (*sethook)(struct zt_chan *chan, int hookstate);
+
+ /* Opt: Dacs the contents of chan2 into chan1 if possible */
+ int (*dacs)(struct zt_chan *chan1, struct zt_chan *chan2);
/* Used by zaptel only -- no user servicable parts inside */
int spanno; /* Span number for zaptel */
@@ -1364,6 +1379,7 @@ extern struct zt_tone *zt_dtmf_tone(char digit, int mf);
not be doing so. rxchunk is modified in-place */
extern void zt_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, const unsigned char *txchunk);
+extern void zt_ec_span(struct zt_span *span);
/* Don't use these directly -- they're not guaranteed to
be there. */