summaryrefslogtreecommitdiff
path: root/wcfxo.c
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2002-06-29 18:08:17 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2002-06-29 18:08:17 +0000
commitf11777d1a323daf29ff336e6a5aed84d8198b360 (patch)
tree03a59b028fa364727024fcf8d493d843afd4e91b /wcfxo.c
parentb40acbadfeec861fee8c403d9fc019f2ec73ccbd (diff)
Version 0.2.0 from FTP
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@89 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'wcfxo.c')
-rwxr-xr-xwcfxo.c240
1 files changed, 152 insertions, 88 deletions
diff --git a/wcfxo.c b/wcfxo.c
index b878467..1b020fd 100755
--- a/wcfxo.c
+++ b/wcfxo.c
@@ -12,15 +12,15 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
@@ -37,9 +37,10 @@
#include <linux/zaptel.h>
#endif
-/* RING detection fiddling */
-#define RING_INC 10
-#define RING_MAX 50
+/* Uncomment to enable tasklet handling in the FXO driver. Not recommended
+ in general, but may improve interactive performance */
+
+/* #define ENABLE_TASKLETS */
#define WC_MAX_IFACES 128
@@ -67,7 +68,11 @@
#define FLAG_READ 2
#define RING_DEBOUNCE 64 /* Ringer Debounce (in ms) */
-#define BATT_DEBOUNCE 8 /* Battery debounce (in ms) */
+#define BATT_DEBOUNCE 80 /* Battery debounce (in ms) */
+
+#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 */
+#define PEGCOUNT 5 /* 5 cycles of pegging means RING */
struct reg {
int flags;
@@ -90,11 +95,13 @@ struct wcfxo {
int flags;
int freeregion;
int ring;
+ int offhook;
int battery;
int wregcount;
int readpos;
int rreadpos;
- int ringdebounce;
+ unsigned int pegtimer;
+ int pegcount;
int battdebounce;
int nobatttimer;
int allread;
@@ -105,6 +112,7 @@ struct wcfxo {
/* Up to 6 register can be written at a time */
struct reg regs[ZT_CHUNKSIZE];
struct reg oldregs[ZT_CHUNKSIZE];
+ unsigned char lasttx[ZT_CHUNKSIZE];
/* Up to 32 registers of whatever we most recently read */
unsigned char readregs[32];
unsigned long ioaddr;
@@ -112,6 +120,15 @@ struct wcfxo {
dma_addr_t writedma;
volatile int *writechunk; /* Double-word aligned write memory */
volatile int *readchunk; /* Double-word aligned read memory */
+#ifdef ENABLE_TASKLETS
+ int taskletrun;
+ int taskletsched;
+ int taskletpending;
+ int taskletexec;
+ int txerrors;
+ int ints;
+ struct tasklet_struct wcfxo_tlet;
+#endif
};
#define FLAG_INVERTSER (1 << 0)
@@ -124,8 +141,9 @@ struct wcfxo_desc {
int flags;
};
+
static struct wcfxo_desc wcfxo = { "Wildcard Prototype", 0};
-static struct wcfxo_desc wcx100p = { "Wildcard X100P",
+static struct wcfxo_desc wcx100p = { "Wildcard X100P",
FLAG_INVERTSER | FLAG_USE_XTAL | FLAG_DOUBLE_CLOCK };
static struct wcfxo *ifaces[WC_MAX_IFACES];
@@ -142,23 +160,37 @@ static inline void wcfxo_transmitprep(struct wcfxo *wc, unsigned char ints)
int x;
int written=0;
unsigned short cmd;
- if (ints & 0x01)
+
+ /* if nothing to transmit, have to do the zt_transmit() anyway */
+ if (!(ints & 3)) {
+ /* Calculate Transmission */
+ zt_transmit(&wc->span);
+ return;
+ }
+
+ /* Remember what it was we just sent */
+ memcpy(wc->lasttx, wc->chan.writechunk, ZT_CHUNKSIZE);
+
+ if (ints & 0x01) {
/* Write is at interrupt address. Start writing from normal offset */
writechunk = wc->writechunk;
- else
+ } else {
writechunk = wc->writechunk + ZT_CHUNKSIZE * 2;
- /* Calculate Transmission */
+ }
+
zt_transmit(&wc->span);
for (x=0;x<ZT_CHUNKSIZE;x++) {
/* Send a sample, as a 32-bit word, and be sure to indicate that a command follows */
- if (wc->flags & FLAG_INVERTSER)
- writechunk[x << 1] =
- ~((unsigned short)(ZT_MULAW(wc->chan.writechunk[x]))| 0x1) << 16;
+ if (wc->flags & FLAG_INVERTSER)
+ writechunk[x << 1] = cpu_to_le32(
+ ~((unsigned short)(ZT_XLAW(wc->chan.writechunk[x], (&wc->chan)))| 0x1) << 16
+ );
else
- writechunk[x << 1] =
- ((unsigned short)(ZT_MULAW(wc->chan.writechunk[x]))| 0x1) << 16;
-
+ writechunk[x << 1] = cpu_to_le32(
+ ((unsigned short)(ZT_XLAW(wc->chan.writechunk[x], (&wc->chan)))| 0x1) << 16
+ );
+
/* We always have a command to follow our signal */
if (!wc->regs[x].flags) {
/* Fill in an empty register command with a read for a potentially useful register */
@@ -172,7 +204,7 @@ static inline void wcfxo_transmitprep(struct wcfxo *wc, unsigned char ints)
}
}
- /* Prepare the command to follow it */
+ /* Prepare the command to follow it */
switch(wc->regs[x].flags) {
case FLAG_READ:
cmd = (wc->regs[x].reg | 0x20) << 8;
@@ -189,9 +221,9 @@ static inline void wcfxo_transmitprep(struct wcfxo *wc, unsigned char ints)
}
/* Setup the write chunk */
if (wc->flags & FLAG_INVERTSER)
- writechunk[(x << 1) + 1] = ~(cmd << 16);
+ writechunk[(x << 1) + 1] = cpu_to_le32(~(cmd << 16));
else
- writechunk[(x << 1) + 1] = cmd << 16;
+ writechunk[(x << 1) + 1] = cpu_to_le32(cmd << 16);
}
if (written)
wc->readpos = 0;
@@ -211,24 +243,29 @@ static inline void wcfxo_receiveprep(struct wcfxo *wc, unsigned char ints)
int x;
int realreg;
int realval;
- if (ints & 0x08)
+ int sample;
+ static int peg = 0;
+ if (ints & 0x04)
/* Read is at interrupt address. Valid data is available at normal offset */
readchunk = wc->readchunk;
else
readchunk = wc->readchunk + ZT_CHUNKSIZE * 2;
+
+ /* Keep track of how quickly our peg alternates */
+ wc->pegtimer+=ZT_CHUNKSIZE;
for (x=0;x<ZT_CHUNKSIZE;x++) {
-
+
/* We always have a command to follow our signal. */
if (wc->oldregs[x].flags == FLAG_READ && !wc->ignoreread) {
- realreg = wecareregs[(wc->regs[x].index + wc->regoffset) %
- (sizeof(wecareregs) / sizeof(wecareregs[0]))];
- realval = (readchunk[(x << 1) +wc->alt] >> 16) & 0xff;
+ realreg = wecareregs[(wc->regs[x].index + wc->regoffset) %
+ (sizeof(wecareregs) / sizeof(wecareregs[0]))];
+ realval = (le32_to_cpu(readchunk[(x << 1) +wc->alt]) >> 16) & 0xff;
if ((realval == 0x89) && (realreg != 0x9)) {
/* Some sort of slippage, correct for it */
while(realreg != 0x9) {
/* Find register 9 */
- realreg = wecareregs[(wc->regs[x].index + ++wc->regoffset) %
- (sizeof(wecareregs) / sizeof(wecareregs[0]))];
+ realreg = wecareregs[(wc->regs[x].index + ++wc->regoffset) %
+ (sizeof(wecareregs) / sizeof(wecareregs[0]))];
wc->regoffset = wc->regoffset % (sizeof(wecareregs) / sizeof(wecareregs[0]));
}
if (debug)
@@ -237,13 +274,67 @@ static inline void wcfxo_receiveprep(struct wcfxo *wc, unsigned char ints)
/* Receive into the proper register */
wc->readregs[realreg] = realval;
}
- wc->chan.readchunk[x] = ZT_LIN2MU(((short)(readchunk[(x << 1) + (1 - wc->alt)] >> 16)));
+ /* Look for pegging to indicate ringing */
+ sample = (short)(le32_to_cpu(readchunk[(x << 1) + (1 - wc->alt)]) >> 16);
+ if ((sample > 32000) && (peg != 1)) {
+ if ((wc->pegtimer < PEGTIME) && (wc->pegtimer > MINPEGTIME))
+ wc->pegcount++;
+ wc->pegtimer = 0;
+ peg = 1;
+ } else if ((sample < -32000) && (peg != -1)) {
+ if ((wc->pegtimer < PEGTIME) && (wc->pegtimer > MINPEGTIME))
+ wc->pegcount++;
+ wc->pegtimer = 0;
+ peg = -1;
+ }
+ wc->chan.readchunk[x] = ZT_LIN2X((sample), (&wc->chan));
+ }
+ if (wc->pegtimer > PEGTIME) {
+ /* Reset pegcount if our timer expires */
+ wc->pegcount = 0;
+ }
+ if (!wc->offhook) {
+ if (!wc->ring && (wc->pegcount > PEGCOUNT)) {
+ /* It's ringing */
+ if (debug)
+ printk("RING!\n");
+ zt_hooksig(&wc->chan, ZT_RXSIG_RING);
+ wc->ring = 1;
+ }
+ if (wc->ring && !wc->pegcount) {
+ /* No more ring */
+ if (debug)
+ printk("NO RING!\n");
+ zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK);
+ wc->ring = 0;
+ }
}
if (wc->ignoreread)
wc->ignoreread--;
+
+ /* Do the echo cancellation... We are echo cancelling against
+ what we sent two chunks ago*/
+ zt_ec_chunk(&wc->chan, wc->chan.readchunk, wc->lasttx);
+
+ /* Receive the result */
zt_receive(&wc->span);
}
+#ifdef ENABLE_TASKLETS
+static void wcfxo_tasklet(unsigned long data)
+{
+ struct wcfxo *wc = (struct wcfxo *)data;
+ wc->taskletrun++;
+ /* Run tasklet */
+ if (wc->taskletpending) {
+ wc->taskletexec++;
+ wcfxo_receiveprep(wc, wc->ints);
+ wcfxo_transmitprep(wc, wc->ints);
+ }
+ wc->taskletpending = 0;
+}
+#endif
+
static void wcfxo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct wcfxo *wc = dev_id;
@@ -261,65 +352,33 @@ static void wcfxo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (!ints)
return;
- if (ints & 0x0f) {
+ if (ints & 0x0c) { /* if there is a rx interrupt pending */
+#ifdef ENABLE_TASKLETS
+ wc->ints = ints;
+ if (!wc->taskletpending) {
+ wc->taskletpending = 1;
+ wc->taskletsched++;
+ tasklet_hi_schedule(&wc->wcfxo_tlet);
+ } else
+ wc->txerrors++;
+#else
wcfxo_receiveprep(wc, ints);
+ /* transmitprep looks to see if there is anything to transmit
+ and returns by itself if there is nothing */
wcfxo_transmitprep(wc, ints);
+#endif
}
-
+
if (ints & 0x10) {
printk("PCI Master abort\n");
return;
}
-
+
if (ints & 0x20) {
printk("PCI Target abort\n");
return;
}
-
if (1 /* !(wc->report % 0xf) */) {
- /* Check RING from register and debounce. This is actually
- fairly challenging to do, because the ring behavior
- seems to differ from one system to another. */
- b = wc->readregs[0x5] & 0x60;
-#ifdef DEBUG_RING
- if (b != oldb) {
- printk("RING Change from %02x to %02x after %d (ringdebounce = %d)\n", oldb, b, oldcnt, wc->ringdebounce);
- oldb = b;
- oldcnt = 0;
- } else
- oldcnt++;
-#endif
- if (!b) {
- /* No ring -- subtract one from our counter. We need
- a whole bunch of no-rings in a row before we consider
- the real ring actually over */
- if (wc->ringdebounce > 0)
- wc->ringdebounce -= 1;
- if (wc->ring && !wc->ringdebounce) {
-#ifndef DEBUG_RING
- if (debug)
-#endif
- printk("NO RING!\n");
- zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK);
- wc->ring = 0;
- }
- } else {
- /* Ring detected... Increment our counter by a bunch */
- wc->ringdebounce += RING_INC;
- /* Max out at RING_MAX. */
- if (wc->ringdebounce > RING_MAX)
- wc->ringdebounce = RING_MAX;
- if (!wc->ring && (wc->ringdebounce >= RING_MAX)) {
- /* Trigger the ring */
-#ifndef DEBUG_RING
- if (debug)
-#endif
- printk("RING!\n");
- zt_hooksig(&wc->chan, ZT_RXSIG_RING);
- wc->ring = 1;
- }
- }
-
/* Check for BATTERY from register and debounce for 8 ms */
b = wc->readregs[0xc] & 0xf;
if (!b) {
@@ -327,7 +386,7 @@ static void wcfxo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#if 0
if (wc->battery)
printk("Battery loss: %d (%d debounce)\n", b, wc->battdebounce);
-#endif
+#endif
if (wc->battery && !wc->battdebounce) {
if (debug)
printk("NO BATTERY!\n");
@@ -359,7 +418,7 @@ static void wcfxo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* It's something else... */
wc->battdebounce = BATT_DEBOUNCE;
}
-
+
if (wc->battdebounce)
wc->battdebounce--;
}
@@ -396,7 +455,7 @@ static int wcfxo_close(struct zt_chan *chan)
wc->usecount--;
MOD_DEC_USE_COUNT;
/* If we're dead, release us now */
- if (!wc->usecount && wc->dead)
+ if (!wc->usecount && wc->dead)
wcfxo_release(wc);
return 0;
}
@@ -412,17 +471,19 @@ static int wcfxo_hooksig(struct zt_chan *chan, zt_txsig_t txsig)
be done in two steps because of a hardware bug. */
reg = wc->readregs[0x5] & ~0x08;
wcfxo_setreg(wc, 0x5, reg);
-
+
reg = reg | 0x1;
wcfxo_setreg(wc, 0x5, reg);
+ wc->offhook = 1;
break;
case ZT_TXSIG_ONHOOK:
/* Put on hook and enable on hook line monitor */
reg = wc->readregs[0x5] & 0xfe;
wcfxo_setreg(wc, 0x5, reg);
-
+
reg = reg | 0x08;
wcfxo_setreg(wc, 0x5, reg);
+ wc->offhook = 0;
break;
default:
printk("wcfxo: Can't set tx state to %d\n", txsig);
@@ -447,6 +508,9 @@ static int wcfxo_initialize(struct wcfxo *wc)
wc->span.close = wcfxo_close;
wc->span.flags = ZT_FLAG_RBS;
wc->span.deflaw = ZT_LAW_MULAW;
+#ifdef ENABLE_TASKLETS
+ tasklet_init(&wc->wcfxo_tlet, wcfxo_tasklet, (unsigned long)wc);
+#endif
init_waitqueue_head(&wc->span.maintq);
wc->span.pvt = wc;
@@ -473,13 +537,13 @@ static int wcfxo_hardware_init(struct wcfxo *wc)
}
/* Set all to outputs except AUX 4, which is an input */
outb(0xef, wc->ioaddr + WC_AUXC);
-
+
/* Back to normal, with automatic DMA wrap around */
outb(0x01, wc->ioaddr + WC_CNTL);
-
+
/* Make sure serial port and DMA are out of reset */
outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL);
-
+
/* Configure serial port for MSB->LSB operation */
if (wc->flags & FLAG_DOUBLE_CLOCK)
outb(0xc1, wc->ioaddr + WC_SERCTL);
@@ -549,7 +613,7 @@ static int wcfxo_init_daa(struct wcfxo *wc)
/* Let the reset go */
set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1);
+ schedule_timeout(1 + (ZT_CHUNKSIZE * HZ) / 800);
/* We have a clock at 18.432 Mhz, so N1=1, M1=2, CGM=0 */
wcfxo_setreg(wc, 0x7, 0x0); /* This value is N1 - 1 */
@@ -559,7 +623,7 @@ static int wcfxo_init_daa(struct wcfxo *wc)
/* Wait until the PLL's are locked. Time is between 100 uSec and 1 mSec */
set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);
+ schedule_timeout(1 + HZ/1000 + (ZT_CHUNKSIZE * HZ) / 800);
/* No additional ration is applied to the PLL and faster lock times
* are possible */
@@ -576,10 +640,10 @@ static int wcfxo_init_daa(struct wcfxo *wc)
/* Wait a couple of jiffies for our writes to finish */
set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);
+ schedule_timeout(1 + (ZT_CHUNKSIZE * HZ) / 800);
/* Didn't get it right. Register 9 is still garbage */
- if (wc->readregs[0x9] != 0x89)
+ if (wc->readregs[0x9] != 0x89)
return -1;
#if 0
{ int x;