📄 wcfxs.c
字号:
/* * Wilcard TDM400P TDM FXS/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/errno.h>#include <linux/pci.h>#include <linux/interrupt.h>#include "proslic.h"#include "wcfxs.h"/* * Define for audio vs. register based ring detection * *//* #define AUDIO_RINGCHECK *//* Experimental max loop current limit for the proslic Loop current limit is from 20 mA to 41 mA in steps of 3 (according to datasheet) So set the value below to: 0x00 : 20mA (default) 0x01 : 23mA 0x02 : 26mA 0x03 : 29mA 0x04 : 32mA 0x05 : 35mA 0x06 : 37mA 0x07 : 41mA*/static int loopcurrent = 20;static alpha indirect_regs[] ={{0,"DTMF_ROW_0_PEAK",0x55C2},{1,"DTMF_ROW_1_PEAK",0x51E6},{2,"DTMF_ROW2_PEAK",0x4B85},{3,"DTMF_ROW3_PEAK",0x4937},{4,"DTMF_COL1_PEAK",0x3333},{5,"DTMF_FWD_TWIST",0x0202},{6,"DTMF_RVS_TWIST",0x0202},{7,"DTMF_ROW_RATIO_TRES",0x0198},{8,"DTMF_COL_RATIO_TRES",0x0198},{9,"DTMF_ROW_2ND_ARM",0x0611},{10,"DTMF_COL_2ND_ARM",0x0202},{11,"DTMF_PWR_MIN_TRES",0x00E5},{12,"DTMF_OT_LIM_TRES",0x0A1C},{13,"OSC1_COEF",0x7B30},{14,"OSC1X",0x0063},{15,"OSC1Y",0x0000},{16,"OSC2_COEF",0x7870},{17,"OSC2X",0x007D},{18,"OSC2Y",0x0000},{19,"RING_V_OFF",0x0000},{20,"RING_OSC",0x7EF0},{21,"RING_X",0x0160},{22,"RING_Y",0x0000},{23,"PULSE_ENVEL",0x2000},{24,"PULSE_X",0x2000},{25,"PULSE_Y",0x0000},//{26,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower{26,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower{27,"XMIT_DIGITAL_GAIN",0x4000},//{27,"XMIT_DIGITAL_GAIN",0x2000},{28,"LOOP_CLOSE_TRES",0x1000},{29,"RING_TRIP_TRES",0x3600},{30,"COMMON_MIN_TRES",0x1000},{31,"COMMON_MAX_TRES",0x0200},{32,"PWR_ALARM_Q1Q2",0x07C0},{33,"PWR_ALARM_Q3Q4",0x2600},{34,"PWR_ALARM_Q5Q6",0x1B80},{35,"LOOP_CLOSURE_FILTER",0x8000},{36,"RING_TRIP_FILTER",0x0320},{37,"TERM_LP_POLE_Q1Q2",0x008C},{38,"TERM_LP_POLE_Q3Q4",0x0100},{39,"TERM_LP_POLE_Q5Q6",0x0010},{40,"CM_BIAS_RINGING",0x0C00},{41,"DCDC_MIN_V",0x0C00},{42,"DCDC_XTRA",0x1000},{43,"LOOP_CLOSE_TRES_LOW",0x1000},};static struct fxo_mode { char *name; /* FXO */ int ohs; int ohs2; int rz; int rt; int ilim; int dcv; int mini; int acim; int ring_osc; int ring_x;} fxo_modes[] ={ { "FCC", 0, 0, 0, 0, 0, 0x3, 0, 0 }, /* US, Canada */ { "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a }, /* Austria, Belgium, Denmark, Finland, France, Germany, Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, Norway, Portugal, Spain, Sweden, Switzerland, and UK */ { "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3 }, { "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3 }, { "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3 }, { "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf }, { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3 }, { "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4 }, { "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0 }, { "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0 }, /* Current loop >= 20ma */ { "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2 }, { "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4 }, { "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2 }, { "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0 }, { "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0 }, { "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3 }, { "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2 }, { "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3 }, { "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2 }, { "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0 }, { "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5 }, { "USA", 0, 0, 0, 0, 0, 0x3, 0, 0 }, { "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0 },};#ifdef STANDALONE_ZAPATA#include "zaptel.h"#else#include <linux/zaptel.h>#endif#ifdef LINUX26#include <linux/moduleparam.h>#endif#define NUM_FXO_REGS 60#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_AUXR 0x07#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 WC_REGBASE 0xc0#define WC_SYNC 0x0#define WC_TEST 0x1#define WC_CS 0x2#define WC_VER 0x3#define BIT_CS (1 << 2)#define BIT_SCLK (1 << 3)#define BIT_SDI (1 << 4)#define BIT_SDO (1 << 5)#define FLAG_EMPTY 0#define FLAG_WRITE 1#define FLAG_READ 2#define RING_DEBOUNCE 64 /* Ringer Debounce (in ms) */#define BATT_DEBOUNCE 64 /* Battery debounce (in ms) */#define POLARITY_DEBOUNCE 64 /* Polarity debounce (in ms) */#define BATT_THRESH 3 /* Anything under this is "no battery" */#define OHT_TIMER 6000 /* How long after RING to retain OHT */#define FLAG_DOUBLE_CLOCK (1 << 0)#define NUM_CARDS 4#define MAX_ALARMS 10#define MOD_TYPE_FXS 0#define MOD_TYPE_FXO 1#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 */#define NUM_CAL_REGS 12struct calregs { unsigned char vals[NUM_CAL_REGS];};struct wcfxs { struct pci_dev *dev; char *variety; struct zt_span span; unsigned char ios; int usecount; int intcount; int dead; int pos; int flags; int freeregion; int alt; int curcard; int cards; int cardflag; /* Bit-map of present cards */ spinlock_t lock; /* FXO Stuff */ union { struct {#ifdef AUDIO_RINGCHECK unsigned int pegtimer[NUM_CARDS]; int pegcount[NUM_CARDS]; int peg[NUM_CARDS]; int ring[NUM_CARDS];#else int wasringing[NUM_CARDS];#endif int ringdebounce[NUM_CARDS]; int offhook[NUM_CARDS]; int battdebounce[NUM_CARDS]; int nobatttimer[NUM_CARDS]; int battery[NUM_CARDS]; int lastpol[NUM_CARDS]; int polarity[NUM_CARDS]; int polaritydebounce[NUM_CARDS]; } fxo; struct { int oldrxhook[NUM_CARDS]; int debouncehook[NUM_CARDS]; int lastrxhook[NUM_CARDS]; int debounce[NUM_CARDS]; int ohttimer[NUM_CARDS]; int idletxhookstate[NUM_CARDS]; /* IDLE changing hook state */ int lasttxhook[NUM_CARDS]; int palarms[NUM_CARDS]; struct calregs calregs[NUM_CARDS]; } fxs; } mod; /* Receive hook state and debouncing */ int modtype[NUM_CARDS]; 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 */ struct zt_chan chans[NUM_CARDS];};struct wcfxs_desc { char *name; int flags;};static struct wcfxs_desc wcfxs = { "Wildcard S400P Prototype", 0 };static struct wcfxs_desc wcfxse = { "Wildcard TDM400P REV E/F", 0 };static struct wcfxs_desc wcfxsh = { "Wildcard TDM400P REV H", 0 };static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 };static struct wcfxs *ifaces[WC_MAX_IFACES];static void wcfxs_release(struct wcfxs *wc);static int debug = 0;static int robust = 0;static int timingonly = 0;static int lowpower = 0;static int boostringer = 0;static int _opermode = 0;static char *opermode = "FCC";static int fxshonormode = 0;static int wcfxs_init_proslic(struct wcfxs *wc, int card, int fast , int manual, int sane);static inline void wcfxs_transmitprep(struct wcfxs *wc, unsigned char ints){ volatile unsigned int *writechunk; int x; if (ints & 0x01) /* Write is at interrupt address. Start writing from normal offset */ writechunk = wc->writechunk; else writechunk = wc->writechunk + ZT_CHUNKSIZE; /* Calculate Transmission */ zt_transmit(&wc->span); for (x=0;x<ZT_CHUNKSIZE;x++) { /* Send a sample, as a 32-bit word */ writechunk[x] = 0; if (wc->cardflag & (1 << 3)) writechunk[x] |= (wc->chans[3].writechunk[x] << 24); if (wc->cardflag & (1 << 2)) writechunk[x] |= (wc->chans[2].writechunk[x] << 16); if (wc->cardflag & (1 << 1)) writechunk[x] |= (wc->chans[1].writechunk[x] << 8); if (wc->cardflag & (1 << 0)) writechunk[x] |= (wc->chans[0].writechunk[x]); }}#ifdef AUDIO_RINGCHECKstatic inline void ring_check(struct wcfxs *wc, int card){ int x; short sample; if (wc->modtype[card] != MOD_TYPE_FXO) return; wc->mod.fxo.pegtimer[card] += ZT_CHUNKSIZE; for (x=0;x<ZT_CHUNKSIZE;x++) { /* Look for pegging to indicate ringing */ sample = ZT_XLAW(wc->chans[card].readchunk[x], (&(wc->chans[card]))); if ((sample > 10000) && (wc->mod.fxo.peg[card] != 1)) { if (debug > 1) printk("High peg!\n"); if ((wc->mod.fxo.pegtimer[card] < PEGTIME) && (wc->mod.fxo.pegtimer[card] > MINPEGTIME)) wc->mod.fxo.pegcount[card]++; wc->mod.fxo.pegtimer[card] = 0; wc->mod.fxo.peg[card] = 1; } else if ((sample < -10000) && (wc->mod.fxo.peg[card] != -1)) { if (debug > 1) printk("Low peg!\n"); if ((wc->mod.fxo.pegtimer[card] < (PEGTIME >> 2)) && (wc->mod.fxo.pegtimer[card] > (MINPEGTIME >> 2))) wc->mod.fxo.pegcount[card]++; wc->mod.fxo.pegtimer[card] = 0; wc->mod.fxo.peg[card] = -1; } } if (wc->mod.fxo.pegtimer[card] > PEGTIME) { /* Reset pegcount if our timer expires */ wc->mod.fxo.pegcount[card] = 0; } /* Decrement debouncer if appropriate */ if (wc->mod.fxo.ringdebounce[card]) wc->mod.fxo.ringdebounce[card]--; if (!wc->mod.fxo.offhook[card] && !wc->mod.fxo.ringdebounce[card]) { if (!wc->mod.fxo.ring[card] && (wc->mod.fxo.pegcount[card] > PEGCOUNT)) { /* It's ringing */ if (debug) printk("RING on %d/%d!\n", wc->span.spanno, card + 1); if (!wc->mod.fxo.offhook[card]) zt_hooksig(&wc->chans[card], ZT_RXSIG_RING); wc->mod.fxo.ring[card] = 1; } if (wc->mod.fxo.ring[card] && !wc->mod.fxo.pegcount[card]) { /* No more ring */ if (debug) printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1); zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); wc->mod.fxo.ring[card] = 0; } }}#endifstatic inline void wcfxs_receiveprep(struct wcfxs *wc, unsigned char ints)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -