Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 222683 Details for
Bug 302316
net-misc/dahdi - additional card support (tzafrir branch)
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
dahdi-2.2.0.2-nondigium.patch
dahdi-2.2.0.2-nondigium.patch (text/plain), 210.20 KB, created by
Jaco Kroon
on 2010-03-08 15:12:14 UTC
(
hide
)
Description:
dahdi-2.2.0.2-nondigium.patch
Filename:
MIME Type:
Creator:
Jaco Kroon
Created:
2010-03-08 15:12:14 UTC
Size:
210.20 KB
patch
obsolete
>Subject: dahdi-extra: out-of-tree DAHDI drivers >Origin: http://git.tzafrir.org.il/?p=dahdi-extra.git >Forwarded: No >Last-Update: 2010-01-06 > >This patch includes a number of out-of-tree DAHDI drivers from the >dahdi-extra repository. They are all out-of-tree and are highly likely > not to be included in DAHDI-linux in the forseeable future. > >Git-Commit: 363ae0d3647133e7db41e8b17af2cf4b8f10ee52 >363ae0d3647133e7db41e8b17af2cf4b8f10ee52 >--- > >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/Kbuild dahdi-svn-new/drivers/dahdi/Kbuild >--- dahdi-linux-2.2.0.2/drivers/dahdi/Kbuild 2009-06-12 15:30:02.000000000 -0700 >+++ dahdi-svn-new/drivers/dahdi/Kbuild 2010-01-06 12:15:03.000000000 -0800 >@@ -1,10 +1,13 @@ > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI) += dahdi.o >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200.o > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DUMMY) += dahdi_dummy.o >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCOPENPCI) += wcopenpci.o > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC) += dahdi_dynamic.o >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ZAPHFC) += zaphfc/ > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_LOC) += dahdi_dynamic_loc.o > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETH) += dahdi_dynamic_eth.o > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TRANSCODE) += dahdi_transcode.o > > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP) += wct4xxp/ > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP) += wctc4xxp/ > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp/ >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/Kconfig dahdi-svn-new/drivers/dahdi/Kconfig >--- dahdi-linux-2.2.0.2/drivers/dahdi/Kconfig 2009-04-29 10:48:39.000000000 -0700 >+++ dahdi-svn-new/drivers/dahdi/Kconfig 2010-01-06 12:15:03.000000000 -0800 >@@ -279,3 +279,79 @@ > If unsure, say Y. > > source "drivers/dahdi/xpp/Kconfig" >+ >+ >+config DAHDI_OPVXA1200 >+ tristate "OpenVox A1200P FXS/FXO Interface" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the OpenVox A1200P FXS/FXO Interface. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called opvxa1200. >+ >+ If unsure, say Y. >+ >+config DAHDI_WCOPENPCI >+ tristate "Voicetronix OpenPCI Interface DAHDI driver" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the Voicetronix OpenPCI Interface. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called wcopenpci. >+ >+ If unsure, say Y. >+ >+config DAHDI_ZAPHFC >+ tristate "HFC-S DAHDI Driver" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides DAHDI support for various HFC-S single-port >+ ISDN (BRI) cards. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called zaphfc. >+ >+ If unsure, say Y. >+ >+config DAHDI_OPVXA1200 >+ tristate "OpenVox A1200P FXS/FXO Interface" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the OpenVox A1200P FXS/FXO Interface. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called opvxa1200. >+ >+ If unsure, say Y. >+ >+config DAHDI_WCOPENPCI >+ tristate "Voicetronix OpenPCI Interface DAHDI driver" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the Voicetronix OpenPCI Interface. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called wcopenpci. >+ >+ If unsure, say Y. >+ >+config DAHDI_ZAPHFC >+ tristate "HFC-S DAHDI Driver" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides DAHDI support for various HFC-S single-port >+ ISDN (BRI) cards. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called zaphfc. >+ >+ If unsure, say Y. >+ >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/opvxa1200.c dahdi-svn-new/drivers/dahdi/opvxa1200.c >--- dahdi-linux-2.2.0.2/drivers/dahdi/opvxa1200.c 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/opvxa1200.c 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,3018 @@ >+/* >+ * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface >+ * >+ * Modify from wctdm.c by MiaoLin<miaolin@openvox.com.cn> >+ * >+ * 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. >+ * >+ */ >+ >+/* Rev histroy >+ * >+ * Rev 0.10 initial version >+ * Rev 0.11 >+ * fixed the led light on/off bug. >+ * modify some wctdm print to opvxa1200 >+ * support firmware version 1.2, faster i/o operation, and better LED control. >+ * >+ * Rev 0.12 patched to support new pci id 0x8519 >+ * Rev 0.13 patched to remove the warning during compile under kernel 2.6.22 >+ * Rev 0.14 patched to remove the bug for ZAP_IRQ_SHARED , 3/9/2007 >+ * Rev 0.15 patched to support new pci ID 0X9532 by james.zhu, 23/10/2007 >+ * Rev 0.16 support new pci id 0x9559 by Miao Lin 21/3/2008 >+ * Rev 0.17 >+ * patched a few bugs, >+ * add hwgain support. >+ * fixed A800P version check >+ * Rev 1.4.9.2 >+ * Only generate 8 channels for A800P >+ * Version number synced to zaptel distribution. >+ * Rev 1.4.9.2.a >+ * Fixed freeregion. >+ * >+ * Rev 1.4.9.2.b >+ * Add cid before first ring support. >+ * New Paremeters: >+ * cidbeforering : set to 1 will cause the card enable cidbeforering function. default 0 >+ * cidbuflen : length of cid buffer, in msec, default 3000 msec. >+ * cidtimeout : time out of a ring, default 6000msec >+ * User must set cidstart=polarity in zapata.conf to use with this feature >+ * cidsignalling = signalling format send before 1st ring. most likely dtmf. >+ * >+ * Rev 1.4.9.2.c >+ * add driver parameter cidtimeout. >+ * >+ * Rev 1.4.9.2.d >+ * add debug stuff to test fxs power alarm >+ * >+ * Rev 1.4.11 >+ * Support enhanced full scale tx/rx for FXO required by europe standard (Register 30, acim) (module parm fxofullscale) >+ * >+ * Rev 1.4.12 2008/10/17 >+ * Fixed bug cause FXS module report fake power alarm. >+ * Power alarm debug stuff removed. >+ * >+ * Rev 2.0 DAHDI 2008/10/17 >+ * >+ * Rev 2.0.1 add new pci id 0x9599 >+ * Re 2.0.2 12/01/2009 >+ add fixedtimepolarity: set time(ms) when send polarity after 1st ring happen. >+ * Sometimes the dtmf cid is sent just after first ring off, and the system do not have >+ * enough time to start detect 1st dtmf. >+ * 0 means send polarity at the end of 1st ring. >+ * x means send ploarity after x ms of 1st ring begin. >+ * >+ * Rev 2.0.3 12/01/2009 >+ * Add touch_softlockup_watchdog() in wctdm_hardware_init, to avoid cpu softlockup system message for FXS. >+ * >+ * >+ * Rev 1.4.12.4 17/04/2009 James.zhu >+ * Changed wctdm_voicedaa_check_hook() to detect FXO battery and solved the problem with dial(dahdi/go/XXXXXXXXXX) >+ * add alarm detection for FXO >+ * >+ * Rev 1.4.12.5 01/10/2009 james.zhu >+ * Add jiffies for 5 second in wctdm_hardware_init >+ * >+ * >+ */ >+ >+#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/sched.h> >+#include <linux/interrupt.h> >+#include <linux/moduleparam.h> >+#include <asm/io.h> >+#include "proslic.h" >+ >+/* MiaoLin debug start */ >+#include <linux/string.h> >+#include <asm/uaccess.h> /* get_fs(), set_fs(), KERNEL_DS */ >+#include <linux/file.h> /* fput() */ >+/* MiaoLin debug end */ >+ >+ >+/* >+ * 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 int reversepolarity = 0; >+ >+static alpha indirect_regs[] = >+{ >+{0,255,"DTMF_ROW_0_PEAK",0x55C2}, >+{1,255,"DTMF_ROW_1_PEAK",0x51E6}, >+{2,255,"DTMF_ROW2_PEAK",0x4B85}, >+{3,255,"DTMF_ROW3_PEAK",0x4937}, >+{4,255,"DTMF_COL1_PEAK",0x3333}, >+{5,255,"DTMF_FWD_TWIST",0x0202}, >+{6,255,"DTMF_RVS_TWIST",0x0202}, >+{7,255,"DTMF_ROW_RATIO_TRES",0x0198}, >+{8,255,"DTMF_COL_RATIO_TRES",0x0198}, >+{9,255,"DTMF_ROW_2ND_ARM",0x0611}, >+{10,255,"DTMF_COL_2ND_ARM",0x0202}, >+{11,255,"DTMF_PWR_MIN_TRES",0x00E5}, >+{12,255,"DTMF_OT_LIM_TRES",0x0A1C}, >+{13,0,"OSC1_COEF",0x7B30}, >+{14,1,"OSC1X",0x0063}, >+{15,2,"OSC1Y",0x0000}, >+{16,3,"OSC2_COEF",0x7870}, >+{17,4,"OSC2X",0x007D}, >+{18,5,"OSC2Y",0x0000}, >+{19,6,"RING_V_OFF",0x0000}, >+{20,7,"RING_OSC",0x7EF0}, >+{21,8,"RING_X",0x0160}, >+{22,9,"RING_Y",0x0000}, >+{23,255,"PULSE_ENVEL",0x2000}, >+{24,255,"PULSE_X",0x2000}, >+{25,255,"PULSE_Y",0x0000}, >+//{26,13,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower >+{26,13,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower >+{27,14,"XMIT_DIGITAL_GAIN",0x4000}, >+//{27,14,"XMIT_DIGITAL_GAIN",0x2000}, >+{28,15,"LOOP_CLOSE_TRES",0x1000}, >+{29,16,"RING_TRIP_TRES",0x3600}, >+{30,17,"COMMON_MIN_TRES",0x1000}, >+{31,18,"COMMON_MAX_TRES",0x0200}, >+{32,19,"PWR_ALARM_Q1Q2",0x07C0}, >+{33,20,"PWR_ALARM_Q3Q4",0x2600}, >+{34,21,"PWR_ALARM_Q5Q6",0x1B80}, >+{35,22,"LOOP_CLOSURE_FILTER",0x8000}, >+{36,23,"RING_TRIP_FILTER",0x0320}, >+{37,24,"TERM_LP_POLE_Q1Q2",0x008C}, >+{38,25,"TERM_LP_POLE_Q3Q4",0x0100}, >+{39,26,"TERM_LP_POLE_Q5Q6",0x0010}, >+{40,27,"CM_BIAS_RINGING",0x0C00}, >+{41,64,"DCDC_MIN_V",0x0C00}, >+{42,255,"DCDC_XTRA",0x1000}, >+{43,66,"LOOP_CLOSE_TRES_LOW",0x1000}, >+}; >+ >+ >+#include <dahdi/kernel.h> >+#include <dahdi/wctdm_user.h> >+ >+#include "fxo_modes.h" >+ >+#define NUM_FXO_REGS 60 >+ >+#define WC_MAX_IFACES 128 >+ >+#define WC_OFFSET 4 /* Offset between transmit and receive, in bytes. */ >+#define WC_SYNCFLAG 0xca1ef1ac >+ >+#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_VER 0x0 >+#define WC_CS 0x1 >+#define WC_SPICTRL 0x2 >+#define WC_SPIDATA 0x3 >+ >+#define BIT_SPI_BYHW (1 << 0) >+#define BIT_SPI_BUSY (1 << 1) // 0=can read/write spi, 1=spi working. >+#define BIT_SPI_START (1 << 2) >+ >+ >+#define BIT_LED_CLK (1 << 0) // MiaoLin add to control the led. >+#define BIT_LED_DATA (1 << 1) // MiaoLin add to control the led. >+ >+#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 DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */ >+#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */ >+#define OHT_TIMER 6000 /* How long after RING to retain OHT */ >+ >+#define FLAG_3215 (1 << 0) >+#define FLAG_A800 (1 << 7) >+ >+#define MAX_NUM_CARDS 12 >+#define NUM_CARDS 12 >+#define NUM_FLAG 4 /* number of flag channels. */ >+ >+ >+enum cid_hook_state { >+ CID_STATE_IDLE = 0, >+ CID_STATE_RING_ON, >+ CID_STATE_RING_OFF, >+ CID_STATE_WAIT_RING_FINISH >+}; >+ >+/* if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. */ >+/* #define TEST_LOG_INCOME_VOICE */ >+#define voc_buffer_size (8000*8) >+ >+ >+#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 12 >+ >+struct calregs { >+ unsigned char vals[NUM_CAL_REGS]; >+}; >+ >+enum proslic_power_warn { >+ PROSLIC_POWER_UNKNOWN = 0, >+ PROSLIC_POWER_ON, >+ PROSLIC_POWER_WARNED, >+}; >+ >+enum battery_state { >+ BATTERY_UNKNOWN = 0, >+ BATTERY_PRESENT, >+ BATTERY_LOST, >+}; >+struct wctdm { >+ struct pci_dev *dev; >+ char *variety; >+ struct dahdi_span span; >+ unsigned char ios; >+ int usecount; >+ unsigned int intcount; >+ int dead; >+ int pos; >+ int flags[MAX_NUM_CARDS]; >+ int freeregion; >+ int alt; >+ int curcard; >+ int cardflag; /* Bit-map of present cards */ >+ enum proslic_power_warn proslic_power; >+ spinlock_t lock; >+ >+ union { >+ struct fxo { >+#ifdef AUDIO_RINGCHECK >+ unsigned int pegtimer; >+ int pegcount; >+ int peg; >+ int ring; >+#else >+ int wasringing; >+ int lastrdtx; >+#endif >+ int ringdebounce; >+ int offhook; >+ unsigned int battdebounce; >+ unsigned int battalarm; >+ enum battery_state battery; >+ int lastpol; >+ int polarity; >+ int polaritydebounce; >+ } fxo; >+ struct fxs { >+ int oldrxhook; >+ int debouncehook; >+ int lastrxhook; >+ int debounce; >+ int ohttimer; >+ int idletxhookstate; /* IDLE changing hook state */ >+ int lasttxhook; >+ int palarms; >+ struct calregs calregs; >+ } fxs; >+ } mod[MAX_NUM_CARDS]; >+ >+ /* Receive hook state and debouncing */ >+ int modtype[MAX_NUM_CARDS]; >+ unsigned char reg0shadow[MAX_NUM_CARDS]; >+ unsigned char reg1shadow[MAX_NUM_CARDS]; >+ >+ unsigned long ioaddr; >+ unsigned long mem_region; /* 32 bit Region allocated to tiger320 */ >+ unsigned long mem_len; /* Length of 32 bit region */ >+ volatile unsigned long mem32; /* Virtual representation of 32 bit memory area */ >+ >+ dma_addr_t readdma; >+ dma_addr_t writedma; >+ volatile unsigned char *writechunk; /* Double-word aligned write memory */ >+ volatile unsigned char *readchunk; /* Double-word aligned read memory */ >+ /*struct dahdi_chan chans[MAX_NUM_CARDS];*/ >+ struct dahdi_chan _chans[NUM_CARDS]; >+ struct dahdi_chan *chans[NUM_CARDS]; >+ >+ >+#ifdef TEST_LOG_INCOME_VOICE >+ char * voc_buf[MAX_NUM_CARDS + NUM_FLAG]; >+ int voc_ptr[MAX_NUM_CARDS + NUM_FLAG]; >+#endif >+ int lastchan; >+ unsigned short ledstate; >+ unsigned char fwversion; >+ int max_cards; >+ char *card_name; >+ >+ char *cid_history_buf[MAX_NUM_CARDS]; >+ int cid_history_ptr[MAX_NUM_CARDS]; >+ int cid_history_clone_cnt[MAX_NUM_CARDS]; >+ enum cid_hook_state cid_state[MAX_NUM_CARDS]; >+ int cid_ring_on_time[MAX_NUM_CARDS]; >+}; >+ >+static char* A1200P_Name = "A1200P"; >+static char* A800P_Name = "A800P"; >+ >+struct wctdm_desc { >+ char *name; >+ int flags; >+}; >+ >+static struct wctdm_desc wctdme = { "OpenVox A1200P/A800P", 0 }; >+static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; >+ >+static struct wctdm *ifaces[WC_MAX_IFACES]; >+ >+static void wctdm_release(struct wctdm *wc); >+ >+static unsigned int battdebounce; >+static unsigned int battalarm; >+static unsigned int battthresh; >+static int ringdebounce = DEFAULT_RING_DEBOUNCE; >+static int fwringdetect = 0; >+static int debug = 0; >+static int robust = 0; >+static int timingonly = 0; >+static int lowpower = 0; >+static int boostringer = 0; >+static int fastringer = 0; >+static int _opermode = 0; >+static char *opermode = "FCC"; >+static int fxshonormode = 0; >+static int alawoverride = 0; >+static int fastpickup = 0; >+static int fxotxgain = 0; >+static int fxorxgain = 0; >+static int fxstxgain = 0; >+static int fxsrxgain = 0; >+/* special h/w control command */ >+static int spibyhw = 1; >+static int usememio = 1; >+static int cidbeforering = 0; >+static int cidbuflen = 3000; /* in msec, default 3000 */ >+static int cidtimeout = 6*1000; /* in msec, default 6000 */ >+static int fxofullscale = 0; /* fxo full scale tx/rx, register 30, acim */ >+static int fixedtimepolarity=0; /* time delay in ms when send polarity after rise edge of 1st ring.*/ >+ >+static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); >+ >+static void wctdm_set_led(struct wctdm* wc, int card, int onoff) >+{ >+ int i; >+ unsigned char c; >+ >+ wc->ledstate &= ~(0x01<<card); >+ wc->ledstate |= (onoff<<card); >+ c = (inb(wc->ioaddr + WC_AUXD)&~BIT_LED_CLK)|BIT_LED_DATA; >+ outb( c, wc->ioaddr + WC_AUXD); >+ for(i=MAX_NUM_CARDS-1; i>=0; i--) >+ { >+ if(wc->ledstate & (0x0001<<i)) >+ if(wc->fwversion == 0x11) >+ c &= ~BIT_LED_DATA; >+ else >+ c |= BIT_LED_DATA; >+ else >+ if(wc->fwversion == 0x11) >+ c |= BIT_LED_DATA; >+ else >+ c &= ~BIT_LED_DATA; >+ >+ outb( c, wc->ioaddr + WC_AUXD); >+ outb( c|BIT_LED_CLK, wc->ioaddr + WC_AUXD); >+ outb( (c&~BIT_LED_CLK)|BIT_LED_DATA, wc->ioaddr + WC_AUXD); >+ } >+} >+ >+ >+static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints) >+{ >+ int x, y, chan_offset, pos; >+ volatile unsigned char *txbuf; >+ >+ if (ints & /*0x01*/ 0x04) >+ /* Write is at interrupt address. Start writing from normal offset */ >+ txbuf = wc->writechunk; >+ else >+ txbuf = wc->writechunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); >+ >+ /* Calculate Transmission */ >+ dahdi_transmit(&wc->span); >+ >+ if(wc->lastchan == -1) // not in sync. >+ return; >+ >+ chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); >+ >+ for (y=0;y<DAHDI_CHUNKSIZE;y++) { >+#ifdef __BIG_ENDIAN >+ // operation pending... >+#else >+ for (x=0;x<(MAX_NUM_CARDS+NUM_FLAG);x++) { >+ pos = y * (MAX_NUM_CARDS+NUM_FLAG) + ((x + chan_offset + MAX_NUM_CARDS+NUM_FLAG - WC_OFFSET)&0x0f); >+ if(x<wc->max_cards/*MAX_NUM_CARDS*/) >+ txbuf[pos] = wc->chans[x]->writechunk[y]; >+ else >+ txbuf[pos] = 0; >+ } >+#endif >+ } >+} >+ >+ >+#ifdef AUDIO_RINGCHECK >+static inline void ring_check(struct wctdm *wc, int card) >+{ >+ int x; >+ short sample; >+ if (wc->modtype[card] != MOD_TYPE_FXO) >+ return; >+ wc->mod[card].fxo.pegtimer += DAHDI_CHUNKSIZE; >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+ /* Look for pegging to indicate ringing */ >+ sample = DAHDI_XLAW(wc->chans[card].readchunk[x], (&(wc->chans[card]))); >+ if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) { >+ if (debug > 1) printk(KERN_DEBUG "High peg!\n"); >+ if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME)) >+ wc->mod[card].fxo.pegcount++; >+ wc->mod[card].fxo.pegtimer = 0; >+ wc->mod[card].fxo.peg = 1; >+ } else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) { >+ if (debug > 1) printk(KERN_DEBUG "Low peg!\n"); >+ if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2))) >+ wc->mod[card].fxo.pegcount++; >+ wc->mod[card].fxo.pegtimer = 0; >+ wc->mod[card].fxo.peg = -1; >+ } >+ } >+ if (wc->mod[card].fxo.pegtimer > PEGTIME) { >+ /* Reset pegcount if our timer expires */ >+ wc->mod[card].fxo.pegcount = 0; >+ } >+ /* Decrement debouncer if appropriate */ >+ if (wc->mod[card].fxo.ringdebounce) >+ wc->mod[card].fxo.ringdebounce--; >+ if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) { >+ if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) { >+ /* It's ringing */ >+ if (debug) >+ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); >+ if (!wc->mod[card].fxo.offhook) >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_RING); >+ wc->mod[card].fxo.ring = 1; >+ } >+ if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) { >+ /* No more ring */ >+ if (debug) >+ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ wc->mod[card].fxo.ring = 0; >+ } >+ } >+} >+#endif >+ >+ >+static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char ints) >+{ >+ volatile unsigned char *rxbuf; >+ int x, y, chan_offset; >+ >+ >+ if (ints & 0x08/*0x04*/) >+ /* Read is at interrupt address. Valid data is available at normal offset */ >+ rxbuf = wc->readchunk; >+ else >+ rxbuf = wc->readchunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); >+ >+ for(x=0; x<4; x++) >+ if( *(int*)(rxbuf+x*4) == WC_SYNCFLAG) >+ break; >+ if(x==4) >+ { >+ printk("buffer sync misseed!\n"); >+ wc->lastchan = -1; >+ return; >+ } >+ else if(wc->lastchan != x) >+ { >+ printk("buffer re-sync occur from %d to %d\n", wc->lastchan, x); >+ wc->lastchan = x; >+ } >+ chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); >+ >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+#ifdef __BIG_ENDIAN >+ // operation pending... >+#else >+ for (y=0;y<wc->max_cards/*MAX_NUM_CARDS*/;y++) { >+ if (wc->cardflag & (1 << y)) >+ wc->chans[y]->readchunk[x] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)]; >+#ifdef TEST_LOG_INCOME_VOICE >+ wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset) & 0x0f)]; >+ wc->voc_ptr[y]++; >+ if(wc->voc_ptr[y] >= voc_buffer_size) >+ wc->voc_ptr[y] = 0; >+#endif >+ } >+#endif >+ } >+ >+ if(cidbeforering) >+ { >+ for(x=0; x<wc->max_cards; x++) >+ { >+ if (wc->modtype[wc->chans[x]->chanpos - 1] == MOD_TYPE_FXO) >+ if(wc->mod[wc->chans[x]->chanpos - 1].fxo.offhook == 0) >+ { >+ /*unsigned int *p_readchunk, *p_cid_history; >+ >+ p_readchunk = (unsigned int*)wc->chans[x].readchunk; >+ p_cid_history = (unsigned int*)(wc->cid_history_buf[x] + wc->cid_history_ptr[x]);*/ >+ >+ if(wc->cid_state[x] == CID_STATE_IDLE) /* we need copy data to the cid voice buffer */ >+ { >+ memcpy(wc->cid_history_buf[x] + wc->cid_history_ptr[x], wc->chans[x]->readchunk, DAHDI_CHUNKSIZE); >+ wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); >+ } >+ else if (wc->cid_state[x] == CID_STATE_RING_ON) >+ wc->cid_history_clone_cnt[x] = cidbuflen; >+ else if (wc->cid_state[x] == CID_STATE_RING_OFF) >+ { >+ if(wc->cid_history_clone_cnt[x]) >+ { >+ memcpy(wc->chans[x]->readchunk, wc->cid_history_buf[x] + wc->cid_history_ptr[x], DAHDI_MAX_CHUNKSIZE); >+ wc->cid_history_clone_cnt[x]--; >+ wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_MAX_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); >+ } >+ else >+ { >+ wc->cid_state[x] = CID_STATE_WAIT_RING_FINISH; >+ wc->cid_history_clone_cnt[x] = cidtimeout; /* wait 6 sec, if no ring, return to idle */ >+ } >+ } >+ else if(wc->cid_state[x] == CID_STATE_WAIT_RING_FINISH) >+ { >+ if(wc->cid_history_clone_cnt[x] > 0) >+ wc->cid_history_clone_cnt[x]--; >+ else >+ { >+ wc->cid_state[x] = CID_STATE_IDLE; >+ wc->cid_history_ptr[x] = 0; >+ wc->cid_history_clone_cnt[x] = 0; >+ } >+ } >+ } >+ } >+ } >+ >+#ifdef AUDIO_RINGCHECK >+ for (x=0;x<wc->max_cards;x++) >+ ring_check(wc, x); >+#endif >+ /* XXX We're wasting 8 taps. We should get closer :( */ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ if (wc->cardflag & (1 << x)) >+ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); >+ } >+ dahdi_receive(&wc->span); >+} >+ >+static void wctdm_stop_dma(struct wctdm *wc); >+static void wctdm_reset_tdm(struct wctdm *wc); >+static void wctdm_restart_dma(struct wctdm *wc); >+ >+ >+static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg); >+static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val); >+ >+ >+static inline void __write_8bits(struct wctdm *wc, unsigned char bits) >+{ >+ if(spibyhw == 0) >+ { >+ int x; >+ /* Drop chip select */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ for (x=0;x<8;x++) { >+ /* Send out each bit, MSB first, drop SCLK as we do so */ >+ if (bits & 0x80) >+ wc->ios |= BIT_SDI; >+ else >+ wc->ios &= ~BIT_SDI; >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Now raise SCLK high again and repeat */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ bits <<= 1; >+ } >+ /* Finally raise CS back high again */ >+ wc->ios |= BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ } >+ else >+ { >+ __wctdm_setcreg(wc, WC_SPIDATA, bits); >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); >+ while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); >+ } >+} >+ >+ >+static inline void __reset_spi(struct wctdm *wc) >+{ >+ __wctdm_setcreg(wc, WC_SPICTRL, 0); >+ >+ /* Drop chip select and clock once and raise and clock once */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios |= BIT_SDI; >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Now raise SCLK high again and repeat */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Finally raise CS back high again */ >+ wc->ios |= BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Clock again */ >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Now raise SCLK high again and repeat */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ >+ __wctdm_setcreg(wc, WC_SPICTRL, spibyhw); >+ >+} >+ >+static inline unsigned char __read_8bits(struct wctdm *wc) >+{ >+ unsigned char res=0, c; >+ int x; >+ if(spibyhw == 0) >+ { >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Drop chip select */ >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ for (x=0;x<8;x++) { >+ res <<= 1; >+ /* Get SCLK */ >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Read back the value */ >+ c = inb(wc->ioaddr + WC_AUXR); >+ if (c & BIT_SDO) >+ res |= 1; >+ /* Now raise SCLK high again */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ } >+ /* Finally raise CS back high again */ >+ wc->ios |= BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ } >+ else >+ { >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); >+ while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); >+ res = __wctdm_getcreg(wc, WC_SPIDATA); >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); >+ } >+ >+ /* And return our result */ >+ return res; >+} >+ >+static void __wctdm_setcreg_mem(struct wctdm *wc, unsigned char reg, unsigned char val) >+{ >+ unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); >+ *p = val; >+} >+ >+static unsigned char __wctdm_getcreg_mem(struct wctdm *wc, unsigned char reg) >+{ >+ unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); >+ return (*p)&0x00ff; >+} >+ >+ >+static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val) >+{ >+ if(usememio) >+ __wctdm_setcreg_mem(wc, reg, val); >+ else >+ outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); >+} >+ >+static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg) >+{ >+ if(usememio) >+ return __wctdm_getcreg_mem(wc, reg); >+ else >+ return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); >+} >+ >+static inline void __wctdm_setcard(struct wctdm *wc, int card) >+{ >+ if (wc->curcard != card) { >+ __wctdm_setcreg(wc, WC_CS, card); >+ wc->curcard = card; >+ //printk("Select card %d\n", card); >+ } >+} >+ >+static void __wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) >+{ >+ __wctdm_setcard(wc, card); >+ if (wc->modtype[card] == MOD_TYPE_FXO) { >+ __write_8bits(wc, 0x20); >+ __write_8bits(wc, reg & 0x7f); >+ } else { >+ __write_8bits(wc, reg & 0x7f); >+ } >+ __write_8bits(wc, value); >+} >+ >+static void wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->lock, flags); >+ __wctdm_setreg(wc, card, reg, value); >+ spin_unlock_irqrestore(&wc->lock, flags); >+} >+ >+static unsigned char __wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) >+{ >+ __wctdm_setcard(wc, card); >+ if (wc->modtype[card] == MOD_TYPE_FXO) { >+ __write_8bits(wc, 0x60); >+ __write_8bits(wc, reg & 0x7f); >+ } else { >+ __write_8bits(wc, reg | 0x80); >+ } >+ return __read_8bits(wc); >+} >+ >+static inline void reset_spi(struct wctdm *wc, int card) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->lock, flags); >+ __wctdm_setcard(wc, card); >+ __reset_spi(wc); >+ __reset_spi(wc); >+ spin_unlock_irqrestore(&wc->lock, flags); >+} >+ >+static unsigned char wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) >+{ >+ unsigned long flags; >+ unsigned char res; >+ spin_lock_irqsave(&wc->lock, flags); >+ res = __wctdm_getreg(wc, card, reg); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return res; >+} >+ >+static int __wait_access(struct wctdm *wc, int card) >+{ >+ unsigned char data = 0; >+ long origjiffies; >+ int count = 0; >+ >+ #define MAX 6000 /* attempts */ >+ >+ >+ origjiffies = jiffies; >+ /* Wait for indirect access */ >+ while (count++ < MAX) >+ { >+ data = __wctdm_getreg(wc, card, I_STATUS); >+ >+ if (!data) >+ return 0; >+ >+ } >+ >+ if(count > (MAX-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data); >+ >+ return 0; >+} >+ >+static unsigned char translate_3215(unsigned char address) >+{ >+ int x; >+ for (x=0;x<sizeof(indirect_regs)/sizeof(indirect_regs[0]);x++) { >+ if (indirect_regs[x].address == address) { >+ address = indirect_regs[x].altaddr; >+ break; >+ } >+ } >+ return address; >+} >+ >+static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned char address, unsigned short data) >+{ >+ unsigned long flags; >+ int res = -1; >+ /* Translate 3215 addresses */ >+ if (wc->flags[card] & FLAG_3215) { >+ address = translate_3215(address); >+ if (address == 255) >+ return 0; >+ } >+ spin_lock_irqsave(&wc->lock, flags); >+ if(!__wait_access(wc, card)) { >+ __wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); >+ __wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); >+ __wctdm_setreg(wc, card, IAA,address); >+ res = 0; >+ }; >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return res; >+} >+ >+static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) >+{ >+ unsigned long flags; >+ int res = -1; >+ char *p=NULL; >+ /* Translate 3215 addresses */ >+ if (wc->flags[card] & FLAG_3215) { >+ address = translate_3215(address); >+ if (address == 255) >+ return 0; >+ } >+ spin_lock_irqsave(&wc->lock, flags); >+ if (!__wait_access(wc, card)) { >+ __wctdm_setreg(wc, card, IAA, address); >+ if (!__wait_access(wc, card)) { >+ unsigned char data1, data2; >+ data1 = __wctdm_getreg(wc, card, IDA_LO); >+ data2 = __wctdm_getreg(wc, card, IDA_HI); >+ res = data1 | (data2 << 8); >+ } else >+ p = "Failed to wait inside\n"; >+ } else >+ p = "failed to wait\n"; >+ spin_unlock_irqrestore(&wc->lock, flags); >+ if (p) >+ printk(KERN_NOTICE "%s", p); >+ return res; >+} >+ >+static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) >+{ >+ unsigned char i; >+ >+ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) >+ { >+ if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial)) >+ return -1; >+ } >+ >+ return 0; >+} >+ >+static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) >+{ >+ int passed = 1; >+ unsigned short i, initial; >+ int j; >+ >+ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) >+ { >+ if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) { >+ printk(KERN_NOTICE "Failed to read indirect register %d\n", i); >+ return -1; >+ } >+ initial= indirect_regs[i].initial; >+ >+ if ( j != initial && (!(wc->flags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) >+ { >+ printk(KERN_NOTICE "!!!!!!! %s iREG %X = %X should be %X\n", >+ indirect_regs[i].name,indirect_regs[i].address,j,initial ); >+ passed = 0; >+ } >+ } >+ >+ if (passed) { >+ if (debug) >+ printk(KERN_DEBUG "Init Indirect Registers completed successfully.\n"); >+ } else { >+ printk(KERN_NOTICE " !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); >+ return -1; >+ } >+ return 0; >+} >+ >+static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) >+{ >+ int res; >+ /* Check loopback */ >+ res = wc->reg1shadow[card]; >+ >+ if (!res && (res != wc->mod[card].fxs.lasttxhook)) // read real state from register By wx >+ res=wctdm_getreg(wc, card, 64); >+ >+ if (!res && (res != wc->mod[card].fxs.lasttxhook)) { >+ res = wctdm_getreg(wc, card, 8); >+ if (res) { >+ printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card); >+ wctdm_init_proslic(wc, card, 1, 0, 1); >+ } else { >+ if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) { >+ printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1); >+ if (wc->mod[card].fxs.lasttxhook == 4) >+ wc->mod[card].fxs.lasttxhook = 1; >+ wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); >+ } else { >+ if (wc->mod[card].fxs.palarms == MAX_ALARMS) >+ printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); >+ } >+ } >+ } >+} >+static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) >+{ >+#define MS_PER_CHECK_HOOK 16 >+ >+#ifndef AUDIO_RINGCHECK >+ unsigned char res; >+#endif >+ signed char b; >+ int errors = 0; >+ struct fxo *fxo = &wc->mod[card].fxo; >+ >+ /* Try to track issues that plague slot one FXO's */ >+ b = wc->reg0shadow[card]; >+ if ((b & 0x2) || !(b & 0x8)) { >+ /* Not good -- don't look at anything else */ >+ if (debug) >+ printk(KERN_DEBUG "Error (%02x) on card %d!\n", b, card + 1); >+ errors++; >+ } >+ b &= 0x9b; >+ if (fxo->offhook) { >+ if (b != 0x9) >+ wctdm_setreg(wc, card, 5, 0x9); >+ } else { >+ if (b != 0x8) >+ wctdm_setreg(wc, card, 5, 0x8); >+ } >+ if (errors) >+ return; >+ if (!fxo->offhook) { >+ if(fixedtimepolarity) { >+ if ( wc->cid_state[card] == CID_STATE_RING_ON && wc->cid_ring_on_time[card]>0) >+ { >+ if(wc->cid_ring_on_time[card]>=fixedtimepolarity ) >+ { >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ wc->cid_ring_on_time[card] = -1; /* the polarity already sent */ >+ } >+ else >+ wc->cid_ring_on_time[card] += 16; >+ } >+} >+ if (fwringdetect) { >+ res = wc->reg0shadow[card] & 0x60; >+ if (fxo->ringdebounce) { >+ --fxo->ringdebounce; >+ if (res && (res != fxo->lastrdtx) && >+ (fxo->battery == BATTERY_PRESENT)) { >+ if (!fxo->wasringing) { >+ fxo->wasringing = 1; >+ if (debug) >+ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_IDLE) >+ { >+ wc->cid_state[card] = CID_STATE_RING_ON; >+ wc->cid_ring_on_time[card] = 16; /* check every 16ms */ >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ } >+ fxo->lastrdtx = res; >+ fxo->ringdebounce = 10; >+ } else if (!res) { >+ if ((fxo->ringdebounce == 0) && fxo->wasringing) { >+ fxo->wasringing = 0; >+ if (debug) >+ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_RING_ON) >+ { >+ if(fixedtimepolarity==0) >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ wc->cid_state[card] = CID_STATE_RING_OFF; >+ } >+ else >+ { >+ if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) >+ wc->cid_history_clone_cnt[card] = cidtimeout; >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ } >+ } >+ else >+ >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ } >+ } >+ } else if (res && (fxo->battery == BATTERY_PRESENT)) { >+ fxo->lastrdtx = res; >+ fxo->ringdebounce = 10; >+ } >+ } else { >+ res = wc->reg0shadow[card]; >+ if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) { >+ fxo->ringdebounce += (DAHDI_CHUNKSIZE * 16); >+ if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { >+ if (!fxo->wasringing) { >+ fxo->wasringing = 1; >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_IDLE) >+ { >+ wc->cid_state[card] = CID_STATE_RING_ON; >+ wc->cid_ring_on_time[card] = 16; /* check every 16ms */ >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ if (debug) >+ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); >+ } >+ fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; >+ } >+ } else { >+ fxo->ringdebounce -= DAHDI_CHUNKSIZE * 4; >+ if (fxo->ringdebounce <= 0) { >+ if (fxo->wasringing) { >+ fxo->wasringing = 0; >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_RING_ON) >+ { >+ if(fixedtimepolarity==0) >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ wc->cid_state[card] = CID_STATE_RING_OFF; >+ } >+ else >+ { >+ if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) >+ wc->cid_history_clone_cnt[card] = cidtimeout; >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ } >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ if (debug) >+ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); >+ } >+ fxo->ringdebounce = 0; >+ } >+ } >+ } >+ } >+ >+ b = wc->reg1shadow[card]; >+ if (abs(b) < battthresh) { >+ /* possible existing states: >+ battery lost, no debounce timer >+ battery lost, debounce timer (going to battery present) >+ battery present or unknown, no debounce timer >+ battery present or unknown, debounce timer (going to battery lost) >+ */ >+ >+ if (fxo->battery == BATTERY_LOST) { >+ if (fxo->battdebounce) { >+ /* we were going to BATTERY_PRESENT, but battery was lost again, >+ so clear the debounce timer */ >+ fxo->battdebounce = 0; >+ } >+ } else { >+ if (fxo->battdebounce) { >+ /* going to BATTERY_LOST, see if we are there yet */ >+ if (--fxo->battdebounce == 0) { >+ fxo->battery = BATTERY_LOST; >+ if (debug) >+ printk(KERN_DEBUG "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); >+#ifdef JAPAN >+ if (!wc->ohdebounce && wc->offhook) { >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_ONHOOK); >+ if (debug) >+ printk(KERN_DEBUG "Signalled On Hook\n"); >+#ifdef ZERO_BATT_RING >+ wc->onhook++; >+#endif >+ } >+#else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); >+ /* set the alarm timer, taking into account that part of its time >+ period has already passed while debouncing occurred */ >+ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; >+#endif >+ } >+ } else { >+ /* start the debounce timer to verify that battery has been lost */ >+ fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; >+ } >+ } >+ } else { >+ /* possible existing states: >+ battery lost or unknown, no debounce timer >+ battery lost or unknown, debounce timer (going to battery present) >+ battery present, no debounce timer >+ battery present, debounce timer (going to battery lost) >+ */ >+ >+ if (fxo->battery == BATTERY_PRESENT) { >+ if (fxo->battdebounce) { >+ /* we were going to BATTERY_LOST, but battery appeared again, >+ so clear the debounce timer */ >+ fxo->battdebounce = 0; >+ } >+ } else { >+ if (fxo->battdebounce) { >+ /* going to BATTERY_PRESENT, see if we are there yet */ >+ if (--fxo->battdebounce == 0) { >+ fxo->battery = BATTERY_PRESENT; >+ if (debug) >+ printk(KERN_DEBUG "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, >+ (b < 0) ? "-" : "+"); >+#ifdef ZERO_BATT_RING >+ if (wc->onhook) { >+ wc->onhook = 0; >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ if (debug) >+ printk(KERN_DEBUG "Signalled Off Hook\n"); >+ } >+#else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+#endif >+ /* set the alarm timer, taking into account that part of its time >+ period has already passed while debouncing occurred */ >+ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; >+ } >+ } else { >+ /* start the debounce timer to verify that battery has appeared */ >+ fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; >+ } >+ } >+ } >+ >+ if (fxo->lastpol >= 0) { >+ if (b < 0) { >+ fxo->lastpol = -1; >+ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; >+ } >+ } >+ if (fxo->lastpol <= 0) { >+ if (b > 0) { >+ fxo->lastpol = 1; >+ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; >+ } >+ } >+ >+ if (fxo->battalarm) { >+ if (--fxo->battalarm == 0) { >+ /* the alarm timer has expired, so update the battery alarm state >+ for this channel */ >+ dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); >+ } >+ } >+ >+ if (fxo->polaritydebounce) { >+ if (--fxo->polaritydebounce == 0) { >+ if (fxo->lastpol != fxo->polarity) { >+ if (debug) >+ printk(KERN_DEBUG "%lu Polarity reversed (%d -> %d)\n", jiffies, >+ fxo->polarity, >+ fxo->lastpol); >+ if (fxo->polarity) >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ fxo->polarity = fxo->lastpol; >+ } >+ } >+ } >+#undef MS_PER_CHECK_HOOK >+} >+ >+static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) >+{ >+ char res; >+ int hook; >+ >+ /* For some reason we have to debounce the >+ hook detector. */ >+ >+ res = wc->reg0shadow[card]; >+ hook = (res & 1); >+ if (hook != wc->mod[card].fxs.lastrxhook) { >+ /* Reset the debounce (must be multiple of 4ms) */ >+ wc->mod[card].fxs.debounce = 8 * (4 * 8); >+#if 0 >+ printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce); >+#endif >+ } else { >+ if (wc->mod[card].fxs.debounce > 0) { >+ wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE; >+#if 0 >+ printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); >+#endif >+ if (!wc->mod[card].fxs.debounce) { >+#if 0 >+ printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook); >+#endif >+ wc->mod[card].fxs.debouncehook = hook; >+ } >+ if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) { >+ /* Off hook */ >+#if 1 >+ if (debug) >+#endif >+ printk(KERN_DEBUG "opvxa1200: Card %d Going off hook\n", card); >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ if (robust) >+ wctdm_init_proslic(wc, card, 1, 0, 1); >+ wc->mod[card].fxs.oldrxhook = 1; >+ >+ } else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) { >+ /* On hook */ >+#if 1 >+ if (debug) >+#endif >+ printk(KERN_DEBUG "opvxa1200: Card %d Going on hook\n", card); >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); >+ wc->mod[card].fxs.oldrxhook = 0; >+ } >+ } >+ } >+ wc->mod[card].fxs.lastrxhook = hook; >+} >+ >+DAHDI_IRQ_HANDLER(wctdm_interrupt) >+{ >+ struct wctdm *wc = dev_id; >+ unsigned char ints; >+ int x, y, z; >+ int mode; >+ >+ ints = inb(wc->ioaddr + WC_INTSTAT); >+ >+ if (!ints) >+ return IRQ_NONE; >+ >+ outb(ints, wc->ioaddr + WC_INTSTAT); >+ >+ if (ints & 0x10) { >+ /* Stop DMA, wait for watchdog */ >+ printk(KERN_INFO "TDM PCI Master abort\n"); >+ wctdm_stop_dma(wc); >+ return IRQ_RETVAL(1); >+ } >+ >+ if (ints & 0x20) { >+ printk(KERN_INFO "PCI Target abort\n"); >+ return IRQ_RETVAL(1); >+ } >+ >+ for (x=0;x<wc->max_cards/*4*3*/;x++) { >+ if (wc->cardflag & (1 << x) && >+ (wc->modtype[x] == MOD_TYPE_FXS)) { >+ if (wc->mod[x].fxs.lasttxhook == 0x4) { >+ /* RINGing, prepare for OHT */ >+ wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; >+ if (reversepolarity) >+ wc->mod[x].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ >+ else >+ wc->mod[x].fxs.idletxhookstate = 0x2; >+ } else { >+ if (wc->mod[x].fxs.ohttimer) { >+ wc->mod[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; >+ if (!wc->mod[x].fxs.ohttimer) { >+ if (reversepolarity) >+ wc->mod[x].fxs.idletxhookstate = 0x5; /* Switch to active */ >+ else >+ wc->mod[x].fxs.idletxhookstate = 0x1; >+ if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook == 0x6)) { >+ /* Apply the change if appropriate */ >+ if (reversepolarity) >+ wc->mod[x].fxs.lasttxhook = 0x5; >+ else >+ wc->mod[x].fxs.lasttxhook = 0x1; >+ wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); >+ } >+ } >+ } >+ } >+ } >+ } >+ >+ if (ints & 0x0f) { >+ wc->intcount++; >+ z = wc->intcount & 0x3; >+ mode = wc->intcount & 0xc; >+ for(y=0; y<wc->max_cards/4/*3*/; y++) >+ { >+ x = z + y*4; >+ if (wc->cardflag & (1 << x ) ) >+ { >+ switch(mode) >+ { >+ case 0: >+ /* Rest */ >+ break; >+ case 4: >+ /* Read first shadow reg */ >+ if (wc->modtype[x] == MOD_TYPE_FXS) >+ wc->reg0shadow[x] = wctdm_getreg(wc, x, 68); >+ else if (wc->modtype[x] == MOD_TYPE_FXO) >+ wc->reg0shadow[x] = wctdm_getreg(wc, x, 5); >+ break; >+ case 8: >+ /* Read second shadow reg */ >+ if (wc->modtype[x] == MOD_TYPE_FXS) >+ wc->reg1shadow[x] = wctdm_getreg(wc, x, 64); >+ else if (wc->modtype[x] == MOD_TYPE_FXO) >+ wc->reg1shadow[x] = wctdm_getreg(wc, x, 29); >+ break; >+ case 12: >+ /* Perform processing */ >+ if (wc->modtype[x] == MOD_TYPE_FXS) { >+ wctdm_proslic_check_hook(wc, x); >+ if (!(wc->intcount & 0xf0)) >+ wctdm_proslic_recheck_sanity(wc, x); >+ } else if (wc->modtype[x] == MOD_TYPE_FXO) { >+ wctdm_voicedaa_check_hook(wc, x); >+ } >+ break; >+ } >+ } >+ } >+ if (!(wc->intcount % 10000)) { >+ /* Accept an alarm once per 10 seconds */ >+ for (x=0;x<wc->max_cards/*4*3*/;x++) >+ if (wc->modtype[x] == MOD_TYPE_FXS) { >+ if (wc->mod[x].fxs.palarms) >+ wc->mod[x].fxs.palarms--; >+ } >+ } >+ wctdm_receiveprep(wc, ints); >+ wctdm_transmitprep(wc, ints); >+ } >+ >+ return IRQ_RETVAL(1); >+ >+} >+ >+static int wctdm_voicedaa_insane(struct wctdm *wc, int card) >+{ >+ int blah; >+ blah = wctdm_getreg(wc, card, 2); >+ if (blah != 0x3) >+ return -2; >+ blah = wctdm_getreg(wc, card, 11); >+ if (debug) >+ printk(KERN_DEBUG "VoiceDAA System: %02x\n", blah & 0xf); >+ return 0; >+} >+ >+static int wctdm_proslic_insane(struct wctdm *wc, int card) >+{ >+ int blah,insane_report; >+ insane_report=0; >+ >+ blah = wctdm_getreg(wc, card, 0); >+ if (debug) >+ printk(KERN_DEBUG "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); >+ >+#if 0 >+ if ((blah & 0x30) >> 4) { >+ printk(KERN_DEBUG "ProSLIC on module %d is not a 3210.\n", card); >+ return -1; >+ } >+#endif >+ if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) { >+ /* SLIC not loaded */ >+ return -1; >+ } >+ if ((blah & 0xf) < 2) { >+ printk(KERN_NOTICE "ProSLIC 3210 version %d is too old\n", blah & 0xf); >+ return -1; >+ } >+ if (wctdm_getreg(wc, card, 1) & 0x80) >+ /* ProSLIC 3215, not a 3210 */ >+ wc->flags[card] |= FLAG_3215; >+ >+ blah = wctdm_getreg(wc, card, 8); >+ if (blah != 0x2) { >+ printk(KERN_NOTICE "ProSLIC on module %d insane (1) %d should be 2\n", card, blah); >+ return -1; >+ } else if ( insane_report) >+ printk(KERN_NOTICE "ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); >+ >+ blah = wctdm_getreg(wc, card, 64); >+ if (blah != 0x0) { >+ printk(KERN_NOTICE "ProSLIC on module %d insane (2)\n", card); >+ return -1; >+ } else if ( insane_report) >+ printk(KERN_NOTICE "ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); >+ >+ blah = wctdm_getreg(wc, card, 11); >+ if (blah != 0x33) { >+ printk(KERN_NOTICE "ProSLIC on module %d insane (3)\n", card); >+ return -1; >+ } else if ( insane_report) >+ printk(KERN_NOTICE "ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah); >+ >+ /* Just be sure it's setup right. */ >+ wctdm_setreg(wc, card, 30, 0); >+ >+ if (debug) >+ printk(KERN_DEBUG "ProSLIC on module %d seems sane.\n", card); >+ return 0; >+} >+ >+static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) >+{ >+ unsigned long origjiffies; >+ unsigned char vbat; >+ >+ /* Turn off linefeed */ >+ wctdm_setreg(wc, card, 64, 0); >+ >+ /* Power down */ >+ wctdm_setreg(wc, card, 14, 0x10); >+ >+ /* Wait for one second */ >+ origjiffies = jiffies; >+ >+ while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { >+ if ((jiffies - origjiffies) >= (HZ/2)) >+ break;; >+ } >+ >+ if (vbat < 0x06) { >+ printk(KERN_NOTICE "Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card, >+ 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ)); >+ return -1; >+ } else if (debug) { >+ printk(KERN_NOTICE "Post-leakage voltage: %d volts\n", 376 * vbat / 1000); >+ } >+ return 0; >+} >+ >+static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) >+{ >+ unsigned char vbat; >+ unsigned long origjiffies; >+ int lim; >+ >+ /* Set period of DC-DC converter to 1/64 khz */ >+ wctdm_setreg(wc, card, 92, 0xff /* was 0xff */); >+ >+ /* Wait for VBat to powerup */ >+ origjiffies = jiffies; >+ >+ /* Disable powerdown */ >+ wctdm_setreg(wc, card, 14, 0); >+ >+ /* If fast, don't bother checking anymore */ >+ if (fast) >+ return 0; >+ >+ while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { >+ /* Wait no more than 500ms */ >+ if ((jiffies - origjiffies) > HZ/2) { >+ break; >+ } >+ } >+ >+ if (vbat < 0xc0) { >+ if (wc->proslic_power == PROSLIC_POWER_UNKNOWN) >+ printk(KERN_NOTICE "ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A1200P??\n", >+ card, (int)(((jiffies - origjiffies) * 1000 / HZ)), >+ vbat * 375); >+ wc->proslic_power = PROSLIC_POWER_WARNED; >+ return -1; >+ } else if (debug) { >+ printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", >+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); >+ } >+ wc->proslic_power = PROSLIC_POWER_ON; >+ >+ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ >+ /* If out of range, just set it to the default value */ >+ lim = (loopcurrent - 20) / 3; >+ if ( loopcurrent > 41 ) { >+ lim = 0; >+ if (debug) >+ printk(KERN_DEBUG "Loop current out of range! Setting to default 20mA!\n"); >+ } >+ else if (debug) >+ printk(KERN_DEBUG "Loop current set to %dmA!\n",(lim*3)+20); >+ wctdm_setreg(wc,card,LOOP_I_LIMIT,lim); >+ >+ /* Engage DC-DC converter */ >+ wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); >+#if 0 >+ origjiffies = jiffies; >+ while(0x80 & wctdm_getreg(wc, card, 93)) { >+ if ((jiffies - origjiffies) > 2 * HZ) { >+ printk(KERN_DEBUG "Timeout waiting for DC-DC calibration on module %d\n", card); >+ return -1; >+ } >+ } >+ >+#if 0 >+ /* Wait a full two seconds */ >+ while((jiffies - origjiffies) < 2 * HZ); >+ >+ /* Just check to be sure */ >+ vbat = wctdm_getreg(wc, card, 82); >+ printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", >+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); >+#endif >+#endif >+ return 0; >+ >+} >+ >+static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card){ >+ unsigned long origjiffies; >+ unsigned char i; >+ >+ wctdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21 >+ wctdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21 >+ wctdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21 >+ wctdm_setreg(wc, card, 64, 0);//(0) >+ >+ wctdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. >+ wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM >+ >+ origjiffies=jiffies; >+ while( wctdm_getreg(wc,card,96)!=0 ){ >+ if((jiffies-origjiffies)>80) >+ return -1; >+ } >+//Initialized DR 98 and 99 to get consistant results. >+// 98 and 99 are the results registers and the search should have same intial conditions. >+ >+/*******************************The following is the manual gain mismatch calibration****************************/ >+/*******************************This is also available as a function *******************************************/ >+ // Delay 10ms >+ origjiffies=jiffies; >+ while((jiffies-origjiffies)<1); >+ wctdm_proslic_setreg_indirect(wc, card, 88,0); >+ wctdm_proslic_setreg_indirect(wc,card,89,0); >+ wctdm_proslic_setreg_indirect(wc,card,90,0); >+ wctdm_proslic_setreg_indirect(wc,card,91,0); >+ wctdm_proslic_setreg_indirect(wc,card,92,0); >+ wctdm_proslic_setreg_indirect(wc,card,93,0); >+ >+ wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time >+ wctdm_setreg(wc, card, 99,0x10); >+ >+ for ( i=0x1f; i>0; i--) >+ { >+ wctdm_setreg(wc, card, 98,i); >+ origjiffies=jiffies; >+ while((jiffies-origjiffies)<4); >+ if((wctdm_getreg(wc,card,88)) == 0) >+ break; >+ } // for >+ >+ for ( i=0x1f; i>0; i--) >+ { >+ wctdm_setreg(wc, card, 99,i); >+ origjiffies=jiffies; >+ while((jiffies-origjiffies)<4); >+ if((wctdm_getreg(wc,card,89)) == 0) >+ break; >+ }//for >+ >+/*******************************The preceding is the manual gain mismatch calibration****************************/ >+/**********************************The following is the longitudinal Balance Cal***********************************/ >+ wctdm_setreg(wc,card,64,1); >+ while((jiffies-origjiffies)<10); // Sleep 100? >+ >+ wctdm_setreg(wc, card, 64, 0); >+ wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal >+ wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration >+ wctdm_setreg(wc, card, 96,0x40); >+ >+ wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */ >+ >+ wctdm_setreg(wc, card, 21, 0xFF); >+ wctdm_setreg(wc, card, 22, 0xFF); >+ wctdm_setreg(wc, card, 23, 0xFF); >+ >+ /**The preceding is the longitudinal Balance Cal***/ >+ return(0); >+ >+} >+#if 1 >+static int wctdm_proslic_calibrate(struct wctdm *wc, int card) >+{ >+ unsigned long origjiffies; >+ int x; >+ /* Perform all calibrations */ >+ wctdm_setreg(wc, card, 97, 0x1f); >+ >+ /* Begin, no speedup */ >+ wctdm_setreg(wc, card, 96, 0x5f); >+ >+ /* Wait for it to finish */ >+ origjiffies = jiffies; >+ while(wctdm_getreg(wc, card, 96)) { >+ if ((jiffies - origjiffies) > 2 * HZ) { >+ printk(KERN_NOTICE "Timeout waiting for calibration of module %d\n", card); >+ return -1; >+ } >+ } >+ >+ if (debug) { >+ /* Print calibration parameters */ >+ printk(KERN_DEBUG "Calibration Vector Regs 98 - 107: \n"); >+ for (x=98;x<108;x++) { >+ printk(KERN_DEBUG "%d: %02x\n", x, wctdm_getreg(wc, card, x)); >+ } >+ } >+ return 0; >+} >+#endif >+ >+static void wait_just_a_bit(int foo) >+{ >+ long newjiffies; >+ newjiffies = jiffies + foo; >+ while(jiffies < newjiffies); >+} >+ >+/********************************************************************* >+ * Set the hwgain on the analog modules >+ * >+ * card = the card position for this module (0-23) >+ * gain = gain in dB x10 (e.g. -3.5dB would be gain=-35) >+ * tx = (0 for rx; 1 for tx) >+ * >+ *******************************************************************/ >+static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) >+{ >+ if (!(wc->modtype[card] == MOD_TYPE_FXO)) { >+ printk(KERN_NOTICE "Cannot adjust gain. Unsupported module type!\n"); >+ return -1; >+ } >+ if (tx) { >+ if (debug) >+ printk(KERN_DEBUG "setting FXO tx gain for card=%d to %d\n", card, gain); >+ if (gain >= -150 && gain <= 0) { >+ wctdm_setreg(wc, card, 38, 16 + (gain/-10)); >+ wctdm_setreg(wc, card, 40, 16 + (-gain%10)); >+ } else if (gain <= 120 && gain > 0) { >+ wctdm_setreg(wc, card, 38, gain/10); >+ wctdm_setreg(wc, card, 40, (gain%10)); >+ } else { >+ printk(KERN_INFO "FXO tx gain is out of range (%d)\n", gain); >+ return -1; >+ } >+ } else { /* rx */ >+ if (debug) >+ printk(KERN_DEBUG "setting FXO rx gain for card=%d to %d\n", card, gain); >+ if (gain >= -150 && gain <= 0) { >+ wctdm_setreg(wc, card, 39, 16+ (gain/-10)); >+ wctdm_setreg(wc, card, 41, 16 + (-gain%10)); >+ } else if (gain <= 120 && gain > 0) { >+ wctdm_setreg(wc, card, 39, gain/10); >+ wctdm_setreg(wc, card, 41, (gain%10)); >+ } else { >+ printk(KERN_INFO "FXO rx gain is out of range (%d)\n", gain); >+ return -1; >+ } >+ } >+ >+ return 0; >+} >+ >+static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) >+{ >+ unsigned char reg16=0, reg26=0, reg30=0, reg31=0; >+ long newjiffies; >+ wc->modtype[card] = MOD_TYPE_FXO; >+ /* Sanity check the ProSLIC */ >+ reset_spi(wc, card); >+ if (!sane && wctdm_voicedaa_insane(wc, card)) >+ return -2; >+ >+ /* Software reset */ >+ wctdm_setreg(wc, card, 1, 0x80); >+ >+ /* Wait just a bit */ >+ wait_just_a_bit(HZ/10); >+ >+ /* Enable PCM, ulaw */ >+ if (alawoverride) >+ wctdm_setreg(wc, card, 33, 0x20); >+ else >+ wctdm_setreg(wc, card, 33, 0x28); >+ >+ /* Set On-hook speed, Ringer impedence, and ringer threshold */ >+ reg16 |= (fxo_modes[_opermode].ohs << 6); >+ reg16 |= (fxo_modes[_opermode].rz << 1); >+ reg16 |= (fxo_modes[_opermode].rt); >+ wctdm_setreg(wc, card, 16, reg16); >+ >+ if(fwringdetect) { >+ /* Enable ring detector full-wave rectifier mode */ >+ wctdm_setreg(wc, card, 18, 2); >+ wctdm_setreg(wc, card, 24, 0); >+ } else { >+ /* Set to the device defaults */ >+ wctdm_setreg(wc, card, 18, 0); >+ wctdm_setreg(wc, card, 24, 0x19); >+ } >+ >+ /* Set DC Termination: >+ Tip/Ring voltage adjust, minimum operational current, current limitation */ >+ reg26 |= (fxo_modes[_opermode].dcv << 6); >+ reg26 |= (fxo_modes[_opermode].mini << 4); >+ reg26 |= (fxo_modes[_opermode].ilim << 1); >+ wctdm_setreg(wc, card, 26, reg26); >+ >+ /* Set AC Impedence */ >+ reg30 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim); >+ wctdm_setreg(wc, card, 30, reg30); >+ >+ /* Misc. DAA parameters */ >+ if (fastpickup) >+ reg31 = 0xb3; >+ else >+ reg31 = 0xa3; >+ >+ reg31 |= (fxo_modes[_opermode].ohs2 << 3); >+ wctdm_setreg(wc, card, 31, reg31); >+ >+ /* Set Transmit/Receive timeslot */ >+ //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); >+ wctdm_setreg(wc, card, 34, (3-(card%4)) * 8 + (card/4) * 64); >+ wctdm_setreg(wc, card, 35, 0x00); >+ wctdm_setreg(wc, card, 36, (3-(card%4)) * 8 + (card/4) * 64); >+ wctdm_setreg(wc, card, 37, 0x00); >+ >+ /* Enable ISO-Cap */ >+ wctdm_setreg(wc, card, 6, 0x00); >+ >+ if (fastpickup) >+ wctdm_setreg(wc, card, 17, wctdm_getreg(wc, card, 17) | 0x20); >+ >+ /* Wait 1000ms for ISO-cap to come up */ >+ newjiffies = jiffies; >+ newjiffies += 2 * HZ; >+ while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) >+ wait_just_a_bit(HZ/10); >+ >+ if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { >+ printk(KERN_NOTICE "VoiceDAA did not bring up ISO link properly!\n"); >+ return -1; >+ } >+ if (debug) >+ printk(KERN_DEBUG "ISO-Cap is now up, line side: %02x rev %02x\n", >+ wctdm_getreg(wc, card, 11) >> 4, >+ (wctdm_getreg(wc, card, 13) >> 2) & 0xf); >+ /* Enable on-hook line monitor */ >+ wctdm_setreg(wc, card, 5, 0x08); >+ >+ /* Take values for fxotxgain and fxorxgain and apply them to module */ >+ wctdm_set_hwgain(wc, card, fxotxgain, 1); >+ wctdm_set_hwgain(wc, card, fxorxgain, 0); >+ >+ /* NZ -- crank the tx gain up by 7 dB */ >+ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { >+ printk(KERN_INFO "Adjusting gain\n"); >+ wctdm_set_hwgain(wc, card, 7, 1); >+ } >+ >+ if(debug) >+ printk(KERN_DEBUG "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16)?-(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16)? -(wctdm_getreg(wc, card, 40) - 16):wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16)? -(wctdm_getreg(wc, card, 39) - 16) : wctdm_getreg(wc, card, 39),(wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16):wctdm_getreg(wc, card, 41)); >+ >+ return 0; >+ >+} >+ >+static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) >+{ >+ >+ unsigned short tmp[5]; >+ unsigned char r19, r9; >+ int x; >+ int fxsmode=0; >+ >+ /* Sanity check the ProSLIC */ >+ if (!sane && wctdm_proslic_insane(wc, card)) >+ return -2; >+ >+ /* By default, don't send on hook */ >+ if (reversepolarity) >+ wc->mod[card].fxs.idletxhookstate = 5; >+ else >+ wc->mod[card].fxs.idletxhookstate = 1; >+ >+ if (sane) { >+ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ >+ wctdm_setreg(wc, card, 14, 0x10); >+ } >+ >+ if (wctdm_proslic_init_indirect_regs(wc, card)) { >+ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card); >+ return -1; >+ } >+ >+ /* Clear scratch pad area */ >+ wctdm_proslic_setreg_indirect(wc, card, 97,0); >+ >+ /* Clear digital loopback */ >+ wctdm_setreg(wc, card, 8, 0); >+ >+ /* Revision C optimization */ >+ wctdm_setreg(wc, card, 108, 0xeb); >+ >+ /* Disable automatic VBat switching for safety to prevent >+ Q7 from accidently turning on and burning out. */ >+ wctdm_setreg(wc, card, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads >+ change this to 0x17 */ >+ >+ /* Turn off Q7 */ >+ wctdm_setreg(wc, card, 66, 1); >+ >+ /* Flush ProSLIC digital filters by setting to clear, while >+ saving old values */ >+ for (x=0;x<5;x++) { >+ tmp[x] = wctdm_proslic_getreg_indirect(wc, card, x + 35); >+ wctdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000); >+ } >+ >+ /* Power up the DC-DC converter */ >+ if (wctdm_powerup_proslic(wc, card, fast)) { >+ printk(KERN_NOTICE "Unable to do INITIAL ProSLIC powerup on module %d\n", card); >+ return -1; >+ } >+ >+ if (!fast) { >+ >+ /* Check for power leaks */ >+ if (wctdm_proslic_powerleak_test(wc, card)) { >+ printk(KERN_NOTICE "ProSLIC module %d failed leakage test. Check for short circuit\n", card); >+ } >+ /* Power up again */ >+ if (wctdm_powerup_proslic(wc, card, fast)) { >+ printk(KERN_NOTICE "Unable to do FINAL ProSLIC powerup on module %d\n", card); >+ return -1; >+ } >+#ifndef NO_CALIBRATION >+ /* Perform calibration */ >+ if(manual) { >+ if (wctdm_proslic_manual_calibrate(wc, card)) { >+ //printk(KERN_NOTICE "Proslic failed on Manual Calibration\n"); >+ if (wctdm_proslic_manual_calibrate(wc, card)) { >+ printk(KERN_NOTICE "Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); >+ return -1; >+ } >+ printk(KERN_NOTICE "Proslic Passed Manual Calibration on Second Attempt\n"); >+ } >+ } >+ else { >+ if(wctdm_proslic_calibrate(wc, card)) { >+ //printk(KERN_NOTICE "ProSlic died on Auto Calibration.\n"); >+ if (wctdm_proslic_calibrate(wc, card)) { >+ printk(KERN_NOTICE "Proslic Failed on Second Attempt to Auto Calibrate\n"); >+ return -1; >+ } >+ printk(KERN_NOTICE "Proslic Passed Auto Calibration on Second Attempt\n"); >+ } >+ } >+ /* Perform DC-DC calibration */ >+ wctdm_setreg(wc, card, 93, 0x99); >+ r19 = wctdm_getreg(wc, card, 107); >+ if ((r19 < 0x2) || (r19 > 0xd)) { >+ printk(KERN_NOTICE "DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); >+ wctdm_setreg(wc, card, 107, 0x8); >+ } >+ >+ /* Save calibration vectors */ >+ for (x=0;x<NUM_CAL_REGS;x++) >+ wc->mod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); >+#endif >+ >+ } else { >+ /* Restore calibration registers */ >+ for (x=0;x<NUM_CAL_REGS;x++) >+ wctdm_setreg(wc, card, 96 + x, wc->mod[card].fxs.calregs.vals[x]); >+ } >+ /* Calibration complete, restore original values */ >+ for (x=0;x<5;x++) { >+ wctdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]); >+ } >+ >+ if (wctdm_proslic_verify_indirect_regs(wc, card)) { >+ printk(KERN_INFO "Indirect Registers failed verification.\n"); >+ return -1; >+ } >+ >+ >+#if 0 >+ /* Disable Auto Power Alarm Detect and other "features" */ >+ wctdm_setreg(wc, card, 67, 0x0e); >+ blah = wctdm_getreg(wc, card, 67); >+#endif >+ >+#if 0 >+ if (wctdm_proslic_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix >+ printk(KERN_INFO "ProSlic IndirectReg Died.\n"); >+ return -1; >+ } >+#endif >+ >+ if (alawoverride) >+ wctdm_setreg(wc, card, 1, 0x20); >+ else >+ wctdm_setreg(wc, card, 1, 0x28); >+ // U-Law 8-bit interface >+ wctdm_setreg(wc, card, 2, (3-(card%4)) * 8 + (card/4) * 64); // Tx Start count low byte 0 >+ wctdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0 >+ wctdm_setreg(wc, card, 4, (3-(card%4)) * 8 + (card/4) * 64); // Rx Start count low byte 0 >+ wctdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0 >+ wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt >+ wctdm_setreg(wc, card, 19, 0xff); >+ wctdm_setreg(wc, card, 20, 0xff); >+ wctdm_setreg(wc, card, 73, 0x04); >+ if (fxshonormode) { >+ fxsmode = acim2tiss[fxo_modes[_opermode].acim]; >+ wctdm_setreg(wc, card, 10, 0x08 | fxsmode); >+ if (fxo_modes[_opermode].ring_osc) >+ wctdm_proslic_setreg_indirect(wc, card, 20, fxo_modes[_opermode].ring_osc); >+ if (fxo_modes[_opermode].ring_x) >+ wctdm_proslic_setreg_indirect(wc, card, 21, fxo_modes[_opermode].ring_x); >+ } >+ if (lowpower) >+ wctdm_setreg(wc, card, 72, 0x10); >+ >+#if 0 >+ wctdm_setreg(wc, card, 21, 0x00); // enable interrupt >+ wctdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt >+ wctdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt >+#endif >+ >+#if 0 >+ /* Enable loopback */ >+ wctdm_setreg(wc, card, 8, 0x2); >+ wctdm_setreg(wc, card, 14, 0x0); >+ wctdm_setreg(wc, card, 64, 0x0); >+ wctdm_setreg(wc, card, 1, 0x08); >+#endif >+ >+ if (fastringer) { >+ /* Speed up Ringer */ >+ wctdm_proslic_setreg_indirect(wc, card, 20, 0x7e6d); >+ wctdm_proslic_setreg_indirect(wc, card, 21, 0x01b9); >+ /* Beef up Ringing voltage to 89V */ >+ if (boostringer) { >+ wctdm_setreg(wc, card, 74, 0x3f); >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x247)) >+ return -1; >+ printk(KERN_INFO "Boosting fast ringer on slot %d (89V peak)\n", card + 1); >+ } else if (lowpower) { >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x14b)) >+ return -1; >+ printk(KERN_INFO "Reducing fast ring power on slot %d (50V peak)\n", card + 1); >+ } else >+ printk(KERN_INFO "Speeding up ringer on slot %d (25Hz)\n", card + 1); >+ } else { >+ /* Beef up Ringing voltage to 89V */ >+ if (boostringer) { >+ wctdm_setreg(wc, card, 74, 0x3f); >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) >+ return -1; >+ printk(KERN_INFO "Boosting ringer on slot %d (89V peak)\n", card + 1); >+ } else if (lowpower) { >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) >+ return -1; >+ printk(KERN_INFO "Reducing ring power on slot %d (50V peak)\n", card + 1); >+ } >+ } >+ >+ if(fxstxgain || fxsrxgain) { >+ r9 = wctdm_getreg(wc, card, 9); >+ switch (fxstxgain) { >+ >+ case 35: >+ r9+=8; >+ break; >+ case -35: >+ r9+=4; >+ break; >+ case 0: >+ break; >+ } >+ >+ switch (fxsrxgain) { >+ >+ case 35: >+ r9+=2; >+ break; >+ case -35: >+ r9+=1; >+ break; >+ case 0: >+ break; >+ } >+ wctdm_setreg(wc,card,9,r9); >+ } >+ >+ if(debug) >+ printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); >+ >+ wctdm_setreg(wc, card, 64, 0x01); >+ return 0; >+} >+ >+ >+static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) >+{ >+ struct wctdm_stats stats; >+ struct wctdm_regs regs; >+ struct wctdm_regop regop; >+ struct wctdm_echo_coefs echoregs; >+ struct dahdi_hwgain hwgain; >+ struct wctdm *wc = chan->pvt; >+ int x; >+ switch (cmd) { >+ case DAHDI_ONHOOKTRANSFER: >+ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) >+ return -EINVAL; >+ if (get_user(x, (__user int *)data)) >+ return -EFAULT; >+ wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; >+ if (reversepolarity) >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ >+ else >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2; >+ if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { >+ /* Apply the change if appropriate */ >+ if (reversepolarity) >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6; >+ else >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2; >+ wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); >+ } >+ break; >+ case DAHDI_SETPOLARITY: >+ if (get_user(x, (__user int *)data)) >+ return -EFAULT; >+ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) >+ return -EINVAL; >+ /* Can't change polarity while ringing or when open */ >+ if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || >+ (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) >+ return -EINVAL; >+ >+ if ((x && !reversepolarity) || (!x && reversepolarity)) >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; >+ else >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; >+ wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); >+ break; >+ case WCTDM_GET_STATS: >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { >+ stats.tipvolt = wctdm_getreg(wc, chan->chanpos - 1, 80) * -376; >+ stats.ringvolt = wctdm_getreg(wc, chan->chanpos - 1, 81) * -376; >+ stats.batvolt = wctdm_getreg(wc, chan->chanpos - 1, 82) * -376; >+ } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { >+ stats.tipvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; >+ stats.ringvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; >+ stats.batvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; >+ } else >+ return -EINVAL; >+ if (copy_to_user((__user void *)data, &stats, sizeof(stats))) >+ return -EFAULT; >+ break; >+ case WCTDM_GET_REGS: >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { >+ for (x=0;x<NUM_INDIRECT_REGS;x++) >+ regs.indirect[x] = wctdm_proslic_getreg_indirect(wc, chan->chanpos -1, x); >+ for (x=0;x<NUM_REGS;x++) >+ regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); >+ } else { >+ memset(®s, 0, sizeof(regs)); >+ for (x=0;x<NUM_FXO_REGS;x++) >+ regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); >+ } >+ if (copy_to_user((__user void *)data, ®s, sizeof(regs))) >+ return -EFAULT; >+ break; >+ case WCTDM_SET_REG: >+ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) >+ return -EFAULT; >+ if (regop.indirect) { >+ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) >+ return -EINVAL; >+ printk(KERN_INFO "Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos); >+ wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); >+ } else { >+ regop.val &= 0xff; >+ printk(KERN_INFO "Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); >+ wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); >+ } >+ break; >+ case WCTDM_SET_ECHOTUNE: >+ printk(KERN_INFO "-- Setting echo registers: \n"); >+ if (copy_from_user(&echoregs, (__user void *)data, sizeof(echoregs))) >+ return -EFAULT; >+ >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { >+ /* Set the ACIM register */ >+ wctdm_setreg(wc, chan->chanpos - 1, 30, (fxofullscale==1) ? (echoregs.acim|0x10) : echoregs.acim); >+ >+ /* Set the digital echo canceller registers */ >+ wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1); >+ wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2); >+ wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3); >+ wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4); >+ wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5); >+ wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6); >+ wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7); >+ wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8); >+ >+ printk(KERN_INFO "-- Set echo registers successfully\n"); >+ >+ break; >+ } else { >+ return -EINVAL; >+ >+ } >+ break; >+ case DAHDI_SET_HWGAIN: >+ if (copy_from_user(&hwgain, (__user void *) data, sizeof(hwgain))) >+ return -EFAULT; >+ >+ wctdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx); >+ >+ if (debug) >+ printk(KERN_DEBUG "Setting hwgain on channel %d to %d for %s direction\n", >+ chan->chanpos-1, hwgain.newgain, hwgain.tx ? "tx" : "rx"); >+ break; >+ default: >+ return -ENOTTY; >+ } >+ return 0; >+ >+} >+ >+static int wctdm_open(struct dahdi_chan *chan) >+{ >+ struct wctdm *wc = chan->pvt; >+ if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) >+ return -ENODEV; >+ if (wc->dead) >+ return -ENODEV; >+ wc->usecount++; >+ >+ /*MOD_INC_USE_COUNT; */ >+ try_module_get(THIS_MODULE); >+ return 0; >+} >+ >+static int wctdm_watchdog(struct dahdi_span *span, int event) >+{ >+ printk(KERN_INFO "opvxa1200: Restarting DMA\n"); >+ wctdm_restart_dma(span->pvt); >+ return 0; >+} >+ >+static int wctdm_close(struct dahdi_chan *chan) >+{ >+ struct wctdm *wc = chan->pvt; >+ wc->usecount--; >+ >+ /*MOD_DEC_USE_COUNT;*/ >+ module_put(THIS_MODULE); >+ >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { >+ if (reversepolarity) >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5; >+ else >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1; >+ } >+ /* If we're dead, release us now */ >+ if (!wc->usecount && wc->dead) >+ wctdm_release(wc); >+ return 0; >+} >+ >+static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) >+{ >+ struct wctdm *wc = chan->pvt; >+ int reg=0; >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { >+ /* XXX Enable hooksig for FXO XXX */ >+ switch(txsig) { >+ case DAHDI_TXSIG_START: >+ case DAHDI_TXSIG_OFFHOOK: >+ wc->mod[chan->chanpos - 1].fxo.offhook = 1; >+ wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); >+ if(cidbeforering) >+ { >+ wc->cid_state[chan->chanpos - 1] = CID_STATE_IDLE; >+ wc->cid_history_clone_cnt[chan->chanpos - 1] = 0; >+ wc->cid_history_ptr[chan->chanpos - 1] = 0; >+ memset(wc->cid_history_buf[chan->chanpos - 1], DAHDI_LIN2X(0, chan), cidbuflen * DAHDI_MAX_CHUNKSIZE); >+ } >+ break; >+ case DAHDI_TXSIG_ONHOOK: >+ wc->mod[chan->chanpos - 1].fxo.offhook = 0; >+ wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); >+ break; >+ default: >+ printk(KERN_NOTICE "wcfxo: Can't set tx state to %d\n", txsig); >+ } >+ } else { >+ switch(txsig) { >+ case DAHDI_TXSIG_ONHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ case DAHDI_SIG_FXOKS: >+ case DAHDI_SIG_FXOLS: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; >+ break; >+ case DAHDI_SIG_FXOGS: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; >+ break; >+ } >+ break; >+ case DAHDI_TXSIG_OFFHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 5; >+ break; >+ default: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; >+ break; >+ } >+ break; >+ case DAHDI_TXSIG_START: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; >+ break; >+ case DAHDI_TXSIG_KEWL: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; >+ break; >+ default: >+ printk(KERN_NOTICE "opvxa1200: Can't set tx state to %d\n", txsig); >+ } >+ if (debug) >+ printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, reg); >+ >+#if 1 >+ wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); >+#endif >+ } >+ return 0; >+} >+ >+static int wctdm_initialize(struct wctdm *wc) >+{ >+ int x; >+ >+ /* Dahdi stuff */ >+ sprintf(wc->span.name, "OPVXA1200/%d", wc->pos); >+ snprintf(wc->span.desc, sizeof(wc->span.desc)-1, "%s Board %d", wc->variety, wc->pos + 1); >+ snprintf(wc->span.location, sizeof(wc->span.location) - 1, >+ "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); >+ wc->span.manufacturer = "OpenVox"; >+ dahdi_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype)); >+ if (alawoverride) { >+ printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); >+ wc->span.deflaw = DAHDI_LAW_ALAW; >+ } else >+ wc->span.deflaw = DAHDI_LAW_MULAW; >+ >+ x = __wctdm_getcreg(wc, WC_VER); >+ wc->fwversion = x; >+ if( x & FLAG_A800) >+ { >+ wc->card_name = A800P_Name; >+ wc->max_cards = 8; >+ } >+ else >+ { >+ wc->card_name = A1200P_Name; >+ wc->max_cards = 12; >+ } >+ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ sprintf(wc->chans[x]->name, "OPVXA1200/%d/%d", wc->pos, x); >+ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; >+ wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; >+ wc->chans[x]->chanpos = x+1; >+ wc->chans[x]->pvt = wc; >+ } >+ wc->span.chans = wc->chans; >+ wc->span.channels = wc->max_cards; /*MAX_NUM_CARDS;*/ >+ wc->span.hooksig = wctdm_hooksig; >+ wc->span.irq = wc->dev->irq; >+ wc->span.open = wctdm_open; >+ wc->span.close = wctdm_close; >+ wc->span.flags = DAHDI_FLAG_RBS; >+ wc->span.ioctl = wctdm_ioctl; >+ wc->span.watchdog = wctdm_watchdog; >+ init_waitqueue_head(&wc->span.maintq); >+ >+ wc->span.pvt = wc; >+ if (dahdi_register(&wc->span, 0)) { >+ printk(KERN_NOTICE "Unable to register span with Dahdi\n"); >+ return -1; >+ } >+ return 0; >+} >+ >+static void wctdm_post_initialize(struct wctdm *wc) >+{ >+ int x; >+ >+ /* Finalize signalling */ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ if (wc->cardflag & (1 << x)) { >+ if (wc->modtype[x] == MOD_TYPE_FXO) >+ wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; >+ else >+ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; >+ } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { >+ wc->chans[x]->sigcap = 0; >+ } >+ } >+} >+ >+static int wctdm_hardware_init(struct wctdm *wc) >+{ >+ /* Hardware stuff */ >+ unsigned char ver; >+ unsigned char x,y; >+ int failed; >+ long origjiffies; //ml. >+ >+ /* Signal Reset */ >+ printk("before raise reset\n"); >+ outb(0x01, wc->ioaddr + WC_CNTL); >+ >+ /* Wait for 5 second */ >+ >+ origjiffies = jiffies; >+ >+ while(1) >+ { >+ if ((jiffies - origjiffies) >= (HZ*5)) >+ break;; >+ } >+ >+ /* printk(KERN_INFO "after raise reset\n");*/ >+ >+ /* Check OpenVox chip */ >+ x=inb(wc->ioaddr + WC_CNTL); >+ ver = __wctdm_getcreg(wc, WC_VER); >+ wc->fwversion = ver; >+ /*if( ver & FLAG_A800) >+ { >+ wc->card_name = A800P_Name; >+ wc->max_cards = 8; >+ } >+ else >+ { >+ wc->card_name = A1200P_Name; >+ wc->max_cards = 12; >+ }*/ >+ printk(KERN_NOTICE "OpenVox %s version: %01x.%01x\n", wc->card_name, (ver&(~FLAG_A800))>>4, ver&0x0f); >+ >+ failed = 0; >+ if (ver != 0x00) { >+ for (x=0;x<16;x++) { >+ /* Test registers */ >+ __wctdm_setcreg(wc, WC_CS, x); >+ y = __wctdm_getcreg(wc, WC_CS) & 0x0f; >+ if (x != y) { >+ printk(KERN_INFO "%02x != %02x\n", x, y); >+ failed++; >+ } >+ } >+ >+ if (!failed) { >+ printk(KERN_INFO "OpenVox %s passed register test\n", wc->card_name); >+ } else { >+ printk(KERN_NOTICE "OpenVox %s failed register test\n", wc->card_name); >+ return -1; >+ } >+ } else { >+ printk(KERN_INFO "No OpenVox chip %02x\n", ver); >+ } >+ >+ if (spibyhw) >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); // spi controled by hw MiaoLin; >+ else >+ __wctdm_setcreg(wc, WC_SPICTRL, 0); >+ >+ /* Reset PCI Interface chip and registers (and serial) */ >+ outb(0x06, wc->ioaddr + WC_CNTL); >+ /* Setup our proper outputs for when we switch for our "serial" port */ >+ wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; >+ >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ >+ /* Set all to outputs except AUX 5, which is an input */ >+ outb(0xdf, wc->ioaddr + WC_AUXC); >+ >+ /* Select alternate function for AUX0 */ /* Useless in OpenVox by MiaoLin. */ >+ /* outb(0x4, wc->ioaddr + WC_AUXFUNC); */ >+ >+ /* Wait 1/4 of a sec */ >+ wait_just_a_bit(HZ/4); >+ >+ /* Back to normal, with automatic DMA wrap around */ >+ outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); >+ wc->ledstate = 0; >+ wctdm_set_led(wc, 0, 0); >+ >+ /* Make sure serial port and DMA are out of reset */ >+ outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, wc->ioaddr + WC_CNTL); >+ >+ /* Configure serial port for MSB->LSB operation */ >+ outb(0xc1, wc->ioaddr + WC_SERCTL); >+ >+ /* Delay FSC by 0 so it's properly aligned */ >+ outb(0x01, wc->ioaddr + WC_FSCDELAY); /* Modify to 1 by MiaoLin */ >+ >+ /* Setup DMA Addresses */ >+ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ >+ outl(wc->writedma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ >+ outl(wc->writedma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE); /* End */ >+ >+ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ >+ outl(wc->readdma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ >+ outl(wc->readdma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMARE); /* End */ >+ >+ /* Clear interrupts */ >+ outb(0xff, wc->ioaddr + WC_INTSTAT); >+ >+ /* Wait 1/4 of a second more */ >+ wait_just_a_bit(HZ/4); >+ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ int sane=0,ret=0,readi=0; >+#if 1 >+ touch_softlockup_watchdog(); // avoid showing CPU softlock message >+ /* Init with Auto Calibration */ >+ if (!(ret=wctdm_init_proslic(wc, x, 0, 0, sane))) { >+ wc->cardflag |= (1 << x); >+ if (debug) { >+ readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); >+ printk("Proslic module %d loop current is %dmA\n",x, >+ ((readi*3)+20)); >+ } >+ printk(KERN_INFO "Module %d: Installed -- AUTO FXS/DPO\n",x); >+ wctdm_set_led(wc, (unsigned int)x, 1); >+ } else { >+ if(ret!=-2) { >+ sane=1; >+ >+ printk(KERN_INFO "Init ProSlic with Manual Calibration \n"); >+ /* Init with Manual Calibration */ >+ if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { >+ wc->cardflag |= (1 << x); >+ if (debug) { >+ readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); >+ printk("Proslic module %d loop current is %dmA\n",x, >+ ((readi*3)+20)); >+ } >+ printk(KERN_INFO "Module %d: Installed -- MANUAL FXS\n",x); >+ } else { >+ printk(KERN_NOTICE "Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); >+ wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN; >+ } >+ } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { >+ wc->cardflag |= (1 << x); >+ printk(KERN_INFO "Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); >+ wctdm_set_led(wc, (unsigned int)x, 1); >+ } else >+ printk(KERN_NOTICE "Module %d: Not installed\n", x); >+ } >+#endif >+ } >+ >+ /* Return error if nothing initialized okay. */ >+ if (!wc->cardflag && !timingonly) >+ return -1; >+ /*__wctdm_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1); */ /* removed by MiaoLin */ >+ return 0; >+} >+ >+static void wctdm_enable_interrupts(struct wctdm *wc) >+{ >+ /* Clear interrupts */ >+ outb(0xff, wc->ioaddr + WC_INTSTAT); >+ >+ /* Enable interrupts (we care about all of them) */ >+ outb(0x3c, wc->ioaddr + WC_MASK0); >+ /* No external interrupts */ >+ outb(0x00, wc->ioaddr + WC_MASK1); >+} >+ >+static void wctdm_restart_dma(struct wctdm *wc) >+{ >+ /* Reset Master and TDM */ >+ outb(0x01, wc->ioaddr + WC_CNTL); >+ outb(0x01, wc->ioaddr + WC_OPER); >+} >+ >+static void wctdm_start_dma(struct wctdm *wc) >+{ >+ /* Reset Master and TDM */ >+ outb(0x0f, wc->ioaddr + WC_CNTL); >+ set_current_state(TASK_INTERRUPTIBLE); >+ schedule_timeout(1); >+ outb(0x01, wc->ioaddr + WC_CNTL); >+ outb(0x01, wc->ioaddr + WC_OPER); >+} >+ >+static void wctdm_stop_dma(struct wctdm *wc) >+{ >+ outb(0x00, wc->ioaddr + WC_OPER); >+} >+ >+static void wctdm_reset_tdm(struct wctdm *wc) >+{ >+ /* Reset TDM */ >+ outb(0x0f, wc->ioaddr + WC_CNTL); >+} >+ >+static void wctdm_disable_interrupts(struct wctdm *wc) >+{ >+ outb(0x00, wc->ioaddr + WC_MASK0); >+ outb(0x00, wc->ioaddr + WC_MASK1); >+} >+ >+static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) >+{ >+ int res; >+ struct wctdm *wc; >+ struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; >+ int x; >+ int y; >+ >+ static int initd_ifaces=0; >+ >+ if(initd_ifaces){ >+ memset((void *)ifaces,0,(sizeof(struct wctdm *))*WC_MAX_IFACES); >+ initd_ifaces=1; >+ } >+ for (x=0;x<WC_MAX_IFACES;x++) >+ if (!ifaces[x]) break; >+ if (x >= WC_MAX_IFACES) { >+ printk(KERN_NOTICE "Too many interfaces\n"); >+ return -EIO; >+ } >+ >+ if (pci_enable_device(pdev)) { >+ res = -EIO; >+ } else { >+ wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); >+ if (wc) { >+ int cardcount = 0; >+ >+ wc->lastchan = -1; /* first channel offset = -1; */ >+ wc->ledstate = 0; >+ >+ ifaces[x] = wc; >+ memset(wc, 0, sizeof(struct wctdm)); >+ for (x=0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); ++x) { >+ wc->chans[x] = &wc->_chans[x]; >+ } >+ >+ spin_lock_init(&wc->lock); >+ wc->curcard = -1; >+ wc->ioaddr = pci_resource_start(pdev, 0); >+ wc->mem_region = pci_resource_start(pdev, 1); >+ wc->mem_len = pci_resource_len(pdev, 1); >+ wc->mem32 = (unsigned long)ioremap(wc->mem_region, wc->mem_len); >+ wc->dev = pdev; >+ wc->pos = x; >+ wc->variety = d->name; >+ for (y=0;y<MAX_NUM_CARDS;y++) >+ wc->flags[y] = d->flags; >+ /* Keep track of whether we need to free the region */ >+ if (request_region(wc->ioaddr, 0xff, "opvxa1200")) >+ wc->freeregion = 1; >+ else >+ wc->freeregion = 0; >+ >+ if (request_mem_region(wc->mem_region, wc->mem_len, "opvxa1200")) >+ wc->freeregion |= 0x02; >+ >+ /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses >+ 8 bits. */ >+ wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma); >+ if (!wc->writechunk) { >+ printk(KERN_NOTICE "opvxa1200: Unable to allocate DMA-able memory\n"); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ return -ENOMEM; >+ } >+ >+ wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ >+ wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ >+ >+ if (wctdm_initialize(wc)) { >+ printk(KERN_NOTICE "opvxa1200: Unable to intialize FXS\n"); >+ /* Set Reset Low */ >+ x=inb(wc->ioaddr + WC_CNTL); >+ outb((~0x1)&x, wc->ioaddr + WC_CNTL); >+ /* Free Resources */ >+ free_irq(pdev->irq, wc); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ } >+ >+ /* Enable bus mastering */ >+ pci_set_master(pdev); >+ >+ /* Keep track of which device we are */ >+ pci_set_drvdata(pdev, wc); >+ >+ >+ if (request_irq(pdev->irq, wctdm_interrupt, DAHDI_IRQ_SHARED, "opvxa1200", wc)) { >+ printk(KERN_NOTICE "opvxa1200: Unable to request IRQ %d\n", pdev->irq); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); >+ pci_set_drvdata(pdev, NULL); >+ kfree(wc); >+ return -EIO; >+ } >+ >+ if (wctdm_hardware_init(wc)) { >+ unsigned char w; >+ >+ /* Set Reset Low */ >+ w=inb(wc->ioaddr + WC_CNTL); >+ outb((~0x1)&w, wc->ioaddr + WC_CNTL); >+ /* Free Resources */ >+ free_irq(pdev->irq, wc); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); >+ pci_set_drvdata(pdev, NULL); >+ dahdi_unregister(&wc->span); >+ kfree(wc); >+ return -EIO; >+ >+ } >+ >+#ifdef TEST_LOG_INCOME_VOICE >+ for(x=0; x<MAX_NUM_CARDS+NUM_FLAG; x++) >+ { >+ wc->voc_buf[x] = kmalloc(voc_buffer_size, GFP_KERNEL); >+ wc->voc_ptr[x] = 0; >+ } >+#endif >+ >+ if(cidbeforering) >+ { >+ int len = cidbuflen * DAHDI_MAX_CHUNKSIZE; >+ if(debug) >+ printk("cidbeforering support enabled, length is %d msec\n", cidbuflen); >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) >+ { >+ wc->cid_history_buf[x] = kmalloc(len, GFP_KERNEL); >+ wc->cid_history_ptr[x] = 0; >+ wc->cid_history_clone_cnt[x] = 0; >+ wc->cid_state[x] = CID_STATE_IDLE; >+ } >+ } >+ >+ wctdm_post_initialize(wc); >+ >+ /* Enable interrupts */ >+ wctdm_enable_interrupts(wc); >+ /* Initialize Write/Buffers to all blank data */ >+ memset((void *)wc->writechunk,0, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2); >+ >+ /* Start DMA */ >+ wctdm_start_dma(wc); >+ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ if (wc->cardflag & (1 << x)) >+ cardcount++; >+ } >+ >+ printk(KERN_INFO "Found an OpenVox %s: Version %x.%x (%d modules)\n", wc->card_name, (wc->fwversion&(~FLAG_A800))>>4, wc->fwversion&0x0f, cardcount); >+ if(debug) >+ printk(KERN_DEBUG "OpenVox %s debug On\n", wc->card_name); >+ >+ res = 0; >+ } else >+ res = -ENOMEM; >+ } >+ return res; >+} >+ >+static void wctdm_release(struct wctdm *wc) >+{ >+#ifdef TEST_LOG_INCOME_VOICE >+ struct file * f = NULL; >+ mm_segment_t orig_fs; >+ int i; >+ char fname[20]; >+#endif >+ >+ dahdi_unregister(&wc->span); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ >+#ifdef TEST_LOG_INCOME_VOICE >+ for(i=0; i<MAX_NUM_CARDS + NUM_FLAG; i++) >+ { >+ sprintf(fname, "//usr//%d.pcm", i); >+ f = filp_open(fname, O_RDWR|O_CREAT, 00); >+ >+ if (!f || !f->f_op || !f->f_op->read) >+ { >+ printk("WARNING: File (read) object is a null pointer!!!\n"); >+ continue; >+ } >+ >+ f->f_pos = 0; >+ >+ orig_fs = get_fs(); >+ set_fs(KERNEL_DS); >+ >+ if(wc->voc_buf[i]) >+ { >+ f->f_op->write(f, wc->voc_buf[i], voc_buffer_size, &f->f_pos); >+ kfree(wc->voc_buf[i]); >+ } >+ >+ set_fs(orig_fs); >+ fput(f); >+ } >+#endif >+ >+ if(cidbeforering) >+ { >+ int x; >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) >+ kfree(wc->cid_history_buf[x]); >+ } >+ >+ kfree(wc); >+ printk(KERN_INFO "Freed a OpenVox A1200 card\n"); >+} >+ >+static void __devexit wctdm_remove_one(struct pci_dev *pdev) >+{ >+ struct wctdm *wc = pci_get_drvdata(pdev); >+ if (wc) { >+ >+ /* Stop any DMA */ >+ wctdm_stop_dma(wc); >+ wctdm_reset_tdm(wc); >+ >+ /* In case hardware is still there */ >+ wctdm_disable_interrupts(wc); >+ >+ /* Immediately free resources */ >+ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); >+ free_irq(pdev->irq, wc); >+ >+ /* Reset PCI chip and registers */ >+ if(wc->fwversion > 0x11) >+ outb(0x0e, wc->ioaddr + WC_CNTL); >+ else >+ { >+ wc->ledstate = 0; >+ wctdm_set_led(wc,0,0); // power off all leds. >+ } >+ >+ /* Release span, possibly delayed */ >+ if (!wc->usecount) >+ wctdm_release(wc); >+ else >+ wc->dead = 1; >+ } >+} >+ >+static struct pci_device_id wctdm_pci_tbl[] = { >+ { 0xe159, 0x0001, 0x9100, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x95D9, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9500, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9532, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x8519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9559, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9599, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0 } >+}; >+ >+MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); >+ >+static struct pci_driver wctdm_driver = { >+ .name = "opvxa1200", >+ .probe = wctdm_init_one, >+ .remove = __devexit_p(wctdm_remove_one), >+ .suspend = NULL, >+ .resume = NULL, >+ .id_table = wctdm_pci_tbl, >+}; >+ >+static int __init wctdm_init(void) >+{ >+ int res; >+ int x; >+ for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { >+ if (!strcmp(fxo_modes[x].name, opermode)) >+ break; >+ } >+ if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) { >+ _opermode = x; >+ } else { >+ printk(KERN_NOTICE "Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); >+ for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++) >+ printk(KERN_INFO " %s\n", fxo_modes[x].name); >+ printk(KERN_INFO "Note this option is CASE SENSITIVE!\n"); >+ return -ENODEV; >+ } >+ if (!strcmp(fxo_modes[_opermode].name, "AUSTRALIA")) { >+ boostringer=1; >+ fxshonormode=1; >+} >+ if (battdebounce == 0) { >+ battdebounce = fxo_modes[_opermode].battdebounce; >+ } >+ if (battalarm == 0) { >+ battalarm = fxo_modes[_opermode].battalarm; >+ } >+ if (battthresh == 0) { >+ battthresh = fxo_modes[_opermode].battthresh; >+ } >+ >+ res = dahdi_pci_module(&wctdm_driver); >+ if (res) >+ return -ENODEV; >+ return 0; >+} >+ >+static void __exit wctdm_cleanup(void) >+{ >+ pci_unregister_driver(&wctdm_driver); >+} >+ >+module_param(debug, int, 0600); >+module_param(loopcurrent, int, 0600); >+module_param(reversepolarity, int, 0600); >+module_param(robust, int, 0600); >+module_param(opermode, charp, 0600); >+module_param(timingonly, int, 0600); >+module_param(lowpower, int, 0600); >+module_param(boostringer, int, 0600); >+module_param(fastringer, int, 0600); >+module_param(fxshonormode, int, 0600); >+module_param(battdebounce, uint, 0600); >+module_param(battthresh, uint, 0600); >+module_param(battalarm, uint, 0600); >+module_param(ringdebounce, int, 0600); >+module_param(fwringdetect, int, 0600); >+module_param(alawoverride, int, 0600); >+module_param(fastpickup, int, 0600); >+module_param(fxotxgain, int, 0600); >+module_param(fxorxgain, int, 0600); >+module_param(fxstxgain, int, 0600); >+module_param(fxsrxgain, int, 0600); >+module_param(spibyhw, int, 0600); >+module_param(usememio, int, 0600); >+module_param(cidbeforering, int, 0600); >+module_param(cidbuflen, int, 0600); >+module_param(cidtimeout, int, 0600); >+module_param(fxofullscale, int, 0600); >+module_param(fixedtimepolarity, int, 0600); >+ >+MODULE_DESCRIPTION("OpenVox A1200 Driver"); >+MODULE_AUTHOR("MiaoLin <miaolin@openvox.com.cn>"); >+MODULE_LICENSE("GPL v2"); >+ >+module_init(wctdm_init); >+module_exit(wctdm_cleanup); >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/wcopenpci.c dahdi-svn-new/drivers/dahdi/wcopenpci.c >--- dahdi-linux-2.2.0.2/drivers/dahdi/wcopenpci.c 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/wcopenpci.c 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,1842 @@ >+/* >+ * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface >+ * >+ * Written by Mark Spencer <markster@linux-support.net> >+ * Matthew Fredrickson <creslin@linux-support.net> >+ * Ben Kramer <ben@voicetronix.com.au> >+ * Ron Lee <ron@voicetronix.com.au> >+ * >+ * Copyright (C) 2001, Linux Support Services, Inc. >+ * Copyright (C) 2005 - 2007, Voicetronix >+ * >+ * 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. >+ * >+ */ >+ >+/* Conditional debug options */ >+#define VERBOSE_TIMING 0 >+ >+/* Driver constants */ >+#define DRIVER_DESCRIPTION "Voicetronix OpenPCI DAHDI driver" >+#define DRIVER_AUTHOR "Mark Spencer <markster@digium.com> "\ >+ "Voicetronix <support@voicetronix.com.au>" >+ >+#define NAME "wcopenpci" >+#define MAX_PORTS 8 /* Maximum number of ports on each carrier */ >+#define MAX_CARDS 8 /* Maximum number of carriers per host */ >+ >+#define DEFAULT_COUNTRY "AUSTRALIA" >+ >+ >+#include <linux/init.h> >+#include <linux/module.h> >+#include <linux/pci.h> >+#include <linux/delay.h> >+#include <linux/sched.h> >+ >+#include <dahdi/kernel.h> >+#include <dahdi/version.h> >+#include "proslic.h" >+#include <dahdi/wctdm_user.h> >+ >+ >+ >+/* Compatibility helpers */ >+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) >+ #include <linux/interrupt.h> >+#else >+ typedef void irqreturn_t; >+ #define IRQ_NONE >+ #define IRQ_HANDLED >+ #define IRQ_RETVAL(x) >+ #define __devexit_p(x) x >+#endif >+ >+// Centos4.3 uses a modified 2.6.9 kernel, with no indication that >+// it is different from the mainstream (or even Centos4.2 2.6.9) >+// kernel, so we must crowbar off the dunce-hat manually here. >+#if !defined CENTOS4_3 && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) >+ typedef int gfp_t; >+ static inline void *kzalloc( size_t n, gfp_t flags ){ >+ void *p = kmalloc(n,flags); >+ if (p) memset(p, 0, n); >+ return p; >+ } >+#endif >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) >+ #define DEFINE_MUTEX(x) DECLARE_MUTEX(x) >+ #define mutex_init(x) init_MUTEX(x) >+ #define mutex_lock(x) down(x) >+ #define mutex_lock_interruptible(x) down_interruptible(x) >+ #define mutex_trylock(x) down_trylock(x) >+ #define mutex_unlock(x) up(x) >+#else >+ #include <linux/mutex.h> >+#endif >+ >+ >+static struct fxo_mode { >+ char *name; >+ 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, 1, 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, }, >+}; >+ >+static struct ps_country_reg { >+ const char *country; >+ unsigned short value; >+} ps_country_regs[] = { >+ {"ARGENTINA", 0x8}, >+ {"AUSTRALIA", 0xD}, >+ {"AUSTRIA", 0xD}, >+ {"BAHRAIN", 0xC}, >+ {"BELGIUM", 0xC}, >+ {"BRAZIL", 0x8}, >+ {"BULGARIA", 0xD}, >+ {"CANADA", 0x8}, >+ {"CHILE", 0x8}, >+ {"CHINA", 0xC}, >+ {"COLOMBIA", 0x8}, >+ {"CROATIA", 0xC}, >+ {"CYPRUS", 0xC}, >+ {"CZECH", 0xC}, >+ {"DENMARK", 0xC}, >+ {"ECUADOR", 0x8}, >+ {"EGYPT", 0x8}, >+ {"ELSALVADOR", 0x8}, >+ {"FINLAND", 0xC}, >+ {"FRANCE", 0xC}, >+ {"GERMANY", 0xD}, >+ {"GREECE", 0xC}, >+ {"GUAM", 0x8}, >+ {"HONGKONG", 0x8}, >+ {"HUNGARY", 0x8}, >+ {"ICELAND", 0xC}, >+ {"INDIA", 0xF}, >+ {"INDONESIA", 0x8}, >+ {"IRELAND", 0xC}, >+ {"ISRAEL", 0xC}, >+ {"ITALY", 0xC}, >+ {"JAPAN", 0x8}, >+ {"JORDAN", 0x8}, >+ {"KAZAKHSTAN", 0x8}, >+ {"KUWAIT", 0x8}, >+ {"LATVIA", 0xC}, >+ {"LEBANON", 0xC}, >+ {"LUXEMBOURG", 0xC}, >+ {"MACAO", 0x8}, >+ {"MALAYSIA", 0x8}, >+ {"MALTA", 0xC}, >+ {"MEXICO", 0x8}, >+ {"MOROCCO", 0xC}, >+ {"NETHERLANDS",0xC}, >+ {"NEWZEALAND", 0xF}, >+ {"NIGERIA", 0xC}, >+ {"NORWAY", 0xC}, >+ {"OMAN", 0x8}, >+ {"PAKISTAN", 0x8}, >+ {"PERU", 0x8}, >+ {"PHILIPPINES",0x8}, >+ {"POLAND", 0x8}, >+ {"PORTUGAL", 0xC}, >+ {"ROMANIA", 0x8}, >+ {"RUSSIA", 0x8}, >+ {"SAUDIARABIA",0x8}, >+ {"SINGAPORE", 0x8}, >+ {"SLOVAKIA", 0xE}, >+ {"SLOVENIA", 0xE}, >+ {"SOUTHAFRICA",0xE}, >+ {"SOUTHKOREA", 0x8}, >+ {"SPAIN", 0xC}, >+ {"SWEDEN", 0xC}, >+ {"SWITZERLAND",0xC}, >+ {"SYRIA", 0x8}, >+ {"TAIWAN", 0x8}, >+ {"THAILAND", 0x8}, >+ {"UAE", 0x8}, >+ {"UK", 0xC}, >+ {"USA", 0x8}, >+ {"YEMEN", 0x8} >+}; >+ >+#define INOUT 2 >+ >+/* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses >+ 32 bits. Allocate an extra set just for control too */ >+#define VT_PCIDMA_BLOCKSIZE (DAHDI_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2) >+#define VT_PCIDMA_MIDDLE (DAHDI_MAX_CHUNKSIZE * MAX_PORTS - 4) >+#define VT_PCIDMA_END (DAHDI_MAX_CHUNKSIZE * MAX_PORTS * 2 - 4) >+ >+#define ID_DATA_MAXSIZE 30 >+ >+#define NUM_CAL_REGS 12 >+#define NUM_FXO_REGS 60 >+ >+#define TREG(addr) (wc->ioaddr + addr) >+ >+#define TJ_CNTL TREG(0x00) >+#define TJ_OPER TREG(0x01) >+#define TJ_AUXC TREG(0x02) >+#define TJ_AUXD TREG(0x03) >+#define TJ_MASK0 TREG(0x04) >+#define TJ_MASK1 TREG(0x05) >+#define TJ_INTSTAT TREG(0x06) >+#define TJ_AUXR TREG(0x07) >+ >+#define TJ_DMAWS TREG(0x08) >+#define TJ_DMAWI TREG(0x0c) >+#define TJ_DMAWE TREG(0x10) >+#define TJ_DMAWC TREG(0x14) >+#define TJ_DMARS TREG(0x18) >+#define TJ_DMARI TREG(0x1c) >+#define TJ_DMARE TREG(0x20) >+#define TJ_DMARC TREG(0x24) >+ >+#define TJ_AUXINTPOL TREG(0x2A) >+ >+#define TJ_AUXFUNC TREG(0x2b) >+#define TJ_SFDELAY TREG(0x2c) >+#define TJ_SERCTL TREG(0x2d) >+#define TJ_SFLC TREG(0x2e) >+#define TJ_FSCDELAY TREG(0x2f) >+ >+#define TJ_REGBASE TREG(0xc0) >+ >+#define PIB(addr) (TJ_REGBASE + addr * 4) >+ >+#define HTXF_READY (inb(PIB(0)) & 0x10) >+#define HRXF_READY (inb(PIB(0)) & 0x20) >+ >+ >+#define VT_PORT_EMPTY 0 >+#define VT_PORT_VDAA 1 /* Voice DAA - FXO */ >+#define VT_PORT_PROSLIC 2 /* ProSLIC - FXS */ >+ >+#define VBAT 0xC7 >+ >+#define HKMODE_FWDACT 1 >+#define HKMODE_FWDONACT 2 >+#define HKMODE_RINGING 4 >+ >+#define HOOK_ONHOOK 0 >+#define HOOK_OFFHOOK 1 >+ >+#define DSP_CODEC_RING 12 /* RING rising edge detected */ >+#define DSP_CODEC_HKOFF 22 /* station port off hook */ >+#define DSP_CODEC_HKON 23 /* station port on hook */ >+#define DSP_RING_OFF 24 /* RING falling edge detected */ >+#define DSP_DROP 25 >+ >+#define DSP_CODEC_FLASH 26 /* station port hook flash */ >+ >+#define DSP_LOOP_OFFHOOK 38 /* Loop Off hook from OpenPCI */ >+#define DSP_LOOP_ONHOOK 39 /* Loop On hook from OpenPCI */ >+#define DSP_LOOP_POLARITY 40 /* Loop Polarity from OpenPCI */ >+#define DSP_LOOP_NOBATT 41 >+ >+#define DSP_PROSLIC_SANITY 50 /* Sanity alert from a ProSLIC port */ >+#define DSP_PROSLIC_PWR_ALARM 51 /* Power Alarm from a ProSLIC port */ >+#define DSP_VDAA_ISO_FRAME_E 52 /* ISO-cap frame sync lost on VDAA port*/ >+ >+#if VERBOSE_TIMING >+ #define REPORT_WAIT(n,x) \ >+ cardinfo(card->cardnum, #n " wait at %d, " #x " = %d", __LINE__, x ) >+#else >+ #define REPORT_WAIT(n,x) >+#endif >+ >+#define BUSY_WAIT(countvar,cond,delay,iter,failret) \ >+ countvar=0; \ >+ while(cond){ \ >+ udelay(delay); \ >+ if(++countvar > iter){ \ >+ cardcrit(wc->boardnum, "busy wait FAILED at %d", __LINE__); \ >+ return failret; \ >+ } \ >+ } \ >+ REPORT_WAIT(busy,i) >+ >+#define LOCKED_WAIT(countvar,cond,delay,iter,failret) \ >+ countvar=0; \ >+ while(cond){ \ >+ udelay(delay); \ >+ if(++countvar > iter){ \ >+ dbginfo(wc->boardnum,"busy wait failed at %d",__LINE__); \ >+ spin_unlock_irqrestore(&wc->lock, flags); \ >+ return failret; \ >+ } \ >+ } \ >+ REPORT_WAIT(locked,i) >+ >+#define HTXF_WAIT() BUSY_WAIT(i,HTXF_READY,5,500,RET_FAIL) >+#define HRXF_WAIT() BUSY_WAIT(i,!HRXF_READY,5,70000,RET_FAIL) >+#define HTXF_WAIT_RET(failret) BUSY_WAIT(i,HTXF_READY,5,500,failret) >+#define HRXF_WAIT_RET(failret) BUSY_WAIT(i,!HRXF_READY,5,1000,failret) >+ >+#define HTXF_WAIT_LOCKED() LOCKED_WAIT(i,HTXF_READY,5,500,RET_FAIL) >+#define HRXF_WAIT_LOCKED() LOCKED_WAIT(i,!HRXF_READY,5,1000,RET_FAIL) >+#define HTXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,HTXF_READY,5,500,failret) >+#define HRXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,!HRXF_READY,5,1000,failret) >+ >+ >+struct openpci { >+ struct pci_dev *dev; >+ char *variety; >+ int boardnum; >+ int portcount; >+ int porttype[MAX_PORTS]; >+ >+ int firmware; >+ char serial[ID_DATA_MAXSIZE]; >+ >+ spinlock_t lock; >+ >+ //XXX Replace these with proper try_module_get locking in the dahdi driver. >+ //int usecount; //XXX >+ //int dead; //XXX >+ union { >+ struct { >+ int offhook; >+ } fxo; >+ struct { >+ int ohttimer; >+ int idletxhookstate; /* IDLE changing hook state */ >+ int lasttxhook; >+ } fxs; >+ } mod[MAX_PORTS]; >+ >+ unsigned long ioaddr; >+ dma_addr_t readdma; >+ dma_addr_t writedma; >+ volatile unsigned int *writechunk; /* Double-word aligned write memory */ >+ volatile unsigned int *readchunk; /* Double-word aligned read memory */ >+ >+ struct dahdi_chan _chans[MAX_PORTS]; >+ struct dahdi_chan *chans[MAX_PORTS]; >+ struct dahdi_span span; >+} *cards[MAX_CARDS]; >+ >+// You must hold this lock anytime you access or modify the cards[] array. >+DEFINE_MUTEX(cards_mutex); >+ >+static unsigned char fxo_port_lookup[8] = { 0x0, 0x8, 0x4, 0xc, 0x10, 0x18, 0x14, 0x1c}; >+static unsigned char fxs_port_lookup[8] = { 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13}; >+static char wcopenpci[] = "Voicetronix OpenPCI"; >+ >+static char *country = DEFAULT_COUNTRY; >+static int reversepolarity; // = 0 >+static int debug; // = 0 >+ >+module_param(country, charp, 0444); >+module_param(debug, int, 0600); >+module_param(reversepolarity, int, 0600); >+MODULE_PARM_DESC(country, "Set the default country name"); >+MODULE_PARM_DESC(debug, "Enable verbose logging"); >+ >+//#define DEBUG_LOOP_VOLTAGE 1 >+#ifdef DEBUG_LOOP_VOLTAGE >+ // This param is a 32 bit bitfield where bit 1 << cardnum * 8 << portnum >+ // will enable voltage monitoring on that port (fxo only presently) >+ static int voltmeter; // = 0 >+ module_param(voltmeter, int, 0600); >+ MODULE_PARM_DESC(voltmeter, "Enable loop voltage metering"); >+#endif >+ >+ >+/* boolean return values */ >+#define RET_OK 1 >+#define RET_FAIL 0 >+ >+/* Convenience macros for logging */ >+#define info(format,...) printk(KERN_INFO NAME ": " format "\n" , ## __VA_ARGS__) >+#define warn(format,...) printk(KERN_WARNING NAME ": " format "\n" , ## __VA_ARGS__) >+#define crit(format,...) printk(KERN_CRIT NAME ": " format "\n" , ## __VA_ARGS__) >+#define cardinfo(cardnum,format,...) info("[%02d] " format, cardnum , ## __VA_ARGS__) >+#define cardwarn(cardnum,format,...) warn("[%02d] " format, cardnum , ## __VA_ARGS__) >+#define cardcrit(cardnum,format,...) crit("[%02d] " format, cardnum , ## __VA_ARGS__) >+#define dbginfo(cardnum,format,...) if(debug) info("[%02d] " format, cardnum , ## __VA_ARGS__) >+ >+ >+static inline const char *porttype(struct openpci *wc, int port) >+{ //{{{ >+ switch( wc->porttype[port] ) { >+ case VT_PORT_VDAA: return "VDAA"; >+ case VT_PORT_PROSLIC: return "ProSLIC"; >+ case VT_PORT_EMPTY: return "empty port"; >+ default: return "unknown type"; >+ } >+} //}}} >+ >+ >+static int __read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned char portadr = fxo_port_lookup[port]; >+ int i; >+ >+ if (HRXF_READY) *value = inb(PIB(1)); >+ >+ outb(0x11, PIB(1)); HTXF_WAIT(); >+ outb(0x2, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); *value = inb(PIB(1)); >+ >+ return RET_OK; >+} //}}} >+ >+static int read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __read_reg_fxo(wc, port, reg, value) ){ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXO port %d, reg %d, read failed!", port, reg); >+ return RET_FAIL; >+} //}}} >+ >+static int __read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned char portadr = fxs_port_lookup[port]; >+ int i; >+ >+ if (HRXF_READY) *value = inb(PIB(1)); >+ >+ outb(0x13, PIB(1)); HTXF_WAIT(); >+ outb(0x2, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); *value = inb(PIB(1)); >+ >+ return RET_OK; >+} //}}} >+ >+static int read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __read_reg_fxs(wc, port, reg, value) ) { >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXS port %d, reg %d, read failed!", port, reg); >+ return RET_FAIL; >+} //}}} >+ >+static int __write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned char portadr = fxo_port_lookup[port]; >+ int i; >+ >+ outb(0x10, PIB(1) ); HTXF_WAIT(); >+ outb(0x3, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ outb(value, PIB(1)); HTXF_WAIT(); >+ >+ return RET_OK; >+} //}}} >+ >+static int write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __write_reg_fxo(wc, port, reg, value) ){ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXO port %d, reg %d, write(%d) failed!", port, reg, value); >+ return RET_FAIL; >+} //}}} >+ >+static int __write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned char portadr = fxs_port_lookup[port]; >+ int i; >+ >+ outb(0x12, PIB(1) ); HTXF_WAIT(); >+ outb(0x3, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ outb(value, PIB(1)); HTXF_WAIT(); >+ >+ return RET_OK; >+} //}}} >+ >+static int write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __write_reg_fxs(wc, port, reg, value) ){ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXS port %d, reg %d, write(%d) failed!", port, reg, value); >+ return RET_FAIL; >+} //}}} >+ >+static int __wait_indreg_fxs(struct openpci *wc, int port) >+{ //{{{ >+ unsigned char value; >+ int count = 100; >+ >+ while (--count) >+ { >+ if( __read_reg_fxs(wc, port, I_STATUS, &value) ){ >+ if( value == 0 ) >+ return RET_OK; >+ } else { >+ cardcrit(wc->boardnum, >+ "failed to read port %d PS_IND_ADDR_ST, retrying...", >+ port); >+ } >+ udelay(5); >+ } >+ cardcrit(wc->boardnum, "Failed to wait for indirect reg write to port %d", port); >+ return RET_FAIL; >+} //}}} >+ >+static int write_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __wait_indreg_fxs(wc, port) >+ && __write_reg_fxs(wc, port, IDA_LO, value & 0xff) >+ && __write_reg_fxs(wc, port, IDA_HI, (value & 0xff00)>>8) >+ && __write_reg_fxs(wc, port, IAA, reg) >+ && __wait_indreg_fxs(wc, port) ) >+ { >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXS indreg %d write failed on port %d", reg, port); >+ return RET_FAIL; >+} //}}} >+ >+static int read_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short *value) >+{ //{{{ >+ unsigned long flags; >+ unsigned char lo, hi; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __wait_indreg_fxs(wc, port) >+ && __write_reg_fxs(wc, port, IAA, reg) >+ && __wait_indreg_fxs(wc, port) >+ && __read_reg_fxs(wc, port, IDA_LO, &lo) >+ && __read_reg_fxs(wc, port, IDA_HI, &hi) ) >+ { >+ *value = lo | hi << 8; >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_FAIL; >+} //}}} >+ >+static void start_dma(struct openpci *wc) >+{ //{{{ >+ outb(0x0f, TJ_CNTL); >+ set_current_state(TASK_INTERRUPTIBLE); >+ schedule_timeout(1); >+ outb(0x01, TJ_CNTL); >+ outb(0x01, TJ_OPER); >+} //}}} >+ >+static void restart_dma(struct openpci *wc) >+{ //{{{ >+ /* Reset Master and TDM */ >+ outb(0x01, TJ_CNTL); >+ outb(0x01, TJ_OPER); >+} //}}} >+ >+/* You must hold the card spinlock to call this function */ >+static int __ping_arm(struct openpci *wc) >+{ //{{{ >+ int i; >+ int pong=0; >+ >+ while(pong != 0x02){ >+ outb(0x02, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); pong = inb(PIB(1)); >+ dbginfo(wc->boardnum, "ping_arm returned %x", pong); >+ } >+ while(pong == 0x02){ >+ // Poke no-ops into the arm while it is still returning data, >+ // if 500 usec elapses with no further response from it then >+ // the message queue is should be completely cleared. >+ outb(0x00, PIB(1)); HTXF_WAIT(); >+ i = 100; >+ while( !HRXF_READY && --i ) udelay(5); >+ if( i == 0 ) break; >+ pong = inb(PIB(1)); >+ dbginfo(wc->boardnum, "ping_arm returned %x.", pong); >+ } >+ return RET_OK; >+} //}}} >+ >+static void arm_event(struct openpci *wc, char *msg) >+{ //{{{ >+ int port = msg[0]; >+ >+ switch(msg[1]){ >+ case DSP_LOOP_OFFHOOK: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); >+ dbginfo(wc->boardnum, "Port %d Loop OffHook", port); >+ break; >+ >+ case DSP_LOOP_ONHOOK: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); >+ dbginfo(wc->boardnum, "Port %d Loop OnHook", port); >+ break; >+ >+ case DSP_LOOP_POLARITY: >+ dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_POLARITY); >+ dbginfo(wc->boardnum, "Port %d Loop Polarity", port); >+ break; >+ >+ case DSP_CODEC_RING: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_RING); >+ dbginfo(wc->boardnum, "Port %d Ring On", port); >+ break; >+ >+ case DSP_RING_OFF: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); >+ dbginfo(wc->boardnum, "Port %d Ring Off", port); >+ break; >+ >+ case DSP_CODEC_HKOFF: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); >+ dbginfo(wc->boardnum, "Port %d Station OffHook", port); >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 5; >+ else >+ wc->mod[port].fxs.idletxhookstate = 1; >+ break; >+ >+ case DSP_CODEC_HKON: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); >+ dbginfo(wc->boardnum, "Port %d Station OnHook", port); >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 6; >+ else >+ wc->mod[port].fxs.idletxhookstate = 2; >+ break; >+ >+ case DSP_CODEC_FLASH: >+ dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_WINKFLASH); >+ dbginfo(wc->boardnum, "Port %d Station Flash", port); >+ break; >+ >+ case DSP_DROP: >+ case DSP_LOOP_NOBATT: >+ break; >+ >+ //XXX What to do to recover from these? >+ case DSP_PROSLIC_SANITY: >+ dbginfo(wc->boardnum, "Port %d ProSlic has gone insane!", port); >+ break; >+ >+ case DSP_PROSLIC_PWR_ALARM: >+ { >+ char errbuf[32] = " Unknown", *p = errbuf; >+ int i = 49; >+ >+ msg[2] >>= 2; >+ for(; i < 55; ++i, msg[2] >>= 1 ) >+ if(msg[2] & 1){ *(++p)='Q'; *(++p)=i; *(++p)=','; } >+ if( p != errbuf ) *p = '\0'; >+ cardcrit(wc->boardnum,"%d: ProSlic power ALARM:%s",msg[0],errbuf); >+ //write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook ); >+ return; >+ } >+ >+ case DSP_VDAA_ISO_FRAME_E: >+ dbginfo(wc->boardnum, "Port %d VDAA has lost ISO-Cap frame lock", port); >+ break; >+ >+ default: >+ cardwarn(wc->boardnum, "Unknown message from Arm[%d] for port %d", >+ msg[1], port); >+ break; >+ } >+} //}}} >+ >+/* You must hold the card spinlock to call this function */ >+static inline int __read_arm_byte( struct openpci *wc, unsigned char *msg ) >+{ //{{{ >+ int i; >+ >+ HRXF_WAIT(); *msg = inb(PIB(1)); >+ return RET_OK; >+} //}}} >+ >+static inline int read_arm_msg( struct openpci *wc, unsigned char *msg ) >+{ //{{{ >+ unsigned long flags; >+ int i, d, count; >+ int ret = RET_OK; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x08, PIB(1)); HTXF_WAIT_LOCKED(); >+ //XXX Do we need to clear the interrupt flag even if this fails? >+ HRXF_WAIT_LOCKED(); count = inb(PIB(1)); >+ if( count == 0 ){ >+ ret = RET_FAIL; >+ } else if( count < 3 || count > 4 ){ >+ cardcrit(wc->boardnum, "BOGUS arm message size %d, flushing queue", count); >+ // NB: This may take a while (up to 500usec or more) to complete >+ // and we are in the isr at present when this is called, so >+ // we may miss an interrupt or two while this is done in the >+ // bottom half, but we are already in trouble, so... >+ d = debug; debug = 5; __ping_arm( wc ); debug = d; >+ ret = RET_FAIL; >+ } else while( --count ){ >+ if( ! __read_arm_byte(wc, msg) ){ >+ cardcrit(wc->boardnum, >+ "Failed to read arm message %d more bytes expected", >+ count); >+ ret = RET_FAIL; >+ break; >+ } >+ ++msg; >+ } >+ outb(0x09, PIB(1)); HTXF_WAIT_LOCKED(); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return ret; >+} //}}} >+ >+static void openpci_arm_work( void *cardptr ) >+{ //{{{ >+ struct openpci *wc = (struct openpci*)cardptr; >+ unsigned char armmsg[4]; >+ >+ if( read_arm_msg(wc, armmsg) ) arm_event(wc, armmsg); >+} //}}} >+ >+ >+static inline void openpci_write(struct openpci *wc, unsigned char flags) >+{ //{{{ >+ int x,y; >+ volatile unsigned int *writechunk; >+ >+ if (flags & 0x01) >+ writechunk = wc->writechunk; >+ else if (flags & 0x02) >+ writechunk = wc->writechunk + DAHDI_CHUNKSIZE*2; >+ else { >+ cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x", >+ flags, inb(TJ_DMAWC) ); >+ return; >+ } >+ /* get data */ >+ dahdi_transmit(&wc->span); >+ for (y=0,x=0;x<DAHDI_CHUNKSIZE;++x) { >+ /* Send a sample, as a 32-bit word */ >+#ifdef __BIG_ENDIAN >+#error No big endian support (yet) >+#else >+ /* transmit second 4 ports */ >+ writechunk[y]=0; >+ if (wc->porttype[4]) >+ writechunk[y] |= (wc->chans[4]->writechunk[x] << 24); >+ else >+ writechunk[y] |= (0x01 << 24); >+ if (wc->porttype[5]) >+ writechunk[y] |= (wc->chans[5]->writechunk[x] << 16); >+ if (wc->porttype[6]) >+ writechunk[y] |= (wc->chans[6]->writechunk[x] << 8); >+ if (wc->porttype[7]) >+ writechunk[y] |= (wc->chans[7]->writechunk[x]); >+ ++y; >+ >+ /* transmit first 4 ports */ >+ writechunk[y]=0x01000000; >+ /* Make sure first port doesnt equal 0x00 */ >+ if (wc->porttype[0]){ >+ if (wc->chans[0]->writechunk[x] == 0) >+ writechunk[y] |= (0x01 << 24); >+ else >+ writechunk[y] |= (wc->chans[0]->writechunk[x] << 24); >+ } >+ //else writechunk[y] |= (0x00 << 24); >+ if (wc->porttype[1]) >+ writechunk[y] |= (wc->chans[1]->writechunk[x] << 16); >+ if (wc->porttype[2]) >+ writechunk[y] |= (wc->chans[2]->writechunk[x] << 8); >+ if (wc->porttype[3]) >+ writechunk[y] |= (wc->chans[3]->writechunk[x]); >+ ++y; >+#endif >+ } >+} //}}} >+ >+static inline void openpci_read(struct openpci *wc, unsigned char flags) >+{ //{{{ >+ int x,y; >+ volatile unsigned int *readchunk; >+ >+ if (flags & 0x08) >+ readchunk = wc->readchunk + DAHDI_CHUNKSIZE*2; >+ else if (flags & 0x04) >+ readchunk = wc->readchunk; >+ else { >+ cardcrit(wc->boardnum, "bad read interrupt flags %x, at %x", >+ flags, inb(TJ_DMARC)); >+ return; >+ } >+ >+ for (y=0,x=0;x<DAHDI_CHUNKSIZE;++x) { >+#ifdef __BIG_ENDIAN >+#error No big endian support (yet) >+#else >+ /* Receive first 4 ports */ >+ >+ if (wc->porttype[0]) >+ wc->chans[0]->readchunk[x] = (readchunk[y] >> 24) & 0xff; >+ if (wc->porttype[1]) >+ wc->chans[1]->readchunk[x] = (readchunk[y] >> 16) & 0xff; >+ if (wc->porttype[2]) >+ wc->chans[2]->readchunk[x] = (readchunk[y] >> 8) & 0xff; >+ if (wc->porttype[3]) >+ wc->chans[3]->readchunk[x] = (readchunk[y]) & 0xff; >+ ++y; >+ /* Receive second 4 ports */ >+ if (wc->porttype[4]) >+ wc->chans[4]->readchunk[x] = (readchunk[y] >> 24) & 0xff; >+ if (wc->porttype[5]) >+ wc->chans[5]->readchunk[x] = (readchunk[y] >> 16) & 0xff; >+ if (wc->porttype[6]) >+ wc->chans[6]->readchunk[x] = (readchunk[y] >> 8) & 0xff; >+ if (wc->porttype[7]) >+ wc->chans[7]->readchunk[x] = (readchunk[y]) & 0xff; >+ ++y; >+#endif >+ } >+ /* XXX We're wasting 8 taps. We should get closer :( */ >+ for (x = 0; x < MAX_PORTS; x++) { >+ if (wc->porttype[x]) >+ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); >+ } >+ dahdi_receive(&wc->span); >+} //}}} >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) >+static irqreturn_t openpci_isr(int irq, void *dev_id, struct pt_regs *regs) >+#else >+static irqreturn_t openpci_isr(int irq, void *dev_id) >+#endif >+{ //{{{ >+ struct openpci *wc = dev_id; >+ unsigned long flags; >+ unsigned char status; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ status = inb(TJ_INTSTAT); >+ outb(status, TJ_INTSTAT); >+ >+ if (!status) { >+ if(inb(TJ_AUXR) & 0x02) { >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return IRQ_NONE; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ openpci_arm_work(wc); >+ return IRQ_HANDLED; >+ } >+ if (status & 0x10){ >+ /* PCI Master abort */ >+ cardcrit(wc->boardnum, "PCI Master Abort."); >+ /* Stop DMA, wait for watchdog */ >+ outb(0x00, TJ_OPER); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return IRQ_HANDLED; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ if (status & 0x20){ >+ /* PCI Target abort */ >+ cardcrit(wc->boardnum, "PCI Target Abort."); >+ return IRQ_HANDLED; >+ } >+ if (status & 0x03){ >+ openpci_write(wc, status); >+ } >+ if (status & 0x0c){ >+ #ifdef DEBUG_LOOP_VOLTAGE >+ //{{{ >+ static int counter[MAX_CARDS]; >+ int card = wc->boardnum; >+ int port = ++counter[card] & 0x07; >+ int ignore = counter[card] & 0xf0; >+ >+ if( ! ignore && (voltmeter & ((1 << (card * 8)) << port)) ) { >+ unsigned char lv; >+ if( wc->porttype[port] == VT_PORT_VDAA && read_reg_fxo(wc, port, 29, &lv) ) >+ cardinfo(wc->boardnum, "Port %d loop voltage %d", >+ port, lv < 128 ? lv : lv - 256); >+ } >+ //}}} >+ #endif >+ openpci_read(wc, status); >+ } >+ >+ return IRQ_HANDLED; >+} //}}} >+ >+static int openpci_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) >+{ //{{{ >+ struct wctdm_stats stats; >+ struct wctdm_regs regs; >+ struct wctdm_regop regop; >+ struct wctdm_echo_coefs echoregs; >+ struct openpci *wc = chan->pvt; >+ int port = chan->chanpos - 1; >+ int x; >+ >+ switch (cmd) { >+ case DAHDI_ONHOOKTRANSFER: >+ if (wc->porttype[port] != VT_PORT_PROSLIC) >+ return -EINVAL; >+ if (get_user(x, (int *)data)) >+ return -EFAULT; >+ wc->mod[port].fxs.ohttimer = x << 3; >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ >+ else >+ wc->mod[port].fxs.idletxhookstate = 0x2; >+ switch(wc->mod[port].fxs.lasttxhook) { >+ case 0x1: >+ case 0x5: >+ if (reversepolarity) >+ wc->mod[port].fxs.lasttxhook = 0x6; >+ else >+ wc->mod[port].fxs.lasttxhook = 0x2; >+ if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) >+ return -EIO; >+ } >+ break; >+ case DAHDI_SETPOLARITY: >+ if (get_user(x, (int *)data)) >+ return -EFAULT; >+ if (wc->porttype[port] != VT_PORT_PROSLIC) >+ return -EINVAL; >+ /* Can't change polarity while ringing or when open */ >+ if ((wc->mod[port].fxs.lasttxhook == 0x04) || >+ (wc->mod[port].fxs.lasttxhook == 0x00)) >+ return -EINVAL; >+ >+ if ((x && !reversepolarity) || (!x && reversepolarity)) >+ wc->mod[port].fxs.lasttxhook |= 0x04; >+ else >+ wc->mod[port].fxs.lasttxhook &= ~0x04; >+ if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) >+ return -EIO; >+ break; >+ case WCTDM_GET_STATS: >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ unsigned char linevolt; >+ if( read_reg_fxs(wc, port, 80, &linevolt) ) >+ stats.tipvolt = linevolt * -376; >+ else >+ return -EIO; >+ if( read_reg_fxs(wc, port, 81, &linevolt) ) >+ stats.ringvolt = linevolt * -376; >+ else >+ return -EIO; >+ if( read_reg_fxs(wc, port, 82, &linevolt) ) >+ stats.batvolt = linevolt * -376; >+ else >+ return -EIO; >+ } else if (wc->porttype[port] == VT_PORT_VDAA) { >+ unsigned char linevolt; >+ if( read_reg_fxo(wc, port, 29, &linevolt) ) >+ stats.tipvolt = stats.ringvolt = stats.batvolt = linevolt * 1000; >+ else >+ return -EIO; >+ } else >+ return -EINVAL; >+ if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats))) >+ return -EFAULT; >+ break; >+ case WCTDM_GET_REGS: >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ for (x=0;x<NUM_INDIRECT_REGS;x++) >+ if( ! read_indreg_fxs(wc, port, x, ®s.indirect[x]) ) >+ return -EIO; >+ for (x=0;x<NUM_REGS;x++) >+ if( ! read_reg_fxs(wc, port, x, ®s.direct[x]) ) >+ return -EIO; >+ } else { >+ memset(®s, 0, sizeof(regs)); >+ for (x=0;x<NUM_FXO_REGS;x++){ >+ if( ! read_reg_fxo(wc, port, x, ®s.direct[x]) ) >+ return -EIO; >+ } >+ } >+ if (copy_to_user((struct wctdm_regs *)data, ®s, sizeof(regs))) >+ return -EFAULT; >+ break; >+ case WCTDM_SET_REG: >+ if (copy_from_user(®op, (struct wctdm_regop *)data, sizeof(regop))) >+ return -EFAULT; >+ if (regop.indirect) { >+ if (wc->porttype[port] != VT_PORT_PROSLIC) >+ return -EINVAL; >+ printk("Setting indirect %d to 0x%04x on %d\n", >+ regop.reg, regop.val, chan->chanpos); >+ if( ! write_indreg_fxs(wc, port, regop.reg, regop.val) ) >+ return -EIO; >+ } else { >+ regop.val &= 0xff; >+ printk("Setting direct %d to %04x on %d\n", >+ regop.reg, regop.val, chan->chanpos); >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ if( ! write_reg_fxs(wc, port, regop.reg, regop.val) ) >+ return -EIO; >+ } else { >+ if( ! write_reg_fxo(wc, port, regop.reg, regop.val) ) >+ return -EIO; >+ } >+ } >+ break; >+ case WCTDM_SET_ECHOTUNE: >+ cardinfo(wc->boardnum, "Setting echo registers"); >+ if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs))) >+ return -EFAULT; >+ >+ if (wc->porttype[port] == VT_PORT_VDAA) { >+ /* Set the ACIM and digital echo canceller registers */ >+ if( ! write_reg_fxo(wc, port, 30, echoregs.acim) >+ || ! write_reg_fxo(wc, port, 45, echoregs.coef1) >+ || ! write_reg_fxo(wc, port, 46, echoregs.coef2) >+ || ! write_reg_fxo(wc, port, 47, echoregs.coef3) >+ || ! write_reg_fxo(wc, port, 48, echoregs.coef4) >+ || ! write_reg_fxo(wc, port, 49, echoregs.coef5) >+ || ! write_reg_fxo(wc, port, 50, echoregs.coef6) >+ || ! write_reg_fxo(wc, port, 51, echoregs.coef7) >+ || ! write_reg_fxo(wc, port, 52, echoregs.coef8) ) >+ { >+ cardcrit(wc->boardnum, "Failed to set echo registers"); >+ return -EIO; >+ } >+ break; >+ } else { >+ return -EINVAL; >+ } >+ break; >+ default: >+ return -ENOTTY; >+ } >+ return 0; >+} //}}} >+ >+static int openpci_open(struct dahdi_chan *chan) >+{ >+ struct openpci *wc = chan->pvt; >+ if( ! wc->porttype[chan->chanpos-1] ) >+ return -ENODEV; >+ >+ //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE >+ // as the owner of the span that holds the pointer to this function, >+ // then bump the refcount in the dahdi code _BEFORE_ the potentially >+ // fatal call to an invalid pointer is made. >+ //if( wc->dead ) return -ENODEV; >+ //wc->usecount++; >+ try_module_get(THIS_MODULE); //XXX >+ >+ return 0; >+} >+ >+static int openpci_watchdog(struct dahdi_span *span, int event) >+{ >+ info("TDM: Restarting DMA"); >+ restart_dma(span->pvt); >+ return 0; >+} >+ >+static int openpci_close(struct dahdi_chan *chan) >+{ >+ struct openpci *wc = chan->pvt; >+ int port = chan->chanpos - 1; >+ >+ //XXX wc->usecount--; >+ //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE >+ // as the owner of the span that holds the pointer to this function, >+ // then bump the refcount in the dahdi code _BEFORE_ the potentially >+ // fatal call to an invalid pointer is made. >+ module_put(THIS_MODULE); >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 5; >+ else >+ wc->mod[port].fxs.idletxhookstate = 1; >+ } >+ /* If we're dead, release us now */ >+ //XXX if (!wc->usecount && wc->dead) openpci_release(wc); >+ >+ return 0; >+} >+ >+static int openpci_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) >+{ //{{{ >+ struct openpci *wc = chan->pvt; >+ int port = chan->chanpos - 1; >+ int new_hk_state; >+ >+ dbginfo(wc->boardnum, "Setting %s port %d hook state %s", >+ wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", >+ port, >+ txsig == 0 ? "ONHOOK" : >+ txsig == 1 ? "OFFHOOK" : >+ txsig == 2 ? "START" : >+ txsig == 3 ? "KEWL" : "UNKNOWN" ); >+ >+ switch(wc->porttype[port]) { >+ case VT_PORT_VDAA: >+ switch(txsig) { >+ case DAHDI_TXSIG_START: >+ case DAHDI_TXSIG_OFFHOOK: >+ if( write_reg_fxo(wc, port, 5, 0x9) >+ && write_reg_fxo(wc, port, 0x20, 0x0) ) >+ wc->mod[port].fxo.offhook = 1; >+ else >+ cardcrit(wc->boardnum, "Failed set fxo off-hook"); >+ break; >+ >+ case DAHDI_TXSIG_ONHOOK: >+ if( write_reg_fxo(wc, port, 5, 0x8) >+ && write_reg_fxo(wc, port, 0x20, 0x3) ) >+ wc->mod[port].fxo.offhook = 0; >+ else >+ cardcrit(wc->boardnum, "Failed set fxo on-hook"); >+ break; >+ >+ default: >+ cardcrit(wc->boardnum, >+ "Can't set FXO port %d tx state to %d", >+ port, txsig); >+ } >+ break; >+ >+ case VT_PORT_PROSLIC: >+ new_hk_state = wc->mod[port].fxs.lasttxhook; >+ switch(txsig) { >+ case DAHDI_TXSIG_ONHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ case DAHDI_SIG_FXOKS: >+ case DAHDI_SIG_FXOLS: >+ new_hk_state = wc->mod[port].fxs.idletxhookstate; >+ break; >+ case DAHDI_SIG_FXOGS: >+ new_hk_state = 3; >+ break; >+ } >+ break; >+ >+ case DAHDI_TXSIG_OFFHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ new_hk_state = 5; >+ break; >+ default: >+ new_hk_state = wc->mod[port].fxs.idletxhookstate; >+ break; >+ } >+ break; >+ >+ case DAHDI_TXSIG_START: >+ new_hk_state = 4; >+ break; >+ >+ case DAHDI_TXSIG_KEWL: >+ new_hk_state = 0; >+ break; >+ >+ default: >+ cardinfo(wc->boardnum, >+ "Can't set FXS port %d tx state to %d", >+ port, txsig); >+ } >+ dbginfo(wc->boardnum, "%s port %d hook state old %d, new %d", >+ wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", >+ port, wc->mod[port].fxs.lasttxhook, new_hk_state ); >+ >+ if (new_hk_state != wc->mod[port].fxs.lasttxhook){ >+ if( write_reg_fxs(wc, port, 64, new_hk_state) ) >+ wc->mod[port].fxs.lasttxhook = new_hk_state; >+ else >+ cardcrit(wc->boardnum, >+ "Failed to set port %d fxs hookstate from %d to %d", >+ port, wc->mod[port].fxs.lasttxhook, new_hk_state); >+ } >+ break; >+ >+ default: >+ cardcrit(wc->boardnum, >+ "Unknown module type %d in openpci_hooksig", >+ wc->porttype[port] ); >+ } >+ return 0; >+} //}}} >+ >+static int span_initialize(struct openpci *wc) >+{ //{{{ >+ int x; >+ >+ //XXX Set a THIS_MODULE as the owner of the span... >+ /* Zapata stuff */ >+ sprintf(wc->span.name, "WCTDM/%d", wc->boardnum); >+ sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->boardnum + 1); >+ for (x = 0; x < MAX_PORTS; x++) { >+ struct dahdi_chan *chan = &wc->_chans[x]; >+ wc->chans[x] = chan; >+ sprintf(chan->name, "WCTDM/%d/%d", wc->boardnum, x); >+ chan->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS >+ | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; >+ chan->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; >+ chan->chanpos = x+1; >+ chan->pvt = wc; >+ } >+ wc->span.deflaw = DAHDI_LAW_MULAW; >+ wc->span.chans = wc->chans; >+ wc->span.channels = MAX_PORTS; >+ wc->span.hooksig = openpci_hooksig; >+ wc->span.open = openpci_open; >+ wc->span.close = openpci_close; >+ wc->span.flags = DAHDI_FLAG_RBS; >+ wc->span.ioctl = openpci_ioctl; >+ wc->span.watchdog = openpci_watchdog; >+ init_waitqueue_head(&wc->span.maintq); >+ >+ wc->span.pvt = wc; >+ if (dahdi_register(&wc->span, 0)) { >+ cardcrit(wc->boardnum, "Unable to register span with dahdi"); >+ return RET_FAIL; >+ } >+ return RET_OK; >+} //}}} >+ >+static int get_port_type(struct openpci *wc, int port) >+{ //{{{ >+ int i, type; >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x20, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); >+ outb(port, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); >+ HRXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); type = inb(PIB(1)); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ return type; >+} //}}} >+ >+static int check_ports(struct openpci *wc) >+{ //{{{ >+ int i = 0; >+ >+ wc->portcount = 0; >+ for(; i < MAX_PORTS; ++i ){ >+ wc->porttype[i] = get_port_type(wc, i); >+ dbginfo(wc->boardnum,"%d: %s", i, porttype(wc,i)); >+ >+ switch( wc->porttype[i] ) { >+ case VT_PORT_PROSLIC: >+ /* By default, don't send on hook */ >+ if (reversepolarity) >+ wc->mod[i].fxs.idletxhookstate = 5; >+ else >+ wc->mod[i].fxs.idletxhookstate = 1; >+ >+ case VT_PORT_VDAA: >+ ++wc->portcount; >+ } >+ } >+ // we 'succeed' if any ports were discovered. >+ return wc->portcount ? RET_OK : RET_FAIL; >+} //}}} >+ >+static int configure_vdaa_country(struct openpci *wc, int port, char *name) >+{ //{{{ >+ unsigned char value; >+ int i; >+ >+ for (i=0; i < sizeof(fxo_modes)/sizeof(struct fxo_mode); ++i){ >+ if(!strcmp(fxo_modes[i].name, name)){ >+ dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); >+ goto part2; >+ } >+ } >+ i = 3; >+ cardinfo(wc->boardnum, "Using default country %s", fxo_modes[i].name); >+ >+ part2: >+ value = (fxo_modes[i].ohs << 6); >+ value |= (fxo_modes[i].rz << 1); >+ value |= (fxo_modes[i].rt << 0); >+ if( ! write_reg_fxo(wc, port, 16, value) ) goto hell; >+ >+ /* DC Termination Control - Register 26 */ >+ value = (fxo_modes[i].dcv << 6); >+ value |= (fxo_modes[i].mini << 4); >+ value |= (fxo_modes[i].ilim << 1); >+ if( ! write_reg_fxo(wc, port, 26, value) ) goto hell; >+ >+ /* AC Termination Control - Register 30 */ >+ value = (fxo_modes[i].acim << 0); >+ if( ! write_reg_fxo(wc, port, 30, value) ) goto hell; >+ >+ /* DAA Control 5 - Register 31 */ >+ msleep(1); >+ if( ! read_reg_fxo(wc, port, 31, &value) ) goto hell; >+ >+ value = (value & 0xf7) | (fxo_modes[i].ohs2 << 3); >+ value = value | 0x02; >+ if( ! write_reg_fxo(wc, port, 31, value) ) goto hell; >+ >+ return RET_OK; >+ >+ hell: >+ cardcrit(wc->boardnum, "port %d failed configure vdaa country", port); >+ return RET_FAIL; >+} //}}} >+ >+// Do not call this from an interrupt context, it may sleep. >+static void configure_vdaa_port(struct openpci *wc, int port) >+{ //{{{ >+ /* Set Country - default to Australia */ >+ if( configure_vdaa_country(wc, port, country) ) >+ ++wc->portcount; >+ else { >+ cardcrit(wc->boardnum, "FAILED to configure vdaa port %d. Disabled.", port); >+ wc->porttype[port] = VT_PORT_EMPTY; >+ } >+} //}}} >+ >+static int configure_proslic_country(struct openpci *wc, int port, const char *name) >+{ //{{{ >+ int i; >+ >+ for(i=0; i < sizeof(ps_country_regs)/sizeof(struct ps_country_reg); ++i) { >+ if(!strcmp(ps_country_regs[i].country, name)){ >+ dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); >+ goto part2; >+ } >+ } >+ return -EINVAL; >+ >+ part2: >+ >+ if( ! write_reg_fxs(wc, port, 10, ps_country_regs[i].value) ){ >+ cardcrit(wc->boardnum,"%d: failed to write PS_IMPEDANCE", port); >+ return -EIO; >+ } >+ return 0; >+} //}}} >+ >+// Do not call this from an interrupt context, it may sleep. >+static void configure_proslic_port(struct openpci *wc, int port) >+{ //{{{ >+ /* Set Country - default to Australia */ >+ switch( configure_proslic_country(wc, port, country) ){ >+ case 0: >+ break; >+ >+ case -EINVAL: >+ cardwarn(wc->boardnum,"%d: Country '%s' unknown, using default", port, country); >+ if( configure_proslic_country(wc, port, DEFAULT_COUNTRY) == 0 ) >+ goto hell; >+ >+ default: >+ goto hell; >+ } >+ >+ ++wc->portcount; >+ return; >+ >+ hell: >+ cardcrit(wc->boardnum, "FAILED to configure proslic port %d. Disabled.", port); >+ wc->porttype[port] = VT_PORT_EMPTY; >+} //}}} >+ >+// Do not call this from an interrupt context, it may (indirectly) sleep. >+static int configure_ports(struct openpci *wc) >+{ //{{{ >+ unsigned long flags; >+ int i; >+ >+ wc->portcount = 0; >+ for(i=0; i < MAX_PORTS; ++i){ >+ switch (wc->porttype[i]){ >+ case VT_PORT_VDAA: configure_vdaa_port(wc,i); break; >+ case VT_PORT_PROSLIC: configure_proslic_port(wc,i); break; >+ } >+ } >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x2c, PIB(1)); HTXF_WAIT_LOCKED(); >+ outb(0xff, PIB(1)); HTXF_WAIT_LOCKED(); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ // otherwise we 'succeed' if any ports were configured successfully. >+ return wc->portcount ? RET_OK : RET_FAIL; >+} //}}} >+ >+static int __get_arm_id(struct openpci *wc, int field, char *value) >+{ //{{{ >+ int i; >+ int x=0; >+ int count=0; >+ >+ outb(0x01, PIB(1)); HTXF_WAIT(); >+ outb(field, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); count = inb(PIB(1)); >+ if (count > ID_DATA_MAXSIZE){ >+ cardcrit(wc->boardnum, "Too many bytes of id(%d) data %d/%d", >+ field, count, ID_DATA_MAXSIZE); >+ return RET_FAIL; >+ } >+ //cardinfo(wc->boardnum, "get_arm_id(%d): byte count %d",field,count); >+ for(; x < count; ++x){ >+ HRXF_WAIT(); *value = inb(PIB(1)); >+ //cardinfo(wc->boardnum, "get_arm_id(%d): byte %d => 0x%02x",field,x,tmp); >+ ++value; >+ } >+ return RET_OK; >+} //}}} >+ >+static void enable_interrupts(struct openpci *wc) >+{ //{{{ >+ outb(0x3f, TJ_MASK0); >+ outb(0x02, TJ_MASK1); >+} //}}} >+ >+static void disable_interrupts(struct openpci *wc) >+{ //{{{ >+ outb(0x00, TJ_MASK0); >+ outb(0x00, TJ_MASK1); >+} //}}} >+ >+// Do not call this from an interrupt context, it may sleep. >+static int check_arm(struct openpci *wc) >+{ //{{{ >+ char model[ID_DATA_MAXSIZE+1] = { 0 }; >+ char date[ID_DATA_MAXSIZE+1] = { 0 }; >+ unsigned long flags; >+ int i=0; >+ int tmp=0; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ while ((tmp != 0x88)&&(++i<100)){ >+ outb(0x88, PIB(0)); >+ msleep(1); >+ tmp = inb(PIB(1)); >+ } >+ if (i>=1000) goto limbo; >+ dbginfo(wc->boardnum, "Arm responded on attempt %d",i); >+ >+ // Flush out the queue if we sent several pings before a response. >+ if(i>1) __ping_arm(wc); >+ >+ if( ! __get_arm_id(wc, 0, model) ) goto hell; >+ sscanf(model, "OpenPCI8.%02d", &(wc->firmware)); >+ cardinfo(wc->boardnum, " model: %s", model); >+ >+ if( ! __get_arm_id(wc, 1, date) ) goto hell; >+ cardinfo(wc->boardnum, " date: %s", date); >+ >+ if( ! __get_arm_id(wc, 2, wc->serial) ) goto hell; >+ cardinfo(wc->boardnum, " serial: %s", wc->serial); >+ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ >+ hell: >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardwarn(wc->boardnum, "Found ARM processor, dumb firmware."); >+ return RET_OK; >+ >+ limbo: >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_FAIL; >+} //}}} >+ >+static int arm_monitor(struct openpci *wc, int on) >+{ //{{{ >+ int i; >+ outb( on ? 0x06 : 0x07, PIB(1) ); HTXF_WAIT(); >+ return RET_OK; >+} //}}} >+ >+static int __devinit openpci_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent) >+{ //{{{ >+ struct openpci *wc; >+ int boardnum = 0; >+ int failret = -ENOMEM; >+ int tmp = 0; >+ int i; >+ unsigned long flags; >+ >+ if( ent->driver_data != (kernel_ulong_t)&wcopenpci ) >+ { >+ info("Probe of non-OpenPCI card, ignoring."); >+ return -EINVAL; >+ } >+ wc = kzalloc(sizeof(struct openpci), GFP_KERNEL); >+ if (!wc){ >+ return -ENOMEM; >+ } >+ >+ mutex_lock(&cards_mutex); >+ for (; boardnum < MAX_CARDS && cards[boardnum]; ++boardnum); >+ if (boardnum >= MAX_CARDS){ >+ crit("Too many OpenPCI cards(%d), max is %d.", boardnum, MAX_CARDS); >+ mutex_unlock(&cards_mutex); >+ goto hell; >+ } >+ cards[boardnum] = wc; >+ mutex_unlock(&cards_mutex); >+ >+ spin_lock_init(&wc->lock); >+ pci_set_drvdata(pdev, wc); >+ >+ wc->boardnum = boardnum; >+ wc->dev = pdev; >+ wc->variety = wcopenpci; >+ >+ cardinfo(boardnum, "Initialising card"); >+ if (pci_enable_device(pdev)) { >+ failret = -EIO; >+ goto hell_2; >+ } >+ wc->ioaddr = pci_resource_start(pdev, 0); >+ if( ! request_region(wc->ioaddr, 0xff, NAME) ){ >+ cardcrit(boardnum, "Failed to lock IO region, another driver already using it"); >+ failret = -EBUSY; >+ goto hell_2; >+ } >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0xff, TJ_AUXD); /* Set up TJ to access the ARM */ >+ outb(0x78, TJ_AUXC); /* Set up for Jtag */ >+ outb(0x00, TJ_CNTL); /* pull ERST low */ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ msleep(1); /* Wait a bit */ >+ >+ dbginfo(boardnum,"Starting ARM"); >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x01, TJ_CNTL); /* pull ERST high again */ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ msleep(100); /* Give it all a chance to boot */ >+ >+ if( ! check_arm(wc) ){ >+ cardcrit(boardnum, "Couldnt find ARM processor"); >+ failret = -EIO; >+ goto hell_3; >+ } >+ if( wc->firmware < 11 ){ >+ cardcrit(boardnum, >+ "Firmware version %d not supported by this driver", >+ wc->firmware); >+ cardcrit(boardnum, " contact Voicetronix to have it updated"); >+ failret = -ENODEV; >+ goto hell_3; >+ } >+ if( ! check_ports(wc) ){ >+ cardcrit(boardnum, "Couldnt find ports!"); >+ failret = -EIO; >+ goto hell_3; >+ } >+ >+ wc->writechunk = pci_alloc_consistent(pdev, VT_PCIDMA_BLOCKSIZE, &wc->writedma); >+ if (!wc->writechunk) { >+ cardcrit(boardnum, "Couldnt get DMA memory."); >+ goto hell_3; >+ } >+ wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int)); >+ wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2); >+ >+ memset((void*)wc->writechunk,0,VT_PCIDMA_BLOCKSIZE); >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0xc1, TJ_SERCTL); >+ outb(0x0, TJ_FSCDELAY); >+ >+ outl(wc->writedma, TJ_DMAWS); >+ outl(wc->writedma + VT_PCIDMA_MIDDLE, TJ_DMAWI); >+ outl(wc->writedma + VT_PCIDMA_END, TJ_DMAWE); >+ outl(wc->readdma, TJ_DMARS); >+ outl(wc->readdma + VT_PCIDMA_MIDDLE, TJ_DMARI); >+ outl(wc->readdma + VT_PCIDMA_END, TJ_DMARE); >+ >+ /* Clear interrupts */ >+ outb(0xff, TJ_INTSTAT); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ if( ! arm_monitor(wc, 1) ){ >+ cardcrit(boardnum, "failed to start arm monitoring"); >+ failret = -EIO; >+ goto hell_4; >+ } >+ msleep(1000); >+ >+ i = 0; >+ while(tmp != 0x88 && ++i < 1000) { >+ outb(0x88, PIB(0)); >+ msleep(250); >+ tmp = inb(PIB(1)); >+ } >+ if(i>=1000) { >+ cardcrit(boardnum, "FAILED to initialise board"); >+ goto hell_4; >+ } >+ >+ if( ! check_ports(wc) ) { >+ cardcrit(boardnum, "FAILED to initialise ports"); >+ failret = -EIO; >+ goto hell_4; >+ } >+ if( ! configure_ports(wc) ){ >+ cardcrit(boardnum, "Failed to configure ports."); >+ failret = -EIO; >+ goto hell_4; >+ } >+ cardinfo(wc->boardnum, "have %d configured ports", wc->portcount); >+ >+ if( ! span_initialize(wc) ) { >+ cardcrit(boardnum, "Failed to register with dahdi driver"); >+ failret = -EFAULT; >+ goto hell_4; >+ } >+ >+ /* Finalize signalling */ >+ for (i=0; i < MAX_PORTS; ++i) { >+ if (wc->porttype[i] == VT_PORT_VDAA) >+ wc->chans[i]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS >+ | DAHDI_SIG_CLEAR | DAHDI_SIG_SF; >+ else if (wc->porttype[i] == VT_PORT_PROSLIC) >+ wc->chans[i]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS >+ | DAHDI_SIG_FXOGS | DAHDI_SIG_SF >+ | DAHDI_SIG_CLEAR | DAHDI_SIG_EM; >+ else if (wc->porttype[i]) >+ cardcrit(wc->boardnum, "Port %d has unknown type (%d)", >+ i, wc->porttype[i]); >+ } >+ >+ /* Enable bus mastering */ >+ pci_set_master(pdev); >+ >+ if (request_irq(pdev->irq, openpci_isr, DAHDI_IRQ_SHARED, NAME, wc)) { >+ cardcrit(boardnum, "Cant get IRQ!"); >+ failret = -EIO; >+ goto hell_5; >+ } >+ cardinfo(boardnum, "Got IRQ %d", pdev->irq); >+ >+ enable_interrupts(wc); >+ start_dma(wc); >+ >+ cardinfo(boardnum,"Initialised card."); >+ return 0; >+ >+ hell_5: >+ dahdi_unregister(&wc->span); >+ hell_4: >+ if (wc->writechunk){ >+ pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, >+ (void*)wc->writechunk, wc->writedma); >+ } >+ hell_3: >+ outb(0x00, TJ_CNTL); >+ release_region(wc->ioaddr, 0xff); >+ hell_2: >+ cards[boardnum] = NULL; >+ hell: >+ kfree(wc); >+ return failret; >+} //}}} >+ >+static void __devexit openpci_remove_board(struct pci_dev *pdev) >+{ //{{{ >+ struct openpci *wc = pci_get_drvdata(pdev); >+ >+ if(!wc) return; >+ >+ arm_monitor(wc,0); >+ >+ /* Stop DMA */ >+ outb(0x00, TJ_OPER); >+ disable_interrupts(wc); >+ >+ //XXX Replace this usecount business... >+ // and do this BEFORE we invalidate everything above... >+ // check that we wont try to write to it in the meantime. >+ /* Release span, possibly delayed */ >+ //XXX if (!wc->usecount) openpci_release(wc); else wc->dead = 1; >+ >+ dahdi_unregister(&wc->span); >+ outb(0x00, TJ_CNTL); >+ >+ pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, (void *)wc->writechunk, wc->writedma); >+ free_irq(pdev->irq, wc); >+ >+ release_region(wc->ioaddr, 0xff); >+ >+ mutex_lock(&cards_mutex); >+ cards[wc->boardnum] = NULL; >+ mutex_unlock(&cards_mutex); >+ >+ kfree(wc); >+ cardinfo(wc->boardnum, "Removed OpenPCI card."); >+} //}}} >+ >+static struct pci_device_id openpci_pci_tbl[] = { >+ { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t) &wcopenpci }, >+ { 0 } >+}; >+ >+MODULE_DEVICE_TABLE(pci, openpci_pci_tbl); >+ >+static struct pci_driver openpci_driver = { >+ name: NAME, >+ probe: openpci_probe_board, >+ remove: __devexit_p(openpci_remove_board), >+ suspend: NULL, >+ resume: NULL, >+ id_table: openpci_pci_tbl, >+}; >+ >+static int __init openpci_init(void) >+{ >+ if( dahdi_pci_module(&openpci_driver) ) >+ return -ENODEV; >+ >+ info("Module loaded %s", debug ? "with debug enabled" : ""); >+ return 0; >+} >+ >+static void __exit openpci_cleanup(void) >+{ >+ pci_unregister_driver(&openpci_driver); >+ info("Module exit"); >+} >+ >+module_init(openpci_init); >+module_exit(openpci_cleanup); >+ >+MODULE_DESCRIPTION(DRIVER_DESCRIPTION); >+MODULE_AUTHOR(DRIVER_AUTHOR); >+MODULE_VERSION(DAHDI_VERSION); >+MODULE_LICENSE("GPL"); >+ >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/base.c dahdi-svn-new/drivers/dahdi/zaphfc/base.c >--- dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/base.c 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/zaphfc/base.c 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,1706 @@ >+/* >+ * zaphfc.c - Dahdi driver for HFC-S PCI A based ISDN BRI cards >+ * >+ * Dahdi rewrite in hardhdlc mode >+ * Jose A. Deniz <odicha@hotmail.com> >+ * >+ * Copyright (C) 2009, Jose A. Deniz >+ * Copyright (C) 2006, headiisue GmbH; Jens Wilke >+ * Copyright (C) 2004 Daniele Orlandi >+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH >+ * >+ * Jens Wilke <jw_vzaphfc@headissue.com> >+ * >+ * Original author of this code is >+ * Daniele "Vihai" Orlandi <daniele@orlandi.com> >+ * >+ * Major rewrite of the driver made by >+ * Klaus-Peter Junghanns <kpj@junghanns.net> >+ * >+ * This program is free software and may be modified and >+ * distributed under the terms of the GNU Public License. >+ * >+ * Please read the README file for important infos. >+ */ >+ >+#include <linux/spinlock.h> >+#include <linux/init.h> >+#include <linux/pci.h> >+#include <linux/interrupt.h> >+#include <linux/module.h> >+#include <linux/moduleparam.h> >+#include <linux/version.h> >+#include <linux/kernel.h> >+#include <linux/delay.h> >+#include <linux/proc_fs.h> >+#include <linux/if_arp.h> >+#include <linux/sched.h> >+ >+#include <dahdi/kernel.h> >+ >+#include "zaphfc.h" >+#include "fifo.h" >+ >+#if CONFIG_PCI >+ >+#define DAHDI_B1 0 >+#define DAHDI_B2 1 >+#define DAHDI_D 2 >+ >+#define D 0 >+#define B1 1 >+#define B2 2 >+ >+/* >+ * Mode Te for all >+ */ >+static int modes; >+static int nt_modes[hfc_MAX_BOARDS]; >+static int nt_modes_count; >+static int force_l1_up; >+static struct proc_dir_entry *hfc_proc_zaphfc_dir; >+ >+#ifdef DEBUG >+int debug_level; >+#endif >+ >+#ifndef FALSE >+#define FALSE 0 >+#endif >+#ifndef TRUE >+#define TRUE (!FALSE) >+#endif >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) >+#define SET_PROC_DIRENTRY_OWNER(p) do { (p)->owner = THIS_MODULE; } while(0); >+#else >+#define SET_PROC_DIRENTRY_OWNER(p) do { } while(0); >+#endif >+ >+static struct pci_device_id hfc_pci_ids[] = { >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {0,} >+}; >+ >+MODULE_DEVICE_TABLE(pci, hfc_pci_ids); >+ >+static int __devinit hfc_probe(struct pci_dev *dev >+ , const struct pci_device_id *ent); >+static void __devexit hfc_remove(struct pci_dev *dev); >+ >+static struct pci_driver hfc_driver = { >+ .name = hfc_DRIVER_NAME, >+ .id_table = hfc_pci_ids, >+ .probe = hfc_probe, >+ .remove = hfc_remove, >+}; >+ >+/****************************************** >+ * HW routines >+ ******************************************/ >+ >+static void hfc_softreset(struct hfc_card *card) >+{ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "resetting\n", >+ card->cardnum); >+ >+/* >+ * Softreset procedure. Put it on, wait and off again >+ */ >+ hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); >+ udelay(6); >+ hfc_outb(card, hfc_CIRM, 0); >+ >+ set_current_state(TASK_UNINTERRUPTIBLE); >+ schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); >+} >+ >+static void hfc_resetCard(struct hfc_card *card) >+{ >+ card->regs.m1 = 0; >+ hfc_outb(card, hfc_INT_M1, card->regs.m1); >+ >+ card->regs.m2 = 0; >+ hfc_outb(card, hfc_INT_M2, card->regs.m2); >+ >+ hfc_softreset(card); >+ >+ card->regs.trm = 0; >+ hfc_outb(card, hfc_TRM, card->regs.trm); >+ >+ /* >+ * Select the non-capacitive line mode for the S/T interface >+ */ >+ card->regs.sctrl = hfc_SCTRL_NONE_CAP; >+ >+ if (card->nt_mode) { >+ /* >+ * ST-Bit delay for NT-Mode >+ */ >+ hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_NT); >+ >+ card->regs.sctrl |= hfc_SCTRL_MODE_NT; >+ } else { >+ /* >+ * ST-Bit delay for TE-Mode >+ */ >+ hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_TE); >+ >+ card->regs.sctrl |= hfc_SCTRL_MODE_TE; >+ } >+ >+ hfc_outb(card, hfc_SCTRL, card->regs.sctrl); >+ >+ /* >+ * S/T Auto awake >+ */ >+ card->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE; >+ hfc_outb(card, hfc_SCTRL_E, card->regs.sctrl_e); >+ >+ /* >+ * No B-channel enabled at startup >+ */ >+ card->regs.sctrl_r = 0; >+ hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); >+ >+ /* >+ * HFC Master Mode >+ */ >+ hfc_outb(card, hfc_MST_MODE, hfc_MST_MODE_MASTER); >+ >+ /* >+ * Connect internal blocks >+ */ >+ card->regs.connect = >+ hfc_CONNECT_B1_HFC_from_ST | >+ hfc_CONNECT_B1_ST_from_HFC | >+ hfc_CONNECT_B1_GCI_from_HFC | >+ hfc_CONNECT_B2_HFC_from_ST | >+ hfc_CONNECT_B2_ST_from_HFC | >+ hfc_CONNECT_B2_GCI_from_HFC; >+ hfc_outb(card, hfc_CONNECT, card->regs.connect); >+ >+ /* >+ * All bchans are HDLC by default, not useful, actually >+ * since mode is set during open() >+ */ >+ hfc_outb(card, hfc_CTMT, 0); >+ >+ /* >+ * bit order >+ */ >+ hfc_outb(card, hfc_CIRM, 0); >+ >+ /* >+ * Enable D-rx FIFO. At least one FIFO must be enabled (by specs) >+ */ >+ card->regs.fifo_en = hfc_FIFOEN_DRX; >+ hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); >+ >+ card->late_irqs = 0; >+ >+ /* >+ * Clear already pending ints >+ */ >+ hfc_inb(card, hfc_INT_S1); >+ hfc_inb(card, hfc_INT_S2); >+ >+ /* >+ * Enable IRQ output >+ */ >+ card->regs.m1 = hfc_INTS_DREC | hfc_INTS_L1STATE | hfc_INTS_TIMER; >+ hfc_outb(card, hfc_INT_M1, card->regs.m1); >+ >+ card->regs.m2 = hfc_M2_IRQ_ENABLE; >+ hfc_outb(card, hfc_INT_M2, card->regs.m2); >+ >+ /* >+ * Unlocks the states machine >+ */ >+ hfc_outb(card, hfc_STATES, 0); >+ >+ /* >+ * There's no need to explicitly activate L1 now. >+ * Activation is managed inside the interrupt routine. >+ */ >+} >+ >+static void hfc_update_fifo_state(struct hfc_card *card) >+{ >+ /* >+ * I'm not sure if irqsave is needed but there could be a race >+ * condition since hfc_update_fifo_state could be called from >+ * both the IRQ handler and the *_(open|close) functions >+ */ >+ >+ unsigned long flags; >+ spin_lock_irqsave(&card->chans[B1].lock, flags); >+ if (!card->fifo_suspended && >+ (card->chans[B1].status == open_framed || >+ card->chans[B1].status == open_voice)) { >+ >+ if (!(card->regs.fifo_en & hfc_FIFOEN_B1RX)) { >+ card->regs.fifo_en |= hfc_FIFOEN_B1RX; >+ hfc_clear_fifo_rx(&card->chans[B1].rx); >+ } >+ >+ if (!(card->regs.fifo_en & hfc_FIFOEN_B1TX)) { >+ card->regs.fifo_en |= hfc_FIFOEN_B1TX; >+ hfc_clear_fifo_tx(&card->chans[B1].tx); >+ } >+ } else { >+ if (card->regs.fifo_en & hfc_FIFOEN_B1RX) >+ card->regs.fifo_en &= ~hfc_FIFOEN_B1RX; >+ if (card->regs.fifo_en & hfc_FIFOEN_B1TX) >+ card->regs.fifo_en &= ~hfc_FIFOEN_B1TX; >+ } >+ spin_unlock_irqrestore(&card->chans[B1].lock, flags); >+ >+ spin_lock_irqsave(&card->chans[B2].lock, flags); >+ if (!card->fifo_suspended && >+ (card->chans[B2].status == open_framed || >+ card->chans[B2].status == open_voice || >+ card->chans[B2].status == sniff_aux)) { >+ >+ if (!(card->regs.fifo_en & hfc_FIFOEN_B2RX)) { >+ card->regs.fifo_en |= hfc_FIFOEN_B2RX; >+ hfc_clear_fifo_rx(&card->chans[B2].rx); >+ } >+ >+ if (!(card->regs.fifo_en & hfc_FIFOEN_B2TX)) { >+ card->regs.fifo_en |= hfc_FIFOEN_B2TX; >+ hfc_clear_fifo_tx(&card->chans[B2].tx); >+ } >+ } else { >+ if (card->regs.fifo_en & hfc_FIFOEN_B2RX) >+ card->regs.fifo_en &= ~hfc_FIFOEN_B2RX; >+ if (card->regs.fifo_en & hfc_FIFOEN_B2TX) >+ card->regs.fifo_en &= ~hfc_FIFOEN_B2TX; >+ } >+ spin_unlock_irqrestore(&card->chans[B2].lock, flags); >+ >+ spin_lock_irqsave(&card->chans[D].lock, flags); >+ if (!card->fifo_suspended && >+ card->chans[D].status == open_framed) { >+ >+ if (!(card->regs.fifo_en & hfc_FIFOEN_DTX)) { >+ card->regs.fifo_en |= hfc_FIFOEN_DTX; >+ >+ card->chans[D].tx.ugly_framebuf_size = 0; >+ card->chans[D].tx.ugly_framebuf_off = 0; >+ } >+ } else { >+ if (card->regs.fifo_en & hfc_FIFOEN_DTX) >+ card->regs.fifo_en &= ~hfc_FIFOEN_DTX; >+ } >+ spin_unlock_irqrestore(&card->chans[D].lock, flags); >+ >+ hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); >+} >+ >+static inline void hfc_suspend_fifo(struct hfc_card *card) >+{ >+ card->fifo_suspended = TRUE; >+ >+ hfc_update_fifo_state(card); >+ >+ /* >+ * When L1 goes down D rx receives garbage; it is nice to >+ * clear it to avoid a CRC error on reactivation >+ * udelay is needed because the FIFO deactivation happens >+ * in 250us >+ */ >+ udelay(250); >+ hfc_clear_fifo_rx(&card->chans[D].rx); >+ >+#ifdef DEBUG >+ if (debug_level >= 3) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "FIFOs suspended\n", >+ card->cardnum); >+ } >+#endif >+} >+ >+static inline void hfc_resume_fifo(struct hfc_card *card) >+{ >+ card->fifo_suspended = FALSE; >+ >+ hfc_update_fifo_state(card); >+ >+#ifdef DEBUG >+ if (debug_level >= 3) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "FIFOs resumed\n", >+ card->cardnum); >+ } >+#endif >+} >+ >+static void hfc_check_l1_up(struct hfc_card *card) >+{ >+ if ((!card->nt_mode && card->l1_state != 7) >+ || (card->nt_mode && card->l1_state != 3)) { >+ >+ hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | >+ hfc_STATES_ACTIVATE| >+ hfc_STATES_NT_G2_G3); >+ >+ /* >+ * 0 because this is quite verbose when an inferface is unconnected, jaw >+ */ >+#if 0 >+ if (debug_level >= 1) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "L1 is down, bringing up L1.\n", >+ card->cardnum); >+ } >+#endif >+ } >+} >+ >+ >+/******************* >+ * Dahdi interface * >+ *******************/ >+ >+static int hfc_zap_open(struct dahdi_chan *zaptel_chan) >+{ >+ struct hfc_chan_duplex *chan = zaptel_chan->pvt; >+ struct hfc_card *card = chan->card; >+ >+ spin_lock(&chan->lock); >+ >+ switch (chan->number) { >+ case D: >+ if (chan->status != free && >+ chan->status != open_framed) { >+ spin_unlock(&chan->lock); >+ return -EBUSY; >+ } >+ chan->status = open_framed; >+ break; >+ >+ case B1: >+ case B2: >+ if (chan->status != free) { >+ spin_unlock(&chan->lock); >+ return -EBUSY; >+ } >+ chan->status = open_voice; >+ break; >+ } >+ >+ chan->open_by_zaptel = TRUE; >+ try_module_get(THIS_MODULE); >+ spin_unlock(&chan->lock); >+ >+ switch (chan->number) { >+ case D: >+ break; >+ >+ case B1: >+ card->regs.m2 |= hfc_M2_PROC_TRANS; >+ /* >+ * Enable transparent mode >+ */ >+ card->regs.ctmt |= hfc_CTMT_TRANSB1; >+ /* >+ * Reversed bit order >+ */ >+ card->regs.cirm |= hfc_CIRM_B1_REV; >+ /* >+ * Enable transmission >+ */ >+ card->regs.sctrl |= hfc_SCTRL_B1_ENA; >+ /* >+ * Enable reception >+ */ >+ card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; >+ break; >+ >+ case B2: >+ card->regs.m2 |= hfc_M2_PROC_TRANS; >+ card->regs.ctmt |= hfc_CTMT_TRANSB2; >+ card->regs.cirm |= hfc_CIRM_B2_REV; >+ card->regs.sctrl |= hfc_SCTRL_B2_ENA; >+ card->regs.sctrl_r |= hfc_SCTRL_R_B2_ENA; >+ break; >+ >+ } >+ >+ /* >+ * If not already enabled, enable processing transition (8KHz) >+ * interrupt >+ */ >+ hfc_outb(card, hfc_INT_M2, card->regs.m2); >+ hfc_outb(card, hfc_CTMT, card->regs.ctmt); >+ hfc_outb(card, hfc_CIRM, card->regs.cirm); >+ hfc_outb(card, hfc_SCTRL, card->regs.sctrl); >+ hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); >+ >+ hfc_update_fifo_state(card); >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s opened as %s.\n", >+ card->cardnum, >+ chan->name, >+ zaptel_chan->name); >+ >+ return 0; >+} >+ >+static int hfc_zap_close(struct dahdi_chan *zaptel_chan) >+{ >+ struct hfc_chan_duplex *chan = zaptel_chan->pvt; >+ struct hfc_card *card = chan->card; >+ >+ if (!card) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "hfc_zap_close called with NULL card\n"); >+ return -1; >+ } >+ >+ spin_lock(&chan->lock); >+ >+ if (chan->status == free) { >+ spin_unlock(&chan->lock); >+ return -EINVAL; >+ } >+ >+ chan->status = free; >+ chan->open_by_zaptel = FALSE; >+ >+ spin_unlock(&chan->lock); >+ >+ switch (chan->number) { >+ case D: >+ break; >+ >+ case B1: >+ card->regs.ctmt &= ~hfc_CTMT_TRANSB1; >+ card->regs.cirm &= ~hfc_CIRM_B1_REV; >+ card->regs.sctrl &= ~hfc_SCTRL_B1_ENA; >+ card->regs.sctrl_r &= ~hfc_SCTRL_R_B1_ENA; >+ break; >+ >+ case B2: >+ card->regs.ctmt &= ~hfc_CTMT_TRANSB2; >+ card->regs.cirm &= ~hfc_CIRM_B2_REV; >+ card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; >+ card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; >+ break; >+ } >+ >+ if (card->chans[B1].status == free && >+ card->chans[B2].status == free) >+ card->regs.m2 &= ~hfc_M2_PROC_TRANS; >+ >+ hfc_outb(card, hfc_INT_M2, card->regs.m2); >+ hfc_outb(card, hfc_CTMT, card->regs.ctmt); >+ hfc_outb(card, hfc_CIRM, card->regs.cirm); >+ hfc_outb(card, hfc_SCTRL, card->regs.sctrl); >+ hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); >+ >+ hfc_update_fifo_state(card); >+ >+ module_put(THIS_MODULE); >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s closed as %s.\n", >+ card->cardnum, >+ chan->name, >+ zaptel_chan->name); >+ >+ return 0; >+} >+ >+static int hfc_zap_rbsbits(struct dahdi_chan *chan, int bits) >+{ >+ return 0; >+} >+ >+static int hfc_zap_ioctl(struct dahdi_chan *chan, >+ unsigned int cmd, unsigned long data) >+{ >+ switch (cmd) { >+ >+ default: >+ return -ENOTTY; >+ } >+ >+ return 0; >+} >+ >+static void hfc_hdlc_hard_xmit(struct dahdi_chan *d_chan) >+{ >+ struct hfc_chan_duplex *chan = d_chan->pvt; >+ struct hfc_card *card = chan->card; >+ struct dahdi_hfc *hfccard = card->ztdev; >+ >+ atomic_inc(&hfccard->hdlc_pending); >+ >+} >+ >+static int hfc_zap_startup(struct dahdi_span *span) >+{ >+ struct dahdi_hfc *zthfc = span->pvt; >+ struct hfc_card *hfctmp = zthfc->card; >+ int alreadyrunning; >+ >+ if (!hfctmp) { >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "no card for span at startup!\n", >+ hfctmp->cardnum); >+ } >+ >+ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; >+ >+ if (!alreadyrunning) >+ span->flags |= DAHDI_FLAG_RUNNING; >+ >+ return 0; >+} >+ >+static int hfc_zap_shutdown(struct dahdi_span *span) >+{ >+ return 0; >+} >+ >+static int hfc_zap_maint(struct dahdi_span *span, int cmd) >+{ >+ return 0; >+} >+ >+static int hfc_zap_chanconfig(struct dahdi_chan *d_chan, int sigtype) >+{ >+ struct hfc_chan_duplex *chan = d_chan->pvt; >+ struct hfc_card *card = chan->card; >+ struct dahdi_hfc *hfccard = card->ztdev; >+ >+ if ((sigtype == DAHDI_SIG_HARDHDLC) && (hfccard->sigchan == d_chan)) { >+ hfccard->sigactive = 0; >+ atomic_set(&hfccard->hdlc_pending, 0); >+ } >+ >+ return 0; >+} >+ >+static int hfc_zap_spanconfig(struct dahdi_span *span, >+ struct dahdi_lineconfig *lc) >+{ >+ span->lineconfig = lc->lineconfig; >+ >+ return 0; >+} >+ >+static int hfc_zap_initialize(struct dahdi_hfc *hfccard) >+{ >+ struct hfc_card *hfctmp = hfccard->card; >+ int i; >+ >+ memset(&hfccard->span, 0x0, sizeof(struct dahdi_span)); >+ sprintf(hfccard->span.name, "ZTHFC%d", hfctmp->cardnum + 1); >+ sprintf(hfccard->span.desc, >+ "HFC-S PCI A ISDN card %d [%s] ", >+ hfctmp->cardnum, >+ hfctmp->nt_mode ? "NT" : "TE"); >+ hfccard->span.spantype = hfctmp->nt_mode ? "NT" : "TE"; >+ hfccard->span.manufacturer = "Cologne Chips"; >+ hfccard->span.spanconfig = hfc_zap_spanconfig; >+ hfccard->span.chanconfig = hfc_zap_chanconfig; >+ hfccard->span.startup = hfc_zap_startup; >+ hfccard->span.shutdown = hfc_zap_shutdown; >+ hfccard->span.maint = hfc_zap_maint; >+ hfccard->span.rbsbits = hfc_zap_rbsbits; >+ hfccard->span.open = hfc_zap_open; >+ hfccard->span.close = hfc_zap_close; >+ hfccard->span.ioctl = hfc_zap_ioctl; >+ hfccard->span.hdlc_hard_xmit = hfc_hdlc_hard_xmit; >+ hfccard->span.flags = 0; >+ hfccard->span.irq = hfctmp->pcidev->irq; >+ dahdi_copy_string(hfccard->span.devicetype, "HFC-S PCI-A ISDN", >+ sizeof(hfccard->span.devicetype)); >+ sprintf(hfccard->span.location, "PCI Bus %02d Slot %02d", >+ hfctmp->pcidev->bus->number, >+ PCI_SLOT(hfctmp->pcidev->devfn) + 1); >+ hfccard->span.chans = hfccard->_chans; >+ hfccard->span.channels = 3; >+ for (i = 0; i < hfccard->span.channels; i++) >+ hfccard->_chans[i] = &hfccard->chans[i]; >+ hfccard->span.deflaw = DAHDI_LAW_ALAW; >+ hfccard->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; >+ hfccard->span.offset = 0; >+ init_waitqueue_head(&hfccard->span.maintq); >+ hfccard->span.pvt = hfccard; >+ >+ for (i = 0; i < hfccard->span.channels; i++) { >+ memset(&hfccard->chans[i], 0x0, sizeof(struct dahdi_chan)); >+ >+ sprintf(hfccard->chans[i].name, >+ "ZTHFC%d/%d/%d", >+ hfctmp->cardnum + 1, 0, i + 1); >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "registered %s\n", >+ hfctmp->cardnum, >+ hfccard->chans[i].name); >+ >+ if (i == hfccard->span.channels - 1) { >+ hfccard->chans[i].sigcap = DAHDI_SIG_HARDHDLC; >+ hfccard->sigchan = &hfccard->chans[D]; >+ hfccard->sigactive = 0; >+ atomic_set(&hfccard->hdlc_pending, 0); >+ } else { >+ hfccard->chans[i].sigcap = >+ DAHDI_SIG_CLEAR | DAHDI_SIG_DACS; >+ } >+ >+ hfccard->chans[i].chanpos = i + 1; >+ } >+ >+ hfccard->chans[DAHDI_D].readchunk = >+ hfctmp->chans[D].rx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_D].writechunk = >+ hfctmp->chans[D].tx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D]; >+ >+ hfccard->chans[DAHDI_B1].readchunk = >+ hfctmp->chans[B1].rx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B1].writechunk = >+ hfctmp->chans[B1].tx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1]; >+ >+ hfccard->chans[DAHDI_B2].readchunk = >+ hfctmp->chans[B2].rx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B2].writechunk = >+ hfctmp->chans[B2].tx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2]; >+ >+ if (dahdi_register(&hfccard->span, 0)) { >+ printk(KERN_CRIT "unable to register zaptel device!\n"); >+ return -1; >+ } >+ >+ return 0; >+} >+ >+static void hfc_zap_transmit(struct hfc_chan_simplex *chan) >+{ >+ hfc_fifo_put(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE); >+} >+ >+static void hfc_zap_receive(struct hfc_chan_simplex *chan) >+{ >+ hfc_fifo_get(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE); >+} >+ >+/****************************************** >+ * Interrupt Handler >+ ******************************************/ >+ >+static void hfc_handle_timer_interrupt(struct hfc_card *card); >+static void hfc_handle_state_interrupt(struct hfc_card *card); >+static void hfc_handle_processing_interrupt(struct hfc_card *card); >+static void hfc_frame_arrived(struct hfc_chan_duplex *chan); >+static void hfc_handle_voice(struct hfc_card *card); >+ >+#if (KERNEL_VERSION(2, 6, 24) < LINUX_VERSION_CODE) >+static irqreturn_t hfc_interrupt(int irq, void *dev_id) >+#else >+static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs) >+#endif >+{ >+ struct hfc_card *card = dev_id; >+ unsigned long flags; >+ u8 status, s1, s2; >+ >+ if (!card) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "spurious interrupt (IRQ %d)\n", >+ irq); >+ return IRQ_NONE; >+ } >+ >+ spin_lock_irqsave(&card->lock, flags); >+ status = hfc_inb(card, hfc_STATUS); >+ if (!(status & hfc_STATUS_ANYINT)) { >+ /* >+ * maybe we are sharing the irq >+ */ >+ spin_unlock_irqrestore(&card->lock, flags); >+ return IRQ_NONE; >+ } >+ >+ /* We used to ingore the IRQ when the card was in processing >+ * state but apparently there is no restriction to access the >+ * card in such state: >+ * >+ * Joerg Ciesielski wrote: >+ * > There is no restriction for the IRQ handler to access >+ * > HFC-S PCI during processing phase. A IRQ latency of 375 us >+ * > is also no problem since there are no interrupt sources in >+ * > HFC-S PCI which must be handled very fast. >+ * > Due to its deep fifos the IRQ latency can be several ms with >+ * > out the risk of loosing data. Even the S/T state interrupts >+ * > must not be handled with a latency less than <5ms. >+ * > >+ * > The processing phase only indicates that HFC-S PCI is >+ * > processing the Fifos as PCI master so that data is read and >+ * > written in the 32k memory window. But there is no restriction >+ * > to access data in the memory window during this time. >+ * >+ * // if (status & hfc_STATUS_PCI_PROC) { >+ * // return IRQ_HANDLED; >+ * // } >+ */ >+ >+ s1 = hfc_inb(card, hfc_INT_S1); >+ s2 = hfc_inb(card, hfc_INT_S2); >+ >+ if (s1 != 0) { >+ if (s1 & hfc_INTS_TIMER) { >+ /* >+ * timer (bit 7) >+ */ >+ hfc_handle_timer_interrupt(card); >+ } >+ >+ if (s1 & hfc_INTS_L1STATE) { >+ /* >+ * state machine (bit 6) >+ */ >+ hfc_handle_state_interrupt(card); >+ } >+ >+ if (s1 & hfc_INTS_DREC) { >+ /* >+ * D chan RX (bit 5) >+ */ >+ hfc_frame_arrived(&card->chans[D]); >+ } >+ >+ if (s1 & hfc_INTS_B1REC) { >+ /* >+ * B1 chan RX (bit 3) >+ */ >+ hfc_frame_arrived(&card->chans[B1]); >+ } >+ >+ if (s1 & hfc_INTS_B2REC) { >+ /* >+ * B2 chan RX (bit 4) >+ */ >+ hfc_frame_arrived(&card->chans[B2]); >+ } >+ >+ if (s1 & hfc_INTS_DTRANS) { >+ /* >+ * D chan TX (bit 2) >+ */ >+ } >+ >+ if (s1 & hfc_INTS_B1TRANS) { >+ /* >+ * B1 chan TX (bit 0) >+ */ >+ } >+ >+ if (s1 & hfc_INTS_B2TRANS) { >+ /* >+ * B2 chan TX (bit 1) >+ */ >+ } >+ >+ } >+ >+ if (s2 != 0) { >+ if (s2 & hfc_M2_PMESEL) { >+ /* >+ * kaboom irq (bit 7) >+ * >+ * CologneChip says: >+ * >+ * the meaning of this fatal error bit is that HFC-S >+ * PCI as PCI master could not access the PCI bus >+ * within 125us to finish its data processing. If this >+ * happens only very seldom it does not cause big >+ * problems but of course some B-channel or D-channel >+ * data will be corrupted due to this event. >+ * >+ * Unfortunately this bit is only set once after the >+ * problem occurs and can only be reseted by a >+ * software reset. That means it is not easily >+ * possible to check how often this fatal error >+ * happens. >+ * >+ */ >+ >+ if (!card->sync_loss_reported) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "sync lost, pci performance too low!\n", >+ card->cardnum); >+ >+ card->sync_loss_reported = TRUE; >+ } >+ } >+ >+ if (s2 & hfc_M2_GCI_MON_REC) { >+ /* >+ * RxR monitor channel (bit 2) >+ */ >+ } >+ >+ if (s2 & hfc_M2_GCI_I_CHG) { >+ /* >+ * GCI I-change (bit 1) >+ */ >+ } >+ >+ if (s2 & hfc_M2_PROC_TRANS) { >+ /* >+ * processing/non-processing transition (bit 0) >+ */ >+ hfc_handle_processing_interrupt(card); >+ } >+ >+ } >+ >+ spin_unlock_irqrestore(&card->lock, flags); >+ >+ return IRQ_HANDLED; >+} >+ >+static void hfc_handle_timer_interrupt(struct hfc_card *card) >+{ >+ if (card->ignore_first_timer_interrupt) { >+ card->ignore_first_timer_interrupt = FALSE; >+ return; >+ } >+ >+ if ((card->nt_mode && card->l1_state == 3) || >+ (!card->nt_mode && card->l1_state == 7)) { >+ >+ card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK; >+ hfc_outb(card, hfc_CTMT, card->regs.ctmt); >+ >+ hfc_resume_fifo(card); >+ } >+} >+ >+static void hfc_handle_state_interrupt(struct hfc_card *card) >+{ >+ u8 new_state = hfc_inb(card, hfc_STATES) & hfc_STATES_STATE_MASK; >+ >+#ifdef DEBUG >+ if (debug_level >= 1) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "layer 1 state = %c%d\n", >+ card->cardnum, >+ card->nt_mode ? 'G' : 'F', >+ new_state); >+ } >+#endif >+ >+ if (card->nt_mode) { >+ /* >+ * NT mode >+ */ >+ >+ if (new_state == 3) { >+ /* >+ * fix to G3 state (see specs) >+ */ >+ hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3); >+ } >+ >+ if (new_state == 3 && card->l1_state != 3) >+ hfc_resume_fifo(card); >+ >+ if (new_state != 3 && card->l1_state == 3) >+ hfc_suspend_fifo(card); >+ >+ } else { >+ if (new_state == 3) { >+ /* >+ * Keep L1 up... zaptel & libpri expects >+ * a always up L1... >+ * Enable only when using an unpatched libpri >+ */ >+ >+ if (force_l1_up) { >+ hfc_outb(card, hfc_STATES, >+ hfc_STATES_DO_ACTION | >+ hfc_STATES_ACTIVATE| >+ hfc_STATES_NT_G2_G3); >+ } >+ } >+ >+ if (new_state == 7 && card->l1_state != 7) { >+ /* >+ * TE is now active, schedule FIFO activation after >+ * some time, otherwise the first frames are lost >+ */ >+ >+ card->regs.ctmt |= hfc_CTMT_TIMER_50 | >+ hfc_CTMT_TIMER_CLEAR; >+ hfc_outb(card, hfc_CTMT, card->regs.ctmt); >+ >+ /* >+ * Activating the timer firest an >+ * interrupt immediately, we >+ * obviously need to ignore it >+ */ >+ card->ignore_first_timer_interrupt = TRUE; >+ } >+ >+ if (new_state != 7 && card->l1_state == 7) { >+ /* >+ * TE has become inactive, disable FIFO >+ */ >+ hfc_suspend_fifo(card); >+ } >+ } >+ >+ card->l1_state = new_state; >+} >+ >+static void hfc_handle_processing_interrupt(struct hfc_card *card) >+{ >+ int available_bytes = 0; >+ >+ /* >+ * Synchronize with the first enabled channel >+ */ >+ if (card->regs.fifo_en & hfc_FIFOEN_B1RX) >+ available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx); >+ if (card->regs.fifo_en & hfc_FIFOEN_B2RX) >+ available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx); >+ else >+ available_bytes = -1; >+ >+ if ((available_bytes == -1 && card->ticks == 8) || >+ available_bytes >= DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { >+ card->ticks = 0; >+ >+ if (available_bytes > DAHDI_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) { >+ card->late_irqs++; >+ /* >+ * we are out of sync, clear fifos, jaw >+ */ >+ hfc_clear_fifo_rx(&card->chans[B1].rx); >+ hfc_clear_fifo_tx(&card->chans[B1].tx); >+ hfc_clear_fifo_rx(&card->chans[B2].rx); >+ hfc_clear_fifo_tx(&card->chans[B2].tx); >+ >+#ifdef DEBUG >+ if (debug_level >= 4) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "late IRQ, %d bytes late\n", >+ card->cardnum, >+ available_bytes - >+ (DAHDI_CHUNKSIZE + >+ hfc_RX_FIFO_PRELOAD)); >+ } >+#endif >+ } else { >+ hfc_handle_voice(card); >+ } >+ } >+ >+ card->ticks++; >+} >+ >+ >+static void hfc_handle_voice(struct hfc_card *card) >+{ >+ struct dahdi_hfc *hfccard = card->ztdev; >+ int frame_left, res; >+ unsigned char buf[hfc_HDLC_BUF_LEN]; >+ unsigned int size = sizeof(buf) / sizeof(buf[0]); >+ >+ >+ if (card->chans[B1].status != open_voice && >+ card->chans[B2].status != open_voice) >+ return; >+ >+ dahdi_transmit(&hfccard->span); >+ >+ if (card->regs.fifo_en & hfc_FIFOEN_B1TX) >+ hfc_zap_transmit(&card->chans[B1].tx); >+ if (card->regs.fifo_en & hfc_FIFOEN_B2TX) >+ hfc_zap_transmit(&card->chans[B2].tx); >+ >+ /* >+ * dahdi hdlc frame tx >+ */ >+ >+ if (atomic_read(&hfccard->hdlc_pending)) { >+ hfc_check_l1_up(card); >+ res = dahdi_hdlc_getbuf(hfccard->sigchan, buf, &size); >+ if (size > 0) { >+ hfccard->sigactive = 1; >+ memcpy(card->chans[D].tx.ugly_framebuf + >+ card->chans[D].tx.ugly_framebuf_size, >+ buf, size); >+ card->chans[D].tx.ugly_framebuf_size += size; >+ if (res != 0) { >+ hfc_fifo_put_frame(&card->chans[D].tx, >+ card->chans[D].tx.ugly_framebuf, >+ card->chans[D].tx.ugly_framebuf_size); >+ ++hfccard->frames_out; >+ hfccard->sigactive = 0; >+ card->chans[D].tx.ugly_framebuf_size >+ = 0; >+ atomic_dec(&hfccard->hdlc_pending); >+ } >+ } >+ } >+ /* >+ * dahdi hdlc frame tx done >+ */ >+ >+ if (card->regs.fifo_en & hfc_FIFOEN_B1RX) >+ hfc_zap_receive(&card->chans[B1].rx); >+ else >+ memset(&card->chans[B1].rx.zaptel_buffer, 0x7f, >+ sizeof(card->chans[B1].rx.zaptel_buffer)); >+ >+ if (card->regs.fifo_en & hfc_FIFOEN_B2RX) >+ hfc_zap_receive(&card->chans[B2].rx); >+ else >+ memset(&card->chans[B2].rx.zaptel_buffer, 0x7f, >+ sizeof(card->chans[B1].rx.zaptel_buffer)); >+ >+ /* >+ * Echo cancellation >+ */ >+ dahdi_ec_chunk(&hfccard->chans[DAHDI_B1], >+ card->chans[B1].rx.zaptel_buffer, >+ card->chans[B1].tx.zaptel_buffer); >+ dahdi_ec_chunk(&hfccard->chans[DAHDI_B2], >+ card->chans[B2].rx.zaptel_buffer, >+ card->chans[B2].tx.zaptel_buffer); >+ >+ /* >+ * dahdi hdlc frame rx >+ */ >+ if (hfc_fifo_has_frames(&card->chans[D].rx)) >+ hfc_frame_arrived(&card->chans[D]); >+ >+ if (card->chans[D].rx.ugly_framebuf_size) { >+ frame_left = card->chans[D].rx.ugly_framebuf_size - >+ card->chans[D].rx.ugly_framebuf_off ; >+ if (frame_left > hfc_HDLC_BUF_LEN) { >+ dahdi_hdlc_putbuf(hfccard->sigchan, >+ card->chans[D].rx.ugly_framebuf + >+ card->chans[D].rx.ugly_framebuf_off, >+ hfc_HDLC_BUF_LEN); >+ card->chans[D].rx.ugly_framebuf_off += >+ hfc_HDLC_BUF_LEN; >+ } else { >+ dahdi_hdlc_putbuf(hfccard->sigchan, >+ card->chans[D].rx.ugly_framebuf + >+ card->chans[D].rx.ugly_framebuf_off, >+ frame_left); >+ dahdi_hdlc_finish(hfccard->sigchan); >+ card->chans[D].rx.ugly_framebuf_size = 0; >+ card->chans[D].rx.ugly_framebuf_off = 0; >+ } >+ } >+ /* >+ * dahdi hdlc frame rx done >+ */ >+ >+ if (hfccard->span.flags & DAHDI_FLAG_RUNNING) >+ dahdi_receive(&hfccard->span); >+ >+} >+ >+static void hfc_frame_arrived(struct hfc_chan_duplex *chan) >+{ >+ struct hfc_card *card = chan->card; >+ int antiloop = 16; >+ struct sk_buff *skb; >+ >+ while (hfc_fifo_has_frames(&chan->rx) && --antiloop) { >+ int frame_size = hfc_fifo_get_frame_size(&chan->rx); >+ >+ if (frame_size < 3) { >+#ifdef DEBUG >+ if (debug_level >= 2) >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "invalid frame received, " >+ "just %d bytes\n", >+ card->cardnum, >+ chan->name, >+ frame_size); >+#endif >+ >+ hfc_fifo_drop_frame(&chan->rx); >+ >+ >+ continue; >+ } else if (frame_size == 3) { >+#ifdef DEBUG >+ if (debug_level >= 2) >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "empty frame received\n", >+ card->cardnum, >+ chan->name); >+#endif >+ >+ hfc_fifo_drop_frame(&chan->rx); >+ >+ >+ continue; >+ } >+ >+ if (chan->open_by_zaptel && >+ card->chans[D].rx.ugly_framebuf_size) { >+ >+ /* >+ * We have to wait for Dahdi to transmit the >+ * frame... wait for next time >+ */ >+ >+ break; >+ } >+ >+ skb = dev_alloc_skb(frame_size - 3); >+ >+ if (!skb) { >+ printk(KERN_ERR hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "cannot allocate skb: frame dropped\n", >+ card->cardnum, >+ chan->name); >+ >+ hfc_fifo_drop_frame(&chan->rx); >+ >+ >+ continue; >+ } >+ >+ >+ /* >+ * HFC does the checksum >+ */ >+#ifndef CHECKSUM_HW >+ skb->ip_summed = CHECKSUM_COMPLETE; >+#else >+ skb->ip_summed = CHECKSUM_HW; >+#endif >+ >+ if (chan->open_by_zaptel) { >+ card->chans[D].rx.ugly_framebuf_size = frame_size - 1; >+ >+ if (hfc_fifo_get_frame(&card->chans[D].rx, >+ card->chans[D].rx.ugly_framebuf, >+ frame_size - 1) == -1) { >+ dev_kfree_skb(skb); >+ continue; >+ } >+ >+ memcpy(skb_put(skb, frame_size - 3), >+ card->chans[D].rx.ugly_framebuf, >+ frame_size - 3); >+ } else { >+ if (hfc_fifo_get_frame(&chan->rx, >+ skb_put(skb, frame_size - 3), >+ frame_size - 3) == -1) { >+ dev_kfree_skb(skb); >+ continue; >+ } >+ } >+ } >+ >+ if (!antiloop) >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "Infinite loop detected\n", >+ card->cardnum); >+} >+ >+/****************************************** >+ * Module initialization and cleanup >+ ******************************************/ >+ >+static int __devinit hfc_probe(struct pci_dev *pci_dev, >+ const struct pci_device_id *ent) >+{ >+ static int cardnum; >+ int err; >+ int i; >+ >+ struct hfc_card *card = NULL; >+ struct dahdi_hfc *zthfc = NULL; >+ card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); >+ if (!card) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "unable to kmalloc!\n"); >+ err = -ENOMEM; >+ goto err_alloc_hfccard; >+ } >+ >+ memset(card, 0x00, sizeof(struct hfc_card)); >+ card->cardnum = cardnum; >+ card->pcidev = pci_dev; >+ spin_lock_init(&card->lock); >+ >+ pci_set_drvdata(pci_dev, card); >+ >+ err = pci_enable_device(pci_dev); >+ if (err) >+ goto err_pci_enable_device; >+ >+ err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT); >+ if (err) { >+ printk(KERN_ERR hfc_DRIVER_PREFIX >+ "card %d: " >+ "No suitable DMA configuration available.\n", >+ card->cardnum); >+ goto err_pci_set_dma_mask; >+ } >+ >+ pci_write_config_word(pci_dev, PCI_COMMAND, PCI_COMMAND_MEMORY); >+ err = pci_request_regions(pci_dev, hfc_DRIVER_NAME); >+ if (err) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "cannot request I/O memory region\n", >+ card->cardnum); >+ goto err_pci_request_regions; >+ } >+ >+ pci_set_master(pci_dev); >+ >+ if (!pci_dev->irq) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "no irq!\n", >+ card->cardnum); >+ err = -ENODEV; >+ goto err_noirq; >+ } >+ >+ card->io_bus_mem = pci_resource_start(pci_dev, 1); >+ if (!card->io_bus_mem) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "no iomem!\n", >+ card->cardnum); >+ err = -ENODEV; >+ goto err_noiobase; >+ } >+ >+ card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE); >+ if (!(card->io_mem)) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "cannot ioremap I/O memory\n", >+ card->cardnum); >+ err = -ENODEV; >+ goto err_ioremap; >+ } >+ >+ /* >+ * pci_alloc_consistent guarantees alignment >+ * (Documentation/DMA-mapping.txt) >+ */ >+ card->fifo_mem = pci_alloc_consistent(pci_dev, >+ hfc_FIFO_SIZE, &card->fifo_bus_mem); >+ if (!card->fifo_mem) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "unable to allocate FIFO DMA memory!\n", >+ card->cardnum); >+ err = -ENOMEM; >+ goto err_alloc_fifo; >+ } >+ >+ memset(card->fifo_mem, 0x00, hfc_FIFO_SIZE); >+ >+ card->fifos = card->fifo_mem; >+ >+ pci_write_config_dword(card->pcidev, hfc_PCI_MWBA, card->fifo_bus_mem); >+ >+ err = request_irq(card->pcidev->irq, &hfc_interrupt, >+ >+#if (KERNEL_VERSION(2, 6, 23) < LINUX_VERSION_CODE) >+ IRQF_SHARED, hfc_DRIVER_NAME, card); >+#else >+ SA_SHIRQ, hfc_DRIVER_NAME, card); >+#endif >+ >+ if (err) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "unable to register irq\n", >+ card->cardnum); >+ goto err_request_irq; >+ } >+ >+ card->nt_mode = FALSE; >+ >+ if (modes & (1 << card->cardnum)) >+ card->nt_mode = TRUE; >+ >+ for (i = 0; i < nt_modes_count; i++) { >+ if (nt_modes[i] == card->cardnum) >+ card->nt_mode = TRUE; >+ } >+ >+ /* >+ * D Channel >+ */ >+ card->chans[D].card = card; >+ card->chans[D].name = "D"; >+ card->chans[D].status = free; >+ card->chans[D].number = D; >+ spin_lock_init(&card->chans[D].lock); >+ >+ card->chans[D].rx.chan = &card->chans[D]; >+ card->chans[D].rx.fifo_base = card->fifos + 0x4000; >+ card->chans[D].rx.z_base = card->fifos + 0x4000; >+ card->chans[D].rx.z1_base = card->fifos + 0x6080; >+ card->chans[D].rx.z2_base = card->fifos + 0x6082; >+ card->chans[D].rx.z_min = 0x0000; >+ card->chans[D].rx.z_max = 0x01FF; >+ card->chans[D].rx.f_min = 0x10; >+ card->chans[D].rx.f_max = 0x1F; >+ card->chans[D].rx.f1 = card->fifos + 0x60a0; >+ card->chans[D].rx.f2 = card->fifos + 0x60a1; >+ card->chans[D].rx.fifo_size = card->chans[D].rx.z_max >+ - card->chans[D].rx.z_min + 1; >+ card->chans[D].rx.f_num = card->chans[D].rx.f_max >+ - card->chans[D].rx.f_min + 1; >+ >+ card->chans[D].tx.chan = &card->chans[D]; >+ card->chans[D].tx.fifo_base = card->fifos + 0x0000; >+ card->chans[D].tx.z_base = card->fifos + 0x0000; >+ card->chans[D].tx.z1_base = card->fifos + 0x2080; >+ card->chans[D].tx.z2_base = card->fifos + 0x2082; >+ card->chans[D].tx.z_min = 0x0000; >+ card->chans[D].tx.z_max = 0x01FF; >+ card->chans[D].tx.f_min = 0x10; >+ card->chans[D].tx.f_max = 0x1F; >+ card->chans[D].tx.f1 = card->fifos + 0x20a0; >+ card->chans[D].tx.f2 = card->fifos + 0x20a1; >+ card->chans[D].tx.fifo_size = card->chans[D].tx.z_max - >+ card->chans[D].tx.z_min + 1; >+ card->chans[D].tx.f_num = card->chans[D].tx.f_max - >+ card->chans[D].tx.f_min + 1; >+ >+ /* >+ * B1 Channel >+ */ >+ card->chans[B1].card = card; >+ card->chans[B1].name = "B1"; >+ card->chans[B1].status = free; >+ card->chans[B1].number = B1; >+ card->chans[B1].protocol = 0; >+ spin_lock_init(&card->chans[B1].lock); >+ >+ card->chans[B1].rx.chan = &card->chans[B1]; >+ card->chans[B1].rx.fifo_base = card->fifos + 0x4200; >+ card->chans[B1].rx.z_base = card->fifos + 0x4000; >+ card->chans[B1].rx.z1_base = card->fifos + 0x6000; >+ card->chans[B1].rx.z2_base = card->fifos + 0x6002; >+ card->chans[B1].rx.z_min = 0x0200; >+ card->chans[B1].rx.z_max = 0x1FFF; >+ card->chans[B1].rx.f_min = 0x00; >+ card->chans[B1].rx.f_max = 0x1F; >+ card->chans[B1].rx.f1 = card->fifos + 0x6080; >+ card->chans[B1].rx.f2 = card->fifos + 0x6081; >+ card->chans[B1].rx.fifo_size = card->chans[B1].rx.z_max - >+ card->chans[B1].rx.z_min + 1; >+ card->chans[B1].rx.f_num = card->chans[B1].rx.f_max - >+ card->chans[B1].rx.f_min + 1; >+ >+ card->chans[B1].tx.chan = &card->chans[B1]; >+ card->chans[B1].tx.fifo_base = card->fifos + 0x0200; >+ card->chans[B1].tx.z_base = card->fifos + 0x0000; >+ card->chans[B1].tx.z1_base = card->fifos + 0x2000; >+ card->chans[B1].tx.z2_base = card->fifos + 0x2002; >+ card->chans[B1].tx.z_min = 0x0200; >+ card->chans[B1].tx.z_max = 0x1FFF; >+ card->chans[B1].tx.f_min = 0x00; >+ card->chans[B1].tx.f_max = 0x1F; >+ card->chans[B1].tx.f1 = card->fifos + 0x2080; >+ card->chans[B1].tx.f2 = card->fifos + 0x2081; >+ card->chans[B1].tx.fifo_size = card->chans[B1].tx.z_max - >+ card->chans[B1].tx.z_min + 1; >+ card->chans[B1].tx.f_num = card->chans[B1].tx.f_max - >+ card->chans[B1].tx.f_min + 1; >+ >+ /* >+ * B2 Channel >+ */ >+ card->chans[B2].card = card; >+ card->chans[B2].name = "B2"; >+ card->chans[B2].status = free; >+ card->chans[B2].number = B2; >+ card->chans[B2].protocol = 0; >+ spin_lock_init(&card->chans[B2].lock); >+ >+ card->chans[B2].rx.chan = &card->chans[B2]; >+ card->chans[B2].rx.fifo_base = card->fifos + 0x6200, >+ card->chans[B2].rx.z_base = card->fifos + 0x6000; >+ card->chans[B2].rx.z1_base = card->fifos + 0x6100; >+ card->chans[B2].rx.z2_base = card->fifos + 0x6102; >+ card->chans[B2].rx.z_min = 0x0200; >+ card->chans[B2].rx.z_max = 0x1FFF; >+ card->chans[B2].rx.f_min = 0x00; >+ card->chans[B2].rx.f_max = 0x1F; >+ card->chans[B2].rx.f1 = card->fifos + 0x6180; >+ card->chans[B2].rx.f2 = card->fifos + 0x6181; >+ card->chans[B2].rx.fifo_size = card->chans[B2].rx.z_max - >+ card->chans[B2].rx.z_min + 1; >+ card->chans[B2].rx.f_num = card->chans[B2].rx.f_max - >+ card->chans[B2].rx.f_min + 1; >+ >+ card->chans[B2].tx.chan = &card->chans[B2]; >+ card->chans[B2].tx.fifo_base = card->fifos + 0x2200; >+ card->chans[B2].tx.z_base = card->fifos + 0x2000; >+ card->chans[B2].tx.z1_base = card->fifos + 0x2100; >+ card->chans[B2].tx.z2_base = card->fifos + 0x2102; >+ card->chans[B2].tx.z_min = 0x0200; >+ card->chans[B2].tx.z_max = 0x1FFF; >+ card->chans[B2].tx.f_min = 0x00; >+ card->chans[B2].tx.f_max = 0x1F; >+ card->chans[B2].tx.f1 = card->fifos + 0x2180; >+ card->chans[B2].tx.f2 = card->fifos + 0x2181; >+ card->chans[B2].tx.fifo_size = card->chans[B2].tx.z_max - >+ card->chans[B2].tx.z_min + 1; >+ card->chans[B2].tx.f_num = card->chans[B2].tx.f_max - >+ card->chans[B2].tx.f_min + 1; >+ >+ /* >+ * All done >+ */ >+ >+ zthfc = kmalloc(sizeof(struct dahdi_hfc), GFP_KERNEL); >+ if (!zthfc) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "unable to kmalloc!\n"); >+ goto err_request_irq; >+ } >+ memset(zthfc, 0x0, sizeof(struct dahdi_hfc)); >+ >+ zthfc->card = card; >+ hfc_zap_initialize(zthfc); >+ card->ztdev = zthfc; >+ >+ snprintf(card->proc_dir_name, >+ sizeof(card->proc_dir_name), >+ "%d", card->cardnum); >+ card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir); >+ SET_PROC_DIRENTRY_OWNER(card->proc_dir); >+ >+ hfc_resetCard(card); >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d configured for %s mode at mem %#lx (0x%p) IRQ %u\n", >+ card->cardnum, >+ card->nt_mode ? "NT" : "TE", >+ card->io_bus_mem, >+ card->io_mem, >+ card->pcidev->irq); >+ >+ cardnum++; >+ >+ return 0; >+ >+err_request_irq: >+ pci_free_consistent(pci_dev, hfc_FIFO_SIZE, >+ card->fifo_mem, card->fifo_bus_mem); >+err_alloc_fifo: >+ iounmap(card->io_mem); >+err_ioremap: >+err_noiobase: >+err_noirq: >+ pci_release_regions(pci_dev); >+err_pci_request_regions: >+err_pci_set_dma_mask: >+err_pci_enable_device: >+ kfree(card); >+err_alloc_hfccard: >+ return err; >+} >+ >+static void __devexit hfc_remove(struct pci_dev *pci_dev) >+{ >+ struct hfc_card *card = pci_get_drvdata(pci_dev); >+ >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "shutting down card at %p.\n", >+ card->cardnum, >+ card->io_mem); >+ >+ hfc_softreset(card); >+ >+ dahdi_unregister(&card->ztdev->span); >+ >+ >+ /* >+ * disable memio and bustmaster >+ */ >+ pci_write_config_word(pci_dev, PCI_COMMAND, 0); >+ >+ remove_proc_entry("bufs", card->proc_dir); >+ remove_proc_entry("fifos", card->proc_dir); >+ remove_proc_entry("info", card->proc_dir); >+ remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir); >+ >+ free_irq(pci_dev->irq, card); >+ >+ pci_free_consistent(pci_dev, hfc_FIFO_SIZE, >+ card->fifo_mem, card->fifo_bus_mem); >+ >+ iounmap(card->io_mem); >+ >+ pci_release_regions(pci_dev); >+ >+ pci_disable_device(pci_dev); >+ >+ kfree(card); >+} >+ >+/****************************************** >+ * Module stuff >+ ******************************************/ >+ >+static int __init hfc_init_module(void) >+{ >+ int ret; >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ hfc_DRIVER_STRING " loading\n"); >+ >+#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) >+ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, NULL); >+#else >+ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); >+#endif >+ >+ ret = dahdi_pci_module(&hfc_driver); >+ return ret; >+} >+ >+module_init(hfc_init_module); >+ >+static void __exit hfc_module_exit(void) >+{ >+ pci_unregister_driver(&hfc_driver); >+ >+#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) >+ remove_proc_entry(hfc_DRIVER_NAME, NULL); >+#else >+ remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); >+#endif >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ hfc_DRIVER_STRING " unloaded\n"); >+} >+ >+module_exit(hfc_module_exit); >+ >+#endif >+ >+MODULE_DESCRIPTION(hfc_DRIVER_DESCR); >+MODULE_AUTHOR("Jens Wilke <jw_vzaphfc@headissue.com>, " >+ "Daniele (Vihai) Orlandi <daniele@orlandi.com>, " >+ "Jose A. Deniz <odicha@hotmail.com>"); >+MODULE_ALIAS("vzaphfc"); >+#ifdef MODULE_LICENSE >+MODULE_LICENSE("GPL"); >+#endif >+ >+ >+module_param(modes, int, 0444); >+ >+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) >+module_param_array(nt_modes, int, &nt_modes_count, 0444); >+#else >+module_param_array(nt_modes, int, nt_modes_count, 0444); >+#endif >+ >+module_param(force_l1_up, int, 0444); >+#ifdef DEBUG >+module_param(debug_level, int, 0444); >+#endif >+ >+MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode"); >+MODULE_PARM_DESC(nt_modes, >+ "Comma-separated list of card IDs to configure in NT mode"); >+MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down"); >+#ifdef DEBUG >+MODULE_PARM_DESC(debug_level, "Debug verbosity level"); >+#endif >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/fifo.c dahdi-svn-new/drivers/dahdi/zaphfc/fifo.c >--- dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/fifo.c 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/zaphfc/fifo.c 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,375 @@ >+/* >+ * fifo.c - HFC FIFO management routines >+ * >+ * Copyright (C) 2006 headissue GmbH; Jens Wilke >+ * Copyright (C) 2004 Daniele Orlandi >+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH >+ * >+ * Original author of this code is >+ * Daniele "Vihai" Orlandi <daniele@orlandi.com> >+ * >+ * This program is free software and may be modified and >+ * distributed under the terms of the GNU Public License. >+ * >+ */ >+ >+#include <linux/kernel.h> >+ >+#include <dahdi/kernel.h> >+ >+#include "fifo.h" >+ >+static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan, >+ int z_start, >+ void *data, int size) >+{ >+ int bytes_to_boundary = chan->z_max - z_start + 1; >+ if (bytes_to_boundary >= size) { >+ memcpy(data, >+ chan->z_base + z_start, >+ size); >+ } else { >+ /* >+ * Buffer wrap >+ */ >+ memcpy(data, >+ chan->z_base + z_start, >+ bytes_to_boundary); >+ >+ memcpy(data + bytes_to_boundary, >+ chan->fifo_base, >+ size - bytes_to_boundary); >+ } >+} >+ >+static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan, >+ void *data, int size) >+{ >+ int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1; >+ if (bytes_to_boundary >= size) { >+ memcpy(chan->z_base + *Z1_F1(chan), >+ data, >+ size); >+ } else { >+ /* >+ * FIFO wrap >+ */ >+ >+ memcpy(chan->z_base + *Z1_F1(chan), >+ data, >+ bytes_to_boundary); >+ >+ memcpy(chan->fifo_base, >+ data + bytes_to_boundary, >+ size - bytes_to_boundary); >+ } >+} >+ >+int hfc_fifo_get(struct hfc_chan_simplex *chan, >+ void *data, int size) >+{ >+ int available_bytes; >+ >+ /* >+ * Some useless statistic >+ */ >+ chan->bytes += size; >+ >+ available_bytes = hfc_fifo_used_rx(chan); >+ >+ if (available_bytes < size && !chan->fifo_underrun++) { >+ /* >+ * print the warning only once >+ */ >+ printk(KERN_WARNING hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "RX FIFO not enough (%d) bytes to receive!\n", >+ chan->chan->card->cardnum, >+ chan->chan->name, >+ available_bytes); >+ return -1; >+ } >+ >+ hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size); >+ *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); >+ return available_bytes - size; >+} >+ >+void hfc_fifo_put(struct hfc_chan_simplex *chan, >+ void *data, int size) >+{ >+ struct hfc_card *card = chan->chan->card; >+ int used_bytes = hfc_fifo_used_tx(chan); >+ int free_bytes = hfc_fifo_free_tx(chan); >+ >+ if (!used_bytes && !chan->fifo_underrun++) { >+ /* >+ * print warning only once, to make timing not worse >+ */ >+ printk(KERN_WARNING hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "TX FIFO has become empty\n", >+ card->cardnum, >+ chan->chan->name); >+ } >+ if (free_bytes < size) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "TX FIFO full!\n", >+ chan->chan->card->cardnum, >+ chan->chan->name); >+ chan->fifo_full++; >+ hfc_clear_fifo_tx(chan); >+ } >+ >+ hfc_fifo_mem_write(chan, data, size); >+ chan->bytes += size; >+ *Z1_F1(chan) = Z_inc(chan, *Z1_F1(chan), size); >+} >+ >+int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size) >+{ >+ int frame_size; >+ u16 newz2 ; >+ >+ if (*chan->f1 == *chan->f2) { >+ /* >+ * nothing received, strange uh? >+ */ >+ printk(KERN_WARNING hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "get_frame called with no frame in FIFO.\n", >+ chan->chan->card->cardnum, >+ chan->chan->name); >+ >+ return -1; >+ } >+ >+ /* >+ * frame_size includes CRC+CRC+STAT >+ */ >+ frame_size = hfc_fifo_get_frame_size(chan); >+ >+#ifdef DEBUG >+ if (debug_level == 3) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "RX len %2d: ", >+ chan->chan->card->cardnum, >+ chan->chan->name, >+ frame_size); >+ } else if (debug_level >= 4) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "RX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", >+ chan->chan->card->cardnum, >+ chan->chan->name, >+ *chan->f1, *chan->f2, *Z1_F2(chan), *Z2_F2(chan), >+ frame_size); >+ } >+ >+ if (debug_level >= 3) { >+ int i; >+ for (i = 0; i < frame_size; i++) { >+ printk("%02x", hfc_fifo_u8(chan, >+ Z_inc(chan, *Z2_F2(chan), i))); >+ } >+ >+ printk("\n"); >+ } >+#endif >+ >+ if (frame_size <= 0) { >+#ifdef DEBUG >+ if (debug_level >= 2) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "invalid (empty) frame received.\n", >+ chan->chan->card->cardnum, >+ chan->chan->name); >+ } >+#endif >+ >+ hfc_fifo_drop_frame(chan); >+ return -1; >+ } >+ >+ /* >+ * STAT is not really received >+ */ >+ chan->bytes += frame_size - 1; >+ >+ /* >+ * Calculate beginning of the next frame >+ */ >+ newz2 = Z_inc(chan, *Z2_F2(chan), frame_size); >+ >+ /* >+ * We cannot use hfc_fifo_get because of different semantic of >+ * "available bytes" and to avoid useless increment of Z2 >+ */ >+ hfc_fifo_mem_read(chan, *Z2_F2(chan), data, >+ frame_size < max_size ? frame_size : max_size); >+ >+ if (hfc_fifo_u8(chan, Z_inc(chan, *Z2_F2(chan), >+ frame_size - 1)) != 0x00) { >+ /* >+ * CRC not ok, frame broken, skipping >+ */ >+#ifdef DEBUG >+ if (debug_level >= 2) { >+ printk(KERN_WARNING hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "Received frame with wrong CRC\n", >+ chan->chan->card->cardnum, >+ chan->chan->name); >+ } >+#endif >+ >+ chan->crc++; >+ >+ hfc_fifo_drop_frame(chan); >+ return -1; >+ } >+ >+ chan->frames++; >+ >+ *chan->f2 = F_inc(chan, *chan->f2, 1); >+ >+ /* >+ * Set Z2 for the next frame we're going to receive >+ */ >+ *Z2_F2(chan) = newz2; >+ >+ return frame_size; >+} >+ >+void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan) >+{ >+ int available_bytes; >+ u16 newz2; >+ >+ if (*chan->f1 == *chan->f2) { >+ /* >+ * nothing received, strange eh? >+ */ >+ printk(KERN_WARNING hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "skip_frame called with no frame in FIFO.\n", >+ chan->chan->card->cardnum, >+ chan->chan->name); >+ >+ return; >+ } >+ >+ available_bytes = hfc_fifo_used_rx(chan) + 1; >+ >+ /* >+ * Calculate beginning of the next frame >+ */ >+ newz2 = Z_inc(chan, *Z2_F2(chan), available_bytes); >+ >+ *chan->f2 = F_inc(chan, *chan->f2, 1); >+ >+ /* >+ * Set Z2 for the next frame we're going to receive >+ */ >+ *Z2_F2(chan) = newz2; >+} >+ >+void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, >+ void *data, int size) >+{ >+ u16 newz1; >+ int available_frames; >+ >+#ifdef DEBUG >+ if (debug_level == 3) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "TX len %2d: ", >+ chan->chan->card->cardnum, >+ chan->chan->name, >+ size); >+ } else if (debug_level >= 4) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "TX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", >+ chan->chan->card->cardnum, >+ chan->chan->name, >+ *chan->f1, *chan->f2, *Z1_F1(chan), *Z2_F1(chan), >+ size); >+ } >+ >+ if (debug_level >= 3) { >+ int i; >+ for (i = 0; i < size; i++) >+ printk("%02x", ((u8 *)data)[i]); >+ >+ printk("\n"); >+ } >+#endif >+ >+ available_frames = hfc_fifo_free_frames(chan); >+ >+ if (available_frames >= chan->f_num) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "TX FIFO total number of frames exceeded!\n", >+ chan->chan->card->cardnum, >+ chan->chan->name); >+ >+ chan->fifo_full++; >+ >+ return; >+ } >+ >+ hfc_fifo_put(chan, data, size); >+ >+ newz1 = *Z1_F1(chan); >+ >+ *chan->f1 = F_inc(chan, *chan->f1, 1); >+ >+ *Z1_F1(chan) = newz1; >+ >+ chan->frames++; >+} >+ >+void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan) >+{ >+ *chan->f2 = *chan->f1; >+ *Z2_F2(chan) = *Z1_F2(chan); >+} >+ >+void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan) >+{ >+ *chan->f1 = *chan->f2; >+ *Z1_F1(chan) = *Z2_F1(chan); >+ >+ if (chan->chan->status == open_voice) { >+ /* >+ * Make sure that at least hfc_TX_FIFO_PRELOAD bytes are >+ * present in the TX FIFOs >+ * Create hfc_TX_FIFO_PRELOAD bytes of empty data >+ * (0x7f is mute audio) >+ */ >+ u8 empty_fifo[hfc_TX_FIFO_PRELOAD + >+ DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; >+ memset(empty_fifo, 0x7f, sizeof(empty_fifo)); >+ >+ hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); >+ } >+} >+ >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/fifo.h dahdi-svn-new/drivers/dahdi/zaphfc/fifo.h >--- dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/fifo.h 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/zaphfc/fifo.h 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,139 @@ >+/* >+ * fifo.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards >+ * >+ * Copyright (C) 2004 Daniele Orlandi >+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH >+ * >+ * Daniele "Vihai" Orlandi <daniele@orlandi.com> >+ * >+ * Major rewrite of the driver made by >+ * Klaus-Peter Junghanns <kpj@junghanns.net> >+ * >+ * This program is free software and may be modified and >+ * distributed under the terms of the GNU Public License. >+ * >+ */ >+ >+#ifndef _HFC_FIFO_H >+#define _HFC_FIFO_H >+ >+#include "zaphfc.h" >+ >+static inline u16 *Z1_F1(struct hfc_chan_simplex *chan) >+{ >+ return chan->z1_base + (*chan->f1 * 4); >+} >+ >+static inline u16 *Z2_F1(struct hfc_chan_simplex *chan) >+{ >+ return chan->z2_base + (*chan->f1 * 4); >+} >+ >+static inline u16 *Z1_F2(struct hfc_chan_simplex *chan) >+{ >+ return chan->z1_base + (*chan->f2 * 4); >+} >+ >+static inline u16 *Z2_F2(struct hfc_chan_simplex *chan) >+{ >+ return chan->z2_base + (*chan->f2 * 4); >+} >+ >+static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc) >+{ >+ /* >+ * declared as u32 in order to manage overflows >+ */ >+ u32 newz = z + inc; >+ if (newz > chan->z_max) >+ newz -= chan->fifo_size; >+ >+ return newz; >+} >+ >+static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc) >+{ >+ /* >+ * declared as u16 in order to manage overflows >+ */ >+ u16 newf = f + inc; >+ if (newf > chan->f_max) >+ newf -= chan->f_num; >+ >+ return newf; >+} >+ >+static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan) >+{ >+ return (*Z1_F2(chan) - *Z2_F2(chan) + >+ chan->fifo_size) % chan->fifo_size; >+} >+ >+static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan) >+{ >+ /* >+ * This +1 is needed because in frame mode the available bytes are Z2-Z1+1 >+ * while in transparent mode I wouldn't consider the byte pointed by Z2 to >+ * be available, otherwise, the FIFO would always contain one byte, even >+ * when Z1==Z2 >+ */ >+ >+ return hfc_fifo_used_rx(chan) + 1; >+} >+ >+static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z) >+{ >+ return *((u8 *)(chan->z_base + z)); >+} >+ >+static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan) >+{ >+ return (*Z1_F1(chan) - *Z2_F1(chan) + >+ chan->fifo_size) % chan->fifo_size; >+} >+ >+static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan) >+{ >+ u16 free_bytes = *Z2_F1(chan) - *Z1_F1(chan); >+ >+ if (free_bytes > 0) >+ return free_bytes; >+ else >+ return free_bytes + chan->fifo_size; >+} >+ >+static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan) >+{ >+ u16 free_bytes = *Z2_F1(chan) - *Z1_F1(chan); >+ >+ if (free_bytes > 0) >+ return free_bytes; >+ else >+ return free_bytes + chan->fifo_size; >+} >+ >+static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan) >+{ >+ return *chan->f1 != *chan->f2; >+} >+ >+static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan) >+{ >+ return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num; >+} >+ >+static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan) >+{ >+ return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num; >+} >+ >+int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size); >+void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size); >+void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size); >+int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size); >+void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan); >+void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size); >+void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan); >+void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan); >+ >+#endif >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/Kbuild dahdi-svn-new/drivers/dahdi/zaphfc/Kbuild >--- dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/Kbuild 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/zaphfc/Kbuild 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,10 @@ >+obj-m += zaphfc.o >+ >+EXTRA_CFLAGS := -I$(src)/.. -Wno-undef >+ >+zaphfc-objs := base.o fifo.o >+ >+$(obj)/base.o: $(src)/zaphfc.h >+$(obj)/fifo.o: $(src)/fifo.h >+ >+ >diff -urN dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/zaphfc.h dahdi-svn-new/drivers/dahdi/zaphfc/zaphfc.h >--- dahdi-linux-2.2.0.2/drivers/dahdi/zaphfc/zaphfc.h 1969-12-31 16:00:00.000000000 -0800 >+++ dahdi-svn-new/drivers/dahdi/zaphfc/zaphfc.h 2010-01-06 12:15:03.000000000 -0800 >@@ -0,0 +1,414 @@ >+/* >+ * zaphfc.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards >+ * >+ * Dahdi port by Jose A. Deniz <odicha@hotmail.com> >+ * >+ * Copyright (C) 2009 Jose A. Deniz >+ * Copyright (C) 2006 headissue GmbH; Jens Wilke >+ * Copyright (C) 2004 Daniele Orlandi >+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH >+ * >+ * Jens Wilke <jw_vzaphfc@headissue.com> >+ * >+ * Orginal author of this code is >+ * Daniele "Vihai" Orlandi <daniele@orlandi.com> >+ * >+ * Major rewrite of the driver made by >+ * Klaus-Peter Junghanns <kpj@junghanns.net> >+ * >+ * This program is free software and may be modified and >+ * distributed under the terms of the GNU Public License. >+ * >+ */ >+ >+#ifndef _HFC_ZAPHFC_H >+#define _HFC_ZAPHFC_H >+ >+#include <asm/io.h> >+ >+#define hfc_DRIVER_NAME "vzaphfc" >+#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " >+#define hfc_DRIVER_DESCR "HFC-S PCI A ISDN" >+#define hfc_DRIVER_VERSION "1.42" >+#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" >+ >+#define hfc_MAX_BOARDS 32 >+ >+#ifndef PCI_DMA_32BIT >+#define PCI_DMA_32BIT 0x00000000ffffffffULL >+#endif >+ >+#ifndef PCI_VENDOR_ID_SITECOM >+#define PCI_VENDOR_ID_SITECOM 0x182D >+#endif >+ >+#ifndef PCI_DEVICE_ID_SITECOM_3069 >+#define PCI_DEVICE_ID_SITECOM_3069 0x3069 >+#endif >+ >+#define hfc_RESET_DELAY 20 >+ >+#define hfc_CLKDEL_TE 0x0f /* CLKDEL in TE mode */ >+#define hfc_CLKDEL_NT 0x6c /* CLKDEL in NT mode */ >+ >+/* PCI memory mapped I/O */ >+ >+#define hfc_PCI_MEM_SIZE 0x0100 >+#define hfc_PCI_MWBA 0x80 >+ >+/* GCI/IOM bus monitor registers */ >+ >+#define hfc_C_I 0x08 >+#define hfc_TRxR 0x0C >+#define hfc_MON1_D 0x28 >+#define hfc_MON2_D 0x2C >+ >+ >+/* GCI/IOM bus timeslot registers */ >+ >+#define hfc_B1_SSL 0x80 >+#define hfc_B2_SSL 0x84 >+#define hfc_AUX1_SSL 0x88 >+#define hfc_AUX2_SSL 0x8C >+#define hfc_B1_RSL 0x90 >+#define hfc_B2_RSL 0x94 >+#define hfc_AUX1_RSL 0x98 >+#define hfc_AUX2_RSL 0x9C >+ >+/* GCI/IOM bus data registers */ >+ >+#define hfc_B1_D 0xA0 >+#define hfc_B2_D 0xA4 >+#define hfc_AUX1_D 0xA8 >+#define hfc_AUX2_D 0xAC >+ >+/* GCI/IOM bus configuration registers */ >+ >+#define hfc_MST_EMOD 0xB4 >+#define hfc_MST_MODE 0xB8 >+#define hfc_CONNECT 0xBC >+ >+ >+/* Interrupt and status registers */ >+ >+#define hfc_FIFO_EN 0x44 >+#define hfc_TRM 0x48 >+#define hfc_B_MODE 0x4C >+#define hfc_CHIP_ID 0x58 >+#define hfc_CIRM 0x60 >+#define hfc_CTMT 0x64 >+#define hfc_INT_M1 0x68 >+#define hfc_INT_M2 0x6C >+#define hfc_INT_S1 0x78 >+#define hfc_INT_S2 0x7C >+#define hfc_STATUS 0x70 >+ >+/* S/T section registers */ >+ >+#define hfc_STATES 0xC0 >+#define hfc_SCTRL 0xC4 >+#define hfc_SCTRL_E 0xC8 >+#define hfc_SCTRL_R 0xCC >+#define hfc_SQ 0xD0 >+#define hfc_CLKDEL 0xDC >+#define hfc_B1_REC 0xF0 >+#define hfc_B1_SEND 0xF0 >+#define hfc_B2_REC 0xF4 >+#define hfc_B2_SEND 0xF4 >+#define hfc_D_REC 0xF8 >+#define hfc_D_SEND 0xF8 >+#define hfc_E_REC 0xFC >+ >+/* Bits and values in various HFC PCI registers */ >+ >+/* bits in status register (READ) */ >+#define hfc_STATUS_PCI_PROC 0x02 >+#define hfc_STATUS_NBUSY 0x04 >+#define hfc_STATUS_TIMER_ELAP 0x10 >+#define hfc_STATUS_STATINT 0x20 >+#define hfc_STATUS_FRAMEINT 0x40 >+#define hfc_STATUS_ANYINT 0x80 >+ >+/* bits in CTMT (Write) */ >+#define hfc_CTMT_TRANSB1 0x01 >+#define hfc_CTMT_TRANSB2 0x02 >+#define hfc_CTMT_TIMER_CLEAR 0x80 >+#define hfc_CTMT_TIMER_MASK 0x1C >+#define hfc_CTMT_TIMER_3_125 (0x01 << 2) >+#define hfc_CTMT_TIMER_6_25 (0x02 << 2) >+#define hfc_CTMT_TIMER_12_5 (0x03 << 2) >+#define hfc_CTMT_TIMER_25 (0x04 << 2) >+#define hfc_CTMT_TIMER_50 (0x05 << 2) >+#define hfc_CTMT_TIMER_400 (0x06 << 2) >+#define hfc_CTMT_TIMER_800 (0x07 << 2) >+#define hfc_CTMT_AUTO_TIMER 0x20 >+ >+/* bits in CIRM (Write) */ >+#define hfc_CIRM_AUX_MSK 0x07 >+#define hfc_CIRM_RESET 0x08 >+#define hfc_CIRM_B1_REV 0x40 >+#define hfc_CIRM_B2_REV 0x80 >+ >+/* bits in INT_M1 and INT_S1 */ >+#define hfc_INTS_B1TRANS 0x01 >+#define hfc_INTS_B2TRANS 0x02 >+#define hfc_INTS_DTRANS 0x04 >+#define hfc_INTS_B1REC 0x08 >+#define hfc_INTS_B2REC 0x10 >+#define hfc_INTS_DREC 0x20 >+#define hfc_INTS_L1STATE 0x40 >+#define hfc_INTS_TIMER 0x80 >+ >+/* bits in INT_M2 */ >+#define hfc_M2_PROC_TRANS 0x01 >+#define hfc_M2_GCI_I_CHG 0x02 >+#define hfc_M2_GCI_MON_REC 0x04 >+#define hfc_M2_IRQ_ENABLE 0x08 >+#define hfc_M2_PMESEL 0x80 >+ >+/* bits in STATES */ >+#define hfc_STATES_STATE_MASK 0x0F >+#define hfc_STATES_LOAD_STATE 0x10 >+#define hfc_STATES_ACTIVATE 0x20 >+#define hfc_STATES_DO_ACTION 0x40 >+#define hfc_STATES_NT_G2_G3 0x80 >+ >+/* bits in HFCD_MST_MODE */ >+#define hfc_MST_MODE_MASTER 0x01 >+#define hfc_MST_MODE_SLAVE 0x00 >+/* remaining bits are for codecs control */ >+ >+/* bits in HFCD_SCTRL */ >+#define hfc_SCTRL_B1_ENA 0x01 >+#define hfc_SCTRL_B2_ENA 0x02 >+#define hfc_SCTRL_MODE_TE 0x00 >+#define hfc_SCTRL_MODE_NT 0x04 >+#define hfc_SCTRL_LOW_PRIO 0x08 >+#define hfc_SCTRL_SQ_ENA 0x10 >+#define hfc_SCTRL_TEST 0x20 >+#define hfc_SCTRL_NONE_CAP 0x40 >+#define hfc_SCTRL_PWR_DOWN 0x80 >+ >+/* bits in SCTRL_E */ >+#define hfc_SCTRL_E_AUTO_AWAKE 0x01 >+#define hfc_SCTRL_E_DBIT_1 0x04 >+#define hfc_SCTRL_E_IGNORE_COL 0x08 >+#define hfc_SCTRL_E_CHG_B1_B2 0x80 >+ >+/* bits in SCTRL_R */ >+#define hfc_SCTRL_R_B1_ENA 0x01 >+#define hfc_SCTRL_R_B2_ENA 0x02 >+ >+/* bits in FIFO_EN register */ >+#define hfc_FIFOEN_B1TX 0x01 >+#define hfc_FIFOEN_B1RX 0x02 >+#define hfc_FIFOEN_B2TX 0x04 >+#define hfc_FIFOEN_B2RX 0x08 >+#define hfc_FIFOEN_DTX 0x10 >+#define hfc_FIFOEN_DRX 0x20 >+ >+#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) >+#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) >+#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) >+ >+/* bits in the CONNECT register */ >+#define hfc_CONNECT_B1_HFC_from_ST 0x00 >+#define hfc_CONNECT_B1_HFC_from_GCI 0x01 >+#define hfc_CONNECT_B1_ST_from_HFC 0x00 >+#define hfc_CONNECT_B1_ST_from_GCI 0x02 >+#define hfc_CONNECT_B1_GCI_from_HFC 0x00 >+#define hfc_CONNECT_B1_GCI_from_ST 0x04 >+ >+#define hfc_CONNECT_B2_HFC_from_ST 0x00 >+#define hfc_CONNECT_B2_HFC_from_GCI 0x08 >+#define hfc_CONNECT_B2_ST_from_HFC 0x00 >+#define hfc_CONNECT_B2_ST_from_GCI 0x10 >+#define hfc_CONNECT_B2_GCI_from_HFC 0x00 >+#define hfc_CONNECT_B2_GCI_from_ST 0x20 >+ >+/* bits in the TRM register */ >+#define hfc_TRM_TRANS_INT_00 0x00 >+#define hfc_TRM_TRANS_INT_01 0x01 >+#define hfc_TRM_TRANS_INT_10 0x02 >+#define hfc_TRM_TRANS_INT_11 0x04 >+#define hfc_TRM_ECHO 0x20 >+#define hfc_TRM_B1_PLUS_B2 0x40 >+#define hfc_TRM_IOM_TEST_LOOP 0x80 >+ >+/* bits in the __SSL and __RSL registers */ >+#define hfc_SRSL_STIO 0x40 >+#define hfc_SRSL_ENABLE 0x80 >+#define hfc_SRCL_SLOT_MASK 0x1f >+ >+/* FIFO memory definitions */ >+ >+#define hfc_FIFO_SIZE 0x8000 >+ >+#define hfc_UGLY_FRAMEBUF 0x2000 >+ >+#define hfc_TX_FIFO_PRELOAD (DAHDI_CHUNKSIZE + 2) >+#define hfc_RX_FIFO_PRELOAD 4 >+ >+/* HDLC STUFF */ >+#define hfc_HDLC_BUF_LEN 32 >+/* arbitrary, just the max # of byts we will send to DAHDI per call */ >+ >+ >+/* NOTE: FIFO pointers are not declared volatile because accesses to the >+ * FIFOs are inherently safe. >+ */ >+ >+#ifdef DEBUG >+extern int debug_level; >+#endif >+ >+struct hfc_chan; >+ >+struct hfc_chan_simplex { >+ struct hfc_chan_duplex *chan; >+ >+ u8 zaptel_buffer[DAHDI_CHUNKSIZE]; >+ >+ u8 ugly_framebuf[hfc_UGLY_FRAMEBUF]; >+ int ugly_framebuf_size; >+ u16 ugly_framebuf_off; >+ >+ void *z1_base, *z2_base; >+ void *fifo_base; >+ void *z_base; >+ u16 z_min; >+ u16 z_max; >+ u16 fifo_size; >+ >+ u8 *f1, *f2; >+ u8 f_min; >+ u8 f_max; >+ u8 f_num; >+ >+ unsigned long long frames; >+ unsigned long long bytes; >+ unsigned long long fifo_full; >+ unsigned long long crc; >+ unsigned long long fifo_underrun; >+}; >+ >+enum hfc_chan_status { >+ free, >+ open_framed, >+ open_voice, >+ sniff_aux, >+ loopback, >+}; >+ >+struct hfc_chan_duplex { >+ struct hfc_card *card; >+ >+ char *name; >+ int number; >+ >+ enum hfc_chan_status status; >+ int open_by_netdev; >+ int open_by_zaptel; >+ >+ unsigned short protocol; >+ >+ spinlock_t lock; >+ >+ struct hfc_chan_simplex rx; >+ struct hfc_chan_simplex tx; >+ >+}; >+ >+typedef struct hfc_card { >+ int cardnum; >+ struct pci_dev *pcidev; >+ struct dahdi_hfc *ztdev; >+ struct proc_dir_entry *proc_dir; >+ char proc_dir_name[32]; >+ >+ struct proc_dir_entry *proc_info; >+ struct proc_dir_entry *proc_fifos; >+ struct proc_dir_entry *proc_bufs; >+ >+ unsigned long io_bus_mem; >+ void __iomem *io_mem; >+ >+ dma_addr_t fifo_bus_mem; >+ void *fifo_mem; >+ void *fifos; >+ >+ int nt_mode; >+ int sync_loss_reported; >+ int late_irqs; >+ >+ u8 l1_state; >+ int fifo_suspended; >+ int ignore_first_timer_interrupt; >+ >+ struct { >+ u8 m1; >+ u8 m2; >+ u8 fifo_en; >+ u8 trm; >+ u8 connect; >+ u8 sctrl; >+ u8 sctrl_r; >+ u8 sctrl_e; >+ u8 ctmt; >+ u8 cirm; >+ } regs; >+ >+ struct hfc_chan_duplex chans[3]; >+ int echo_enabled; >+ >+ >+ >+ int debug_event; >+ >+ spinlock_t lock; >+ unsigned int irq; >+ unsigned int iomem; >+ int ticks; >+ int clicks; >+ unsigned char *pci_io; >+ void *fifomem; /* start of the shared mem */ >+ >+ unsigned int pcibus; >+ unsigned int pcidevfn; >+ >+ int drecinframe; >+ >+ unsigned char cardno; >+ struct hfc_card *next; >+ >+} hfc_card; >+ >+typedef struct dahdi_hfc { >+ unsigned int usecount; >+ struct dahdi_span span; >+ struct dahdi_chan chans[3]; >+ struct dahdi_chan *_chans[3]; >+ struct hfc_card *card; >+ >+ /* pointer to the signalling channel for this span */ >+ struct dahdi_chan *sigchan; >+ /* nonzero means we're in the middle of sending an HDLC frame */ >+ int sigactive; >+ /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ >+ atomic_t hdlc_pending; >+ int frames_out; >+ int frames_in; >+ >+} dahdi_hfc; >+ >+static inline u8 hfc_inb(struct hfc_card *card, int offset) >+{ >+ return readb(card->io_mem + offset); >+} >+ >+static inline void hfc_outb(struct hfc_card *card, int offset, u8 value) >+{ >+ writeb(value, card->io_mem + offset); >+} >+ >+#endif
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 302316
: 222683 |
222685