?? wcfxo.c
字號(hào):
/* * Wilcard X100P FXO Interface Driver for Zapata Telephony interface * * Written by Mark Spencer <markster@linux-support.net> * Matthew Fredrickson <creslin@linux-support.net> * * Copyright (C) 2001, Linux Support Services, Inc. * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <linux/kernel.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/init.h>#include <linux/usb.h>#include <linux/errno.h>#include <linux/pci.h>#ifdef STANDALONE_ZAPATA#include "zaptel.h"#else#include <linux/zaptel.h>#endif#ifdef LINUX26#include <linux/moduleparam.h>#endif/* Uncomment to enable tasklet handling in the FXO driver. Not recommended in general, but may improve interactive performance *//* #define ENABLE_TASKLETS *//* Un-comment the following for POTS line support for Japan *//* #define JAPAN *//* Un-comment for lines (eg from and ISDN TA) that remove *//* phone power during ringing *//* #define ZERO_BATT_RING */#define WC_MAX_IFACES 128#define WC_CNTL 0x00#define WC_OPER 0x01#define WC_AUXC 0x02#define WC_AUXD 0x03#define WC_MASK0 0x04#define WC_MASK1 0x05#define WC_INTSTAT 0x06#define WC_DMAWS 0x08#define WC_DMAWI 0x0c#define WC_DMAWE 0x10#define WC_DMARS 0x18#define WC_DMARI 0x1c#define WC_DMARE 0x20#define WC_AUXFUNC 0x2b#define WC_SERCTL 0x2d#define WC_FSCDELAY 0x2f#define FLAG_EMPTY 0#define FLAG_WRITE 1#define FLAG_READ 2#ifdef ZERO_BATT_RING /* Need to debounce Off/On hook too */#define JAPAN#endif#define RING_DEBOUNCE 64 /* Ringer Debounce (in ms) */#ifdef JAPAN#define BATT_DEBOUNCE 30 /* Battery debounce (in ms) */#define OH_DEBOUNCE 350 /* Off/On hook debounce (in ms) */#else#define BATT_DEBOUNCE 80 /* Battery debounce (in ms) */#endif#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 { unsigned long flags; unsigned char index; unsigned char reg; unsigned char value;};static int wecareregs[] = { 5, 6, 9, 11, 12, 13, 17, 19, };struct wcfxo { struct pci_dev *dev; char *variety; struct zt_span span; struct zt_chan chan; int usecount; int dead; int pos; unsigned long flags; int freeregion; int ring; int offhook; int battery; int wregcount; int readpos; int rreadpos; unsigned int pegtimer; int pegcount; int peg; int battdebounce; int nobatttimer; int ringdebounce;#ifdef JAPAN int ohdebounce;#endif int allread; int regoffset; /* How far off our registers are from what we expect */ int alt; int ignoreread; int reset; /* 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; dma_addr_t readdma; dma_addr_t writedma; volatile int *writechunk; /* Double-word aligned write memory */ volatile int *readchunk; /* Double-word aligned read memory */#ifdef ZERO_BATT_RING int onhook;#endif#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)#define FLAG_USE_XTAL (1 << 1)#define FLAG_DOUBLE_CLOCK (1 << 2)#define FLAG_RESET_ON_AUX5 (1 << 3)struct wcfxo_desc { char *name; unsigned long flags;};static struct wcfxo_desc wcx100p = { "Wildcard X100P", FLAG_INVERTSER | FLAG_USE_XTAL | FLAG_DOUBLE_CLOCK };static struct wcfxo_desc wcx101p = { "Wildcard X101P", FLAG_USE_XTAL | FLAG_DOUBLE_CLOCK };static struct wcfxo_desc generic = { "Generic Clone", FLAG_USE_XTAL | FLAG_DOUBLE_CLOCK };static struct wcfxo *ifaces[WC_MAX_IFACES];static void wcfxo_release(struct wcfxo *wc);static int debug = 0;static int monitor = 0;static int quiet = 0;static int boost = 0;static int opermode = 0;static struct fxo_mode { char *name; int ohs; int act; int dct; int rz; int rt; int lim; int vol;} fxo_modes[] ={ { "FCC", 0, 0, 2, 0, 0, 0, 0 }, /* US */ { "CTR21", 0, 0, 3, 0, 0, 3, 0 }, /* Austria, Belgium, Denmark, Finland, France, Germany, Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, Norway, Portugal, Spain, Sweden, Switzerland, and UK */};static inline void wcfxo_transmitprep(struct wcfxo *wc, unsigned char ints){ volatile int *writechunk; int x; int written=0; unsigned short cmd; /* 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 { writechunk = wc->writechunk + ZT_CHUNKSIZE * 2; } 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] = cpu_to_le32( ~((unsigned short)(ZT_XLAW(wc->chan.writechunk[x], (&wc->chan)))| 0x1) << 16 ); else 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 */ wc->regs[x].flags = FLAG_READ; wc->regs[x].reg = wecareregs[wc->readpos]; wc->regs[x].index = wc->readpos; wc->readpos++; if (wc->readpos >= (sizeof(wecareregs) / sizeof(wecareregs[0]))) { wc->allread = 1; wc->readpos = 0; } } /* Prepare the command to follow it */ switch(wc->regs[x].flags) { case FLAG_READ: cmd = (wc->regs[x].reg | 0x20) << 8; break; case FLAG_WRITE: cmd = (wc->regs[x].reg << 8) | (wc->regs[x].value & 0xff); written = 1; /* Wait at least four samples before reading */ wc->ignoreread = 4; break; default: printk("wcfxo: Huh? No read or write??\n"); cmd = 0; } /* Setup the write chunk */ if (wc->flags & FLAG_INVERTSER) writechunk[(x << 1) + 1] = cpu_to_le32(~(cmd << 16)); else writechunk[(x << 1) + 1] = cpu_to_le32(cmd << 16); } if (written) wc->readpos = 0; wc->wregcount = 0; for (x=0;x<ZT_CHUNKSIZE;x++) { /* Rotate through registers */ wc->oldregs[x] = wc->regs[x]; wc->regs[x].flags = FLAG_EMPTY; }}static inline void wcfxo_receiveprep(struct wcfxo *wc, unsigned char ints){ volatile int *readchunk; int x; int realreg; int realval; int sample; 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 = (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]))]; wc->regoffset = wc->regoffset % (sizeof(wecareregs) / sizeof(wecareregs[0])); } if (debug) printk("New regoffset: %d\n", wc->regoffset); } /* Receive into the proper register */ wc->readregs[realreg] = realval; } /* Look for pegging to indicate ringing */ sample = (short)(le32_to_cpu(readchunk[(x << 1) + (1 - wc->alt)]) >> 16); if ((sample > 32000) && (wc->peg != 1)) { if ((wc->pegtimer < PEGTIME) && (wc->pegtimer > MINPEGTIME)) wc->pegcount++; wc->pegtimer = 0; wc->peg = 1; } else if ((sample < -32000) && (wc->peg != -1)) { if ((wc->pegtimer < PEGTIME) && (wc->pegtimer > MINPEGTIME)) wc->pegcount++; wc->pegtimer = 0; wc->peg = -1; } wc->chan.readchunk[x] = ZT_LIN2X((sample), (&wc->chan)); } if (wc->pegtimer > PEGTIME) { /* Reset pegcount if our timer expires */ wc->pegcount = 0; } /* Decrement debouncer if appropriate */ if (wc->ringdebounce) wc->ringdebounce--; if (!wc->offhook && !wc->ringdebounce) { 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_TASKLETSstatic 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;}#endifstatic void wcfxo_stop_dma(struct wcfxo *wc);static void wcfxo_restart_dma(struct wcfxo *wc);#ifdef LINUX26static irqreturn_t wcfxo_interrupt(int irq, void *dev_id, struct pt_regs *regs)#elsestatic void wcfxo_interrupt(int irq, void *dev_id, struct pt_regs *regs)#endif{ struct wcfxo *wc = dev_id; unsigned char ints; unsigned char b;#ifdef DEBUG_RING static int oldb = 0; static int oldcnt = 0;#endif ints = inb(wc->ioaddr + WC_INTSTAT); outb(ints, wc->ioaddr + WC_INTSTAT); if (!ints)#ifdef LINUX26 return IRQ_NONE;#else return;#endif 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("FXO PCI Master abort\n"); /* Stop DMA andlet the watchdog start it again */ wcfxo_stop_dma(wc);#ifdef LINUX26 return IRQ_RETVAL(1);#else return;#endif } if (ints & 0x20) { printk("PCI Target abort\n");#ifdef LINUX26 return IRQ_RETVAL(1);#else return;#endif } if (1 /* !(wc->report % 0xf) */) { /* Check for BATTERY from register and debounce for 8 ms */ b = wc->readregs[0xc] & 0xf; if (!b) { wc->nobatttimer++;#if 0 if (wc->battery) printk("Battery loss: %d (%d debounce)\n", b, wc->battdebounce);#endif if (wc->battery && !wc->battdebounce) { if (debug) printk("NO BATTERY!\n"); wc->battery = 0;#ifdef JAPAN if ((!wc->ohdebounce) && wc->offhook) { zt_hooksig(&wc->chan, ZT_RXSIG_ONHOOK); if (debug) printk("Signalled On Hook\n");#ifdef ZERO_BATT_RING wc->onhook++;#endif }#else zt_hooksig(&wc->chan, ZT_RXSIG_ONHOOK);#endif wc->battdebounce = BATT_DEBOUNCE; } else if (!wc->battery) wc->battdebounce = BATT_DEBOUNCE; if ((wc->nobatttimer > 5000) &&#ifdef ZERO_BATT_RING !(wc->readregs[0x05] & 0x04) &&#endif (!wc->span.alarms)) { wc->span.alarms = ZT_ALARM_RED; zt_alarm_notify(&wc->span); } } else if (b == 0xf) { if (!wc->battery && !wc->battdebounce) { if (debug) printk("BATTERY!\n");#ifdef ZERO_BATT_RING if (wc->onhook) { wc->onhook = 0; zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK); if (debug) printk("Signalled Off Hook\n"); }#else zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK);#endif wc->battery = 1; wc->nobatttimer = 0; wc->battdebounce = BATT_DEBOUNCE; if (wc->span.alarms) { wc->span.alarms = 0; zt_alarm_notify(&wc->span); } } else if (wc->battery) wc->battdebounce = BATT_DEBOUNCE; } else { /* It's something else... */ wc->battdebounce = BATT_DEBOUNCE; }
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -