⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wcfxo.c

📁 This a SOFTWARE pbx DRIVER
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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;		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -