[Pkg-voip-commits] r7979 - in /dahdi-linux/trunk: debian/changelog debian/patches/dahdi_linux_extra debian/patches/series drivers/
tzafrir-guest at alioth.debian.org
tzafrir-guest at alioth.debian.org
Thu Jan 21 11:50:20 UTC 2010
Author: tzafrir-guest
Date: Thu Jan 21 11:50:17 2010
New Revision: 7979
URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=7979
Log:
Replaced extra drivers and patches with huge patch dahdi_linux_extra
(Closes: #564720).
Added:
dahdi-linux/trunk/debian/patches/dahdi_linux_extra
Removed:
dahdi-linux/trunk/drivers/
Modified:
dahdi-linux/trunk/debian/changelog
dahdi-linux/trunk/debian/patches/series
Modified: dahdi-linux/trunk/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/trunk/debian/changelog?rev=7979&op=diff
==============================================================================
--- dahdi-linux/trunk/debian/changelog (original)
+++ dahdi-linux/trunk/debian/changelog Thu Jan 21 11:50:17 2010
@@ -10,8 +10,10 @@
* Patch uk_rotary (Closes: #546329).
* Override the bogus lintian warning for 'm-a a-i dahdi'
* Remove some remaining Zaptel left-overs.
-
- -- Tzafrir Cohen <tzafrir.cohen at xorcom.com> Thu, 21 Jan 2010 10:24:17 +0200
+ * Replaced extra drivers and patches with huge patch dahdi_linux_extra
+ (Closes: #564720).
+
+ -- Tzafrir Cohen <tzafrir.cohen at xorcom.com> Thu, 21 Jan 2010 13:18:27 +0200
dahdi-linux (1:2.2.0.2~dfsg-1) unstable; urgency=low
Added: dahdi-linux/trunk/debian/patches/dahdi_linux_extra
URL: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/trunk/debian/patches/dahdi_linux_extra?rev=7979&op=file
==============================================================================
--- dahdi-linux/trunk/debian/patches/dahdi_linux_extra (added)
+++ dahdi-linux/trunk/debian/patches/dahdi_linux_extra Thu Jan 21 11:50:17 2010
@@ -1,0 +1,9178 @@
+Subject: dahdi-extra: out-of-tree DAHDI drivers
+Origin: http://git.tzafrir.org.il/?p=dahdi-extra.git
+Forwarded: No
+Last-Update: 2010-01-21
+
+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: 88d86f570f2460fb1adfb645f527699d615ef125
+---
+
+diff -urN dahdi-svn-orig/drivers/dahdi/Kbuild dahdi-svn-new/drivers/dahdi/Kbuild
+--- dahdi-svn-orig/drivers/dahdi/Kbuild 2009-06-13 01:30:02.000000000 +0300
++++ dahdi-svn-new/drivers/dahdi/Kbuild 2010-01-21 13:35:37.000000000 +0200
+@@ -1,10 +1,16 @@
+ 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_ECHO) += ../staging/echo/
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETH) += dahdi_dynamic_eth.o
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_OSLEC) += dahdi_echocan_oslec.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-svn-orig/drivers/dahdi/Kconfig dahdi-svn-new/drivers/dahdi/Kconfig
+--- dahdi-svn-orig/drivers/dahdi/Kconfig 2009-04-29 20:48:39.000000000 +0300
++++ dahdi-svn-new/drivers/dahdi/Kconfig 2010-01-21 13:35:37.000000000 +0200
+@@ -279,3 +279,54 @@
+ 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 ECHO
++ tristate "Line Echo Canceller support"
++ default DAHDI
++ --help--
++ This driver provides line echo cancelling support for mISDN and
++ DAHDI drivers.
++
++ To compile this driver as a module, choose M here: the
++ module will be called echo.
++
++ If unsure, say Y.
++
+diff -urN dahdi-svn-orig/drivers/dahdi/opvxa1200.c dahdi-svn-new/drivers/dahdi/opvxa1200.c
+--- dahdi-svn-orig/drivers/dahdi/opvxa1200.c 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/opvxa1200.c 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,3018 @@
++/*
++ * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface
++ *
++ * Modify from wctdm.c by MiaoLin<miaolin at 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/interrupt.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.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 at openvox.com.cn>");
++MODULE_LICENSE("GPL v2");
++
++module_init(wctdm_init);
++module_exit(wctdm_cleanup);
+diff -urN dahdi-svn-orig/drivers/dahdi/wcopenpci.c dahdi-svn-new/drivers/dahdi/wcopenpci.c
+--- dahdi-svn-orig/drivers/dahdi/wcopenpci.c 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/wcopenpci.c 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,1842 @@
++/*
++ * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface
++ *
++ * Written by Mark Spencer <markster at linux-support.net>
++ * Matthew Fredrickson <creslin at linux-support.net>
++ * Ben Kramer <ben at voicetronix.com.au>
++ * Ron Lee <ron at 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 at digium.com> "\
++ "Voicetronix <support at 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-svn-orig/drivers/dahdi/zaphfc/base.c dahdi-svn-new/drivers/dahdi/zaphfc/base.c
+--- dahdi-svn-orig/drivers/dahdi/zaphfc/base.c 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/zaphfc/base.c 2010-01-21 13:35:37.000000000 +0200
+@@ -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 at 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 at headissue.com>
++ *
++ * Original author of this code is
++ * Daniele "Vihai" Orlandi <daniele at orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj at 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/sched.h>
++#include <linux/proc_fs.h>
++#include <linux/if_arp.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 at headissue.com>, "
++ "Daniele (Vihai) Orlandi <daniele at orlandi.com>, "
++ "Jose A. Deniz <odicha at 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-svn-orig/drivers/dahdi/zaphfc/fifo.c dahdi-svn-new/drivers/dahdi/zaphfc/fifo.c
+--- dahdi-svn-orig/drivers/dahdi/zaphfc/fifo.c 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/zaphfc/fifo.c 2010-01-21 13:35:37.000000000 +0200
+@@ -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 at 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-svn-orig/drivers/dahdi/zaphfc/fifo.h dahdi-svn-new/drivers/dahdi/zaphfc/fifo.h
+--- dahdi-svn-orig/drivers/dahdi/zaphfc/fifo.h 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/zaphfc/fifo.h 2010-01-21 13:35:37.000000000 +0200
+@@ -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 at orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj at 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-svn-orig/drivers/dahdi/zaphfc/Kbuild dahdi-svn-new/drivers/dahdi/zaphfc/Kbuild
+--- dahdi-svn-orig/drivers/dahdi/zaphfc/Kbuild 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/zaphfc/Kbuild 2010-01-21 13:35:37.000000000 +0200
+@@ -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-svn-orig/drivers/dahdi/zaphfc/zaphfc.h dahdi-svn-new/drivers/dahdi/zaphfc/zaphfc.h
+--- dahdi-svn-orig/drivers/dahdi/zaphfc/zaphfc.h 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/dahdi/zaphfc/zaphfc.h 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,414 @@
++/*
++ * zaphfc.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards
++ *
++ * Dahdi port by Jose A. Deniz <odicha at 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 at headissue.com>
++ *
++ * Orginal author of this code is
++ * Daniele "Vihai" Orlandi <daniele at orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj at 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
+diff -urN dahdi-svn-orig/drivers/staging/echo/echo.c dahdi-svn-new/drivers/staging/echo/echo.c
+--- dahdi-svn-orig/drivers/staging/echo/echo.c 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/echo.c 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,662 @@
++/*
++ * SpanDSP - a series of DSP components for telephony
++ *
++ * echo.c - A line echo canceller. This code is being developed
++ * against and partially complies with G168.
++ *
++ * Written by Steve Underwood <steveu at coppice.org>
++ * and David Rowe <david_at_rowetel_dot_com>
++ *
++ * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe
++ *
++ * Based on a bit from here, a bit from there, eye of toad, ear of
++ * bat, 15 years of failed attempts by David and a few fried brain
++ * cells.
++ *
++ * 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 version 2, as
++ * published by the Free Software Foundation.
++ *
++ * 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.
++ */
++
++/*! \file */
++
++/* Implementation Notes
++ David Rowe
++ April 2007
++
++ This code started life as Steve's NLMS algorithm with a tap
++ rotation algorithm to handle divergence during double talk. I
++ added a Geigel Double Talk Detector (DTD) [2] and performed some
++ G168 tests. However I had trouble meeting the G168 requirements,
++ especially for double talk - there were always cases where my DTD
++ failed, for example where near end speech was under the 6dB
++ threshold required for declaring double talk.
++
++ So I tried a two path algorithm [1], which has so far given better
++ results. The original tap rotation/Geigel algorithm is available
++ in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit.
++ It's probably possible to make it work if some one wants to put some
++ serious work into it.
++
++ At present no special treatment is provided for tones, which
++ generally cause NLMS algorithms to diverge. Initial runs of a
++ subset of the G168 tests for tones (e.g ./echo_test 6) show the
++ current algorithm is passing OK, which is kind of surprising. The
++ full set of tests needs to be performed to confirm this result.
++
++ One other interesting change is that I have managed to get the NLMS
++ code to work with 16 bit coefficients, rather than the original 32
++ bit coefficents. This reduces the MIPs and storage required.
++ I evaulated the 16 bit port using g168_tests.sh and listening tests
++ on 4 real-world samples.
++
++ I also attempted the implementation of a block based NLMS update
++ [2] but although this passes g168_tests.sh it didn't converge well
++ on the real-world samples. I have no idea why, perhaps a scaling
++ problem. The block based code is also available in SVN
++ http://svn.rowetel.com/software/oslec/tags/before_16bit. If this
++ code can be debugged, it will lead to further reduction in MIPS, as
++ the block update code maps nicely onto DSP instruction sets (it's a
++ dot product) compared to the current sample-by-sample update.
++
++ Steve also has some nice notes on echo cancellers in echo.h
++
++ References:
++
++ [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo
++ Path Models", IEEE Transactions on communications, COM-25,
++ No. 6, June
++ 1977.
++ http://www.rowetel.com/images/echo/dual_path_paper.pdf
++
++ [2] The classic, very useful paper that tells you how to
++ actually build a real world echo canceller:
++ Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice
++ Echo Canceller with a TMS320020,
++ http://www.rowetel.com/images/echo/spra129.pdf
++
++ [3] I have written a series of blog posts on this work, here is
++ Part 1: http://www.rowetel.com/blog/?p=18
++
++ [4] The source code http://svn.rowetel.com/software/oslec/
++
++ [5] A nice reference on LMS filters:
++ http://en.wikipedia.org/wiki/Least_mean_squares_filter
++
++ Credits:
++
++ Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan
++ Muthukrishnan for their suggestions and email discussions. Thanks
++ also to those people who collected echo samples for me such as
++ Mark, Pawel, and Pavel.
++*/
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include "echo.h"
++
++#define MIN_TX_POWER_FOR_ADAPTION 64
++#define MIN_RX_POWER_FOR_ADAPTION 64
++#define DTD_HANGOVER 600 /* 600 samples, or 75ms */
++#define DC_LOG2BETA 3 /* log2() of DC filter Beta */
++
++
++/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */
++
++#ifdef __bfin__
++static inline void lms_adapt_bg(struct oslec_state *ec, int clean,
++ int shift)
++{
++ int i, j;
++ int offset1;
++ int offset2;
++ int factor;
++ int exp;
++ int16_t *phist;
++ int n;
++
++ if (shift > 0)
++ factor = clean << shift;
++ else
++ factor = clean >> -shift;
++
++ /* Update the FIR taps */
++
++ offset2 = ec->curr_pos;
++ offset1 = ec->taps - offset2;
++ phist = &ec->fir_state_bg.history[offset2];
++
++ /* st: and en: help us locate the assembler in echo.s */
++
++ /* asm("st:"); */
++ n = ec->taps;
++ for (i = 0, j = offset2; i < n; i++, j++) {
++ exp = *phist++ * factor;
++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
++ }
++ /* asm("en:"); */
++
++ /* Note the asm for the inner loop above generated by Blackfin gcc
++ 4.1.1 is pretty good (note even parallel instructions used):
++
++ R0 = W [P0++] (X);
++ R0 *= R2;
++ R0 = R0 + R3 (NS) ||
++ R1 = W [P1] (X) ||
++ nop;
++ R0 >>>= 15;
++ R0 = R0 + R1;
++ W [P1++] = R0;
++
++ A block based update algorithm would be much faster but the
++ above can't be improved on much. Every instruction saved in
++ the loop above is 2 MIPs/ch! The for loop above is where the
++ Blackfin spends most of it's time - about 17 MIPs/ch measured
++ with speedtest.c with 256 taps (32ms). Write-back and
++ Write-through cache gave about the same performance.
++ */
++}
++
++/*
++ IDEAS for further optimisation of lms_adapt_bg():
++
++ 1/ The rounding is quite costly. Could we keep as 32 bit coeffs
++ then make filter pluck the MS 16-bits of the coeffs when filtering?
++ However this would lower potential optimisation of filter, as I
++ think the dual-MAC architecture requires packed 16 bit coeffs.
++
++ 2/ Block based update would be more efficient, as per comments above,
++ could use dual MAC architecture.
++
++ 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC
++ packing.
++
++ 4/ Execute the whole e/c in a block of say 20ms rather than sample
++ by sample. Processing a few samples every ms is inefficient.
++*/
++
++#else
++static inline void lms_adapt_bg(struct oslec_state *ec, int clean,
++ int shift)
++{
++ int i;
++
++ int offset1;
++ int offset2;
++ int factor;
++ int exp;
++
++ if (shift > 0)
++ factor = clean << shift;
++ else
++ factor = clean >> -shift;
++
++ /* Update the FIR taps */
++
++ offset2 = ec->curr_pos;
++ offset1 = ec->taps - offset2;
++
++ for (i = ec->taps - 1; i >= offset1; i--) {
++ exp = (ec->fir_state_bg.history[i - offset1] * factor);
++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
++ }
++ for (; i >= 0; i--) {
++ exp = (ec->fir_state_bg.history[i + offset2] * factor);
++ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15);
++ }
++}
++#endif
++
++static inline int top_bit(unsigned int bits)
++{
++ if (bits == 0)
++ return -1;
++ else
++ return (int)fls((int32_t)bits)-1;
++}
++
++struct oslec_state *oslec_create(int len, int adaption_mode)
++{
++ struct oslec_state *ec;
++ int i;
++
++ ec = kzalloc(sizeof(*ec), GFP_KERNEL);
++ if (!ec)
++ return NULL;
++
++ ec->taps = len;
++ ec->log2taps = top_bit(len);
++ ec->curr_pos = ec->taps - 1;
++
++ for (i = 0; i < 2; i++) {
++ ec->fir_taps16[i] =
++ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL);
++ if (!ec->fir_taps16[i])
++ goto error_oom;
++ }
++
++ fir16_create(&ec->fir_state, ec->fir_taps16[0], ec->taps);
++ fir16_create(&ec->fir_state_bg, ec->fir_taps16[1], ec->taps);
++
++ for (i = 0; i < 5; i++)
++ ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0;
++
++ ec->cng_level = 1000;
++ oslec_adaption_mode(ec, adaption_mode);
++
++ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL);
++ if (!ec->snapshot)
++ goto error_oom;
++
++ ec->cond_met = 0;
++ ec->Pstates = 0;
++ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0;
++ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0;
++ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0;
++ ec->Lbgn = ec->Lbgn_acc = 0;
++ ec->Lbgn_upper = 200;
++ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13;
++
++ return ec;
++
++error_oom:
++ for (i = 0; i < 2; i++)
++ kfree(ec->fir_taps16[i]);
++
++ kfree(ec);
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(oslec_create);
++
++void oslec_free(struct oslec_state *ec)
++{
++ int i;
++
++ fir16_free(&ec->fir_state);
++ fir16_free(&ec->fir_state_bg);
++ for (i = 0; i < 2; i++)
++ kfree(ec->fir_taps16[i]);
++ kfree(ec->snapshot);
++ kfree(ec);
++}
++EXPORT_SYMBOL_GPL(oslec_free);
++
++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode)
++{
++ ec->adaption_mode = adaption_mode;
++}
++EXPORT_SYMBOL_GPL(oslec_adaption_mode);
++
++void oslec_flush(struct oslec_state *ec)
++{
++ int i;
++
++ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0;
++ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0;
++ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0;
++
++ ec->Lbgn = ec->Lbgn_acc = 0;
++ ec->Lbgn_upper = 200;
++ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13;
++
++ ec->nonupdate_dwell = 0;
++
++ fir16_flush(&ec->fir_state);
++ fir16_flush(&ec->fir_state_bg);
++ ec->fir_state.curr_pos = ec->taps - 1;
++ ec->fir_state_bg.curr_pos = ec->taps - 1;
++ for (i = 0; i < 2; i++)
++ memset(ec->fir_taps16[i], 0, ec->taps * sizeof(int16_t));
++
++ ec->curr_pos = ec->taps - 1;
++ ec->Pstates = 0;
++}
++EXPORT_SYMBOL_GPL(oslec_flush);
++
++void oslec_snapshot(struct oslec_state *ec)
++{
++ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t));
++}
++EXPORT_SYMBOL_GPL(oslec_snapshot);
++
++/* Dual Path Echo Canceller */
++
++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx)
++{
++ int32_t echo_value;
++ int clean_bg;
++ int tmp, tmp1;
++
++ /*
++ * Input scaling was found be required to prevent problems when tx
++ * starts clipping. Another possible way to handle this would be the
++ * filter coefficent scaling.
++ */
++
++ ec->tx = tx;
++ ec->rx = rx;
++ tx >>= 1;
++ rx >>= 1;
++
++ /*
++ * Filter DC, 3dB point is 160Hz (I think), note 32 bit precision
++ * required otherwise values do not track down to 0. Zero at DC, Pole
++ * at (1-Beta) on real axis. Some chip sets (like Si labs) don't
++ * need this, but something like a $10 X100P card does. Any DC really
++ * slows down convergence.
++ *
++ * Note: removes some low frequency from the signal, this reduces the
++ * speech quality when listening to samples through headphones but may
++ * not be obvious through a telephone handset.
++ *
++ * Note that the 3dB frequency in radians is approx Beta, e.g. for Beta
++ * = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz.
++ */
++
++ if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) {
++ tmp = rx << 15;
++
++ /*
++ * Make sure the gain of the HPF is 1.0. This can still
++ * saturate a little under impulse conditions, and it might
++ * roll to 32768 and need clipping on sustained peak level
++ * signals. However, the scale of such clipping is small, and
++ * the error due to any saturation should not markedly affect
++ * the downstream processing.
++ */
++ tmp -= (tmp >> 4);
++
++ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2;
++
++ /*
++ * hard limit filter to prevent clipping. Note that at this
++ * stage rx should be limited to +/- 16383 due to right shift
++ * above
++ */
++ tmp1 = ec->rx_1 >> 15;
++ if (tmp1 > 16383)
++ tmp1 = 16383;
++ if (tmp1 < -16383)
++ tmp1 = -16383;
++ rx = tmp1;
++ ec->rx_2 = tmp;
++ }
++
++ /* Block average of power in the filter states. Used for
++ adaption power calculation. */
++
++ {
++ int new, old;
++
++ /* efficient "out with the old and in with the new" algorithm so
++ we don't have to recalculate over the whole block of
++ samples. */
++ new = (int)tx * (int)tx;
++ old = (int)ec->fir_state.history[ec->fir_state.curr_pos] *
++ (int)ec->fir_state.history[ec->fir_state.curr_pos];
++ ec->Pstates +=
++ ((new - old) + (1 << (ec->log2taps-1))) >> ec->log2taps;
++ if (ec->Pstates < 0)
++ ec->Pstates = 0;
++ }
++
++ /* Calculate short term average levels using simple single pole IIRs */
++
++ ec->Ltxacc += abs(tx) - ec->Ltx;
++ ec->Ltx = (ec->Ltxacc + (1 << 4)) >> 5;
++ ec->Lrxacc += abs(rx) - ec->Lrx;
++ ec->Lrx = (ec->Lrxacc + (1 << 4)) >> 5;
++
++ /* Foreground filter */
++
++ ec->fir_state.coeffs = ec->fir_taps16[0];
++ echo_value = fir16(&ec->fir_state, tx);
++ ec->clean = rx - echo_value;
++ ec->Lcleanacc += abs(ec->clean) - ec->Lclean;
++ ec->Lclean = (ec->Lcleanacc + (1 << 4)) >> 5;
++
++ /* Background filter */
++
++ echo_value = fir16(&ec->fir_state_bg, tx);
++ clean_bg = rx - echo_value;
++ ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg;
++ ec->Lclean_bg = (ec->Lclean_bgacc + (1 << 4)) >> 5;
++
++ /* Background Filter adaption */
++
++ /* Almost always adap bg filter, just simple DT and energy
++ detection to minimise adaption in cases of strong double talk.
++ However this is not critical for the dual path algorithm.
++ */
++ ec->factor = 0;
++ ec->shift = 0;
++ if ((ec->nonupdate_dwell == 0)) {
++ int P, logP, shift;
++
++ /* Determine:
++
++ f = Beta * clean_bg_rx/P ------ (1)
++
++ where P is the total power in the filter states.
++
++ The Boffins have shown that if we obey (1) we converge
++ quickly and avoid instability.
++
++ The correct factor f must be in Q30, as this is the fixed
++ point format required by the lms_adapt_bg() function,
++ therefore the scaled version of (1) is:
++
++ (2^30) * f = (2^30) * Beta * clean_bg_rx/P
++ factor = (2^30) * Beta * clean_bg_rx/P ----- (2)
++
++ We have chosen Beta = 0.25 by experiment, so:
++
++ factor = (2^30) * (2^-2) * clean_bg_rx/P
++
++ (30 - 2 - log2(P))
++ factor = clean_bg_rx 2 ----- (3)
++
++ To avoid a divide we approximate log2(P) as top_bit(P),
++ which returns the position of the highest non-zero bit in
++ P. This approximation introduces an error as large as a
++ factor of 2, but the algorithm seems to handle it OK.
++
++ Come to think of it a divide may not be a big deal on a
++ modern DSP, so its probably worth checking out the cycles
++ for a divide versus a top_bit() implementation.
++ */
++
++ P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates;
++ logP = top_bit(P) + ec->log2taps;
++ shift = 30 - 2 - logP;
++ ec->shift = shift;
++
++ lms_adapt_bg(ec, clean_bg, shift);
++ }
++
++ /* very simple DTD to make sure we dont try and adapt with strong
++ near end speech */
++
++ ec->adapt = 0;
++ if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx))
++ ec->nonupdate_dwell = DTD_HANGOVER;
++ if (ec->nonupdate_dwell)
++ ec->nonupdate_dwell--;
++
++ /* Transfer logic */
++
++ /* These conditions are from the dual path paper [1], I messed with
++ them a bit to improve performance. */
++
++ if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) &&
++ (ec->nonupdate_dwell == 0) &&
++ /* (ec->Lclean_bg < 0.875*ec->Lclean) */
++ (8 * ec->Lclean_bg < 7 * ec->Lclean) &&
++ /* (ec->Lclean_bg < 0.125*ec->Ltx) */
++ (8 * ec->Lclean_bg < ec->Ltx)) {
++ if (ec->cond_met == 6) {
++ /*
++ * BG filter has had better results for 6 consecutive
++ * samples
++ */
++ ec->adapt = 1;
++ memcpy(ec->fir_taps16[0], ec->fir_taps16[1],
++ ec->taps * sizeof(int16_t));
++ } else
++ ec->cond_met++;
++ } else
++ ec->cond_met = 0;
++
++ /* Non-Linear Processing */
++
++ ec->clean_nlp = ec->clean;
++ if (ec->adaption_mode & ECHO_CAN_USE_NLP) {
++ /*
++ * Non-linear processor - a fancy way to say "zap small
++ * signals, to avoid residual echo due to (uLaw/ALaw)
++ * non-linearity in the channel.".
++ */
++
++ if ((16 * ec->Lclean < ec->Ltx)) {
++ /*
++ * Our e/c has improved echo by at least 24 dB (each
++ * factor of 2 is 6dB, so 2*2*2*2=16 is the same as
++ * 6+6+6+6=24dB)
++ */
++ if (ec->adaption_mode & ECHO_CAN_USE_CNG) {
++ ec->cng_level = ec->Lbgn;
++
++ /*
++ * Very elementary comfort noise generation.
++ * Just random numbers rolled off very vaguely
++ * Hoth-like. DR: This noise doesn't sound
++ * quite right to me - I suspect there are some
++ * overlfow issues in the filtering as it's too
++ * "crackly".
++ * TODO: debug this, maybe just play noise at
++ * high level or look at spectrum.
++ */
++
++ ec->cng_rndnum =
++ 1664525U * ec->cng_rndnum + 1013904223U;
++ ec->cng_filter =
++ ((ec->cng_rndnum & 0xFFFF) - 32768 +
++ 5 * ec->cng_filter) >> 3;
++ ec->clean_nlp =
++ (ec->cng_filter * ec->cng_level * 8) >> 14;
++
++ } else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) {
++ /* This sounds much better than CNG */
++ if (ec->clean_nlp > ec->Lbgn)
++ ec->clean_nlp = ec->Lbgn;
++ if (ec->clean_nlp < -ec->Lbgn)
++ ec->clean_nlp = -ec->Lbgn;
++ } else {
++ /*
++ * just mute the residual, doesn't sound very
++ * good, used mainly in G168 tests
++ */
++ ec->clean_nlp = 0;
++ }
++ } else {
++ /*
++ * Background noise estimator. I tried a few
++ * algorithms here without much luck. This very simple
++ * one seems to work best, we just average the level
++ * using a slow (1 sec time const) filter if the
++ * current level is less than a (experimentally
++ * derived) constant. This means we dont include high
++ * level signals like near end speech. When combined
++ * with CNG or especially CLIP seems to work OK.
++ */
++ if (ec->Lclean < 40) {
++ ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn;
++ ec->Lbgn = (ec->Lbgn_acc + (1 << 11)) >> 12;
++ }
++ }
++ }
++
++ /* Roll around the taps buffer */
++ if (ec->curr_pos <= 0)
++ ec->curr_pos = ec->taps;
++ ec->curr_pos--;
++
++ if (ec->adaption_mode & ECHO_CAN_DISABLE)
++ ec->clean_nlp = rx;
++
++ /* Output scaled back up again to match input scaling */
++
++ return (int16_t) ec->clean_nlp << 1;
++}
++EXPORT_SYMBOL_GPL(oslec_update);
++
++/* This function is seperated from the echo canceller is it is usually called
++ as part of the tx process. See rx HP (DC blocking) filter above, it's
++ the same design.
++
++ Some soft phones send speech signals with a lot of low frequency
++ energy, e.g. down to 20Hz. This can make the hybrid non-linear
++ which causes the echo canceller to fall over. This filter can help
++ by removing any low frequency before it gets to the tx port of the
++ hybrid.
++
++ It can also help by removing and DC in the tx signal. DC is bad
++ for LMS algorithms.
++
++ This is one of the classic DC removal filters, adjusted to provide
++ sufficient bass rolloff to meet the above requirement to protect hybrids
++ from things that upset them. The difference between successive samples
++ produces a lousy HPF, and then a suitably placed pole flattens things out.
++ The final result is a nicely rolled off bass end. The filtering is
++ implemented with extended fractional precision, which noise shapes things,
++ giving very clean DC removal.
++*/
++
++int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx)
++{
++ int tmp, tmp1;
++
++ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) {
++ tmp = tx << 15;
++
++ /*
++ * Make sure the gain of the HPF is 1.0. The first can still
++ * saturate a little under impulse conditions, and it might
++ * roll to 32768 and need clipping on sustained peak level
++ * signals. However, the scale of such clipping is small, and
++ * the error due to any saturation should not markedly affect
++ * the downstream processing.
++ */
++ tmp -= (tmp >> 4);
++
++ ec->tx_1 += -(ec->tx_1 >> DC_LOG2BETA) + tmp - ec->tx_2;
++ tmp1 = ec->tx_1 >> 15;
++ if (tmp1 > 32767)
++ tmp1 = 32767;
++ if (tmp1 < -32767)
++ tmp1 = -32767;
++ tx = tmp1;
++ ec->tx_2 = tmp;
++ }
++
++ return tx;
++}
++EXPORT_SYMBOL_GPL(oslec_hpf_tx);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Rowe");
++MODULE_DESCRIPTION("Open Source Line Echo Canceller");
++MODULE_VERSION("0.3.0");
+diff -urN dahdi-svn-orig/drivers/staging/echo/echo.h dahdi-svn-new/drivers/staging/echo/echo.h
+--- dahdi-svn-orig/drivers/staging/echo/echo.h 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/echo.h 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,175 @@
++/*
++ * SpanDSP - a series of DSP components for telephony
++ *
++ * echo.c - A line echo canceller. This code is being developed
++ * against and partially complies with G168.
++ *
++ * Written by Steve Underwood <steveu at coppice.org>
++ * and David Rowe <david_at_rowetel_dot_com>
++ *
++ * Copyright (C) 2001 Steve Underwood and 2007 David Rowe
++ *
++ * 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 version 2, as
++ * published by the Free Software Foundation.
++ *
++ * 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.
++ */
++
++#ifndef __ECHO_H
++#define __ECHO_H
++
++/*
++Line echo cancellation for voice
++
++What does it do?
++
++This module aims to provide G.168-2002 compliant echo cancellation, to remove
++electrical echoes (e.g. from 2-4 wire hybrids) from voice calls.
++
++
++How does it work?
++
++The heart of the echo cancellor is FIR filter. This is adapted to match the
++echo impulse response of the telephone line. It must be long enough to
++adequately cover the duration of that impulse response. The signal transmitted
++to the telephone line is passed through the FIR filter. Once the FIR is
++properly adapted, the resulting output is an estimate of the echo signal
++received from the line. This is subtracted from the received signal. The result
++is an estimate of the signal which originated at the far end of the line, free
++from echos of our own transmitted signal.
++
++The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and
++was introduced in 1960. It is the commonest form of filter adaption used in
++things like modem line equalisers and line echo cancellers. There it works very
++well. However, it only works well for signals of constant amplitude. It works
++very poorly for things like speech echo cancellation, where the signal level
++varies widely. This is quite easy to fix. If the signal level is normalised -
++similar to applying AGC - LMS can work as well for a signal of varying
++amplitude as it does for a modem signal. This normalised least mean squares
++(NLMS) algorithm is the commonest one used for speech echo cancellation. Many
++other algorithms exist - e.g. RLS (essentially the same as Kalman filtering),
++FAP, etc. Some perform significantly better than NLMS. However, factors such
++as computational complexity and patents favour the use of NLMS.
++
++A simple refinement to NLMS can improve its performance with speech. NLMS tends
++to adapt best to the strongest parts of a signal. If the signal is white noise,
++the NLMS algorithm works very well. However, speech has more low frequency than
++high frequency content. Pre-whitening (i.e. filtering the signal to flatten its
++spectrum) the echo signal improves the adapt rate for speech, and ensures the
++final residual signal is not heavily biased towards high frequencies. A very
++low complexity filter is adequate for this, so pre-whitening adds little to the
++compute requirements of the echo canceller.
++
++An FIR filter adapted using pre-whitened NLMS performs well, provided certain
++conditions are met:
++
++ - The transmitted signal has poor self-correlation.
++ - There is no signal being generated within the environment being
++ cancelled.
++
++The difficulty is that neither of these can be guaranteed.
++
++If the adaption is performed while transmitting noise (or something fairly
++noise like, such as voice) the adaption works very well. If the adaption is
++performed while transmitting something highly correlative (typically narrow
++band energy such as signalling tones or DTMF), the adaption can go seriously
++wrong. The reason is there is only one solution for the adaption on a near
++random signal - the impulse response of the line. For a repetitive signal,
++there are any number of solutions which converge the adaption, and nothing
++guides the adaption to choose the generalised one. Allowing an untrained
++canceller to converge on this kind of narrowband energy probably a good thing,
++since at least it cancels the tones. Allowing a well converged canceller to
++continue converging on such energy is just a way to ruin its generalised
++adaption. A narrowband detector is needed, so adapation can be suspended at
++appropriate times.
++
++The adaption process is based on trying to eliminate the received signal. When
++there is any signal from within the environment being cancelled it may upset
++the adaption process. Similarly, if the signal we are transmitting is small,
++noise may dominate and disturb the adaption process. If we can ensure that the
++adaption is only performed when we are transmitting a significant signal level,
++and the environment is not, things will be OK. Clearly, it is easy to tell when
++we are sending a significant signal. Telling, if the environment is generating
++a significant signal, and doing it with sufficient speed that the adaption will
++not have diverged too much more we stop it, is a little harder.
++
++The key problem in detecting when the environment is sourcing significant
++energy is that we must do this very quickly. Given a reasonably long sample of
++the received signal, there are a number of strategies which may be used to
++assess whether that signal contains a strong far end component. However, by the
++time that assessment is complete the far end signal will have already caused
++major mis-convergence in the adaption process. An assessment algorithm is
++needed which produces a fairly accurate result from a very short burst of far
++end energy.
++
++How do I use it?
++
++The echo cancellor processes both the transmit and receive streams sample by
++sample. The processing function is not declared inline. Unfortunately,
++cancellation requires many operations per sample, so the call overhead is only
++a minor burden.
++*/
++
++#include "fir.h"
++#include "oslec.h"
++
++/*
++ G.168 echo canceller descriptor. This defines the working state for a line
++ echo canceller.
++*/
++struct oslec_state {
++ int16_t tx, rx;
++ int16_t clean;
++ int16_t clean_nlp;
++
++ int nonupdate_dwell;
++ int curr_pos;
++ int taps;
++ int log2taps;
++ int adaption_mode;
++
++ int cond_met;
++ int32_t Pstates;
++ int16_t adapt;
++ int32_t factor;
++ int16_t shift;
++
++ /* Average levels and averaging filter states */
++ int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc;
++ int Ltx, Lrx;
++ int Lclean;
++ int Lclean_bg;
++ int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc;
++
++ /* foreground and background filter states */
++ struct fir16_state_t fir_state;
++ struct fir16_state_t fir_state_bg;
++ int16_t *fir_taps16[2];
++
++ /* DC blocking filter states */
++ int tx_1, tx_2, rx_1, rx_2;
++
++ /* optional High Pass Filter states */
++ int32_t xvtx[5], yvtx[5];
++ int32_t xvrx[5], yvrx[5];
++
++ /* Parameters for the optional Hoth noise generator */
++ int cng_level;
++ int cng_rndnum;
++ int cng_filter;
++
++ /* snapshot sample of coeffs used for development */
++ int16_t *snapshot;
++};
++
++#endif /* __ECHO_H */
+diff -urN dahdi-svn-orig/drivers/staging/echo/echo.mod.c dahdi-svn-new/drivers/staging/echo/echo.mod.c
+--- dahdi-svn-orig/drivers/staging/echo/echo.mod.c 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/echo.mod.c 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,17 @@
++#include <linux/module.h>
++#include <linux/vermagic.h>
++#include <linux/compiler.h>
++
++MODULE_INFO(vermagic, VERMAGIC_STRING);
++
++struct module __this_module
++__attribute__((section(".gnu.linkonce.this_module"))) = {
++ .name = KBUILD_MODNAME,
++ .arch = MODULE_ARCH_INIT,
++};
++
++static const char __module_depends[]
++__used
++__attribute__((section(".modinfo"))) =
++"depends=";
++
+diff -urN dahdi-svn-orig/drivers/staging/echo/fir.h dahdi-svn-new/drivers/staging/echo/fir.h
+--- dahdi-svn-orig/drivers/staging/echo/fir.h 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/fir.h 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,286 @@
++/*
++ * SpanDSP - a series of DSP components for telephony
++ *
++ * fir.h - General telephony FIR routines
++ *
++ * Written by Steve Underwood <steveu at coppice.org>
++ *
++ * Copyright (C) 2002 Steve Underwood
++ *
++ * 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 version 2, as
++ * published by the Free Software Foundation.
++ *
++ * 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.
++ */
++
++#if !defined(_FIR_H_)
++#define _FIR_H_
++
++/*
++ Blackfin NOTES & IDEAS:
++
++ A simple dot product function is used to implement the filter. This performs
++ just one MAC/cycle which is inefficient but was easy to implement as a first
++ pass. The current Blackfin code also uses an unrolled form of the filter
++ history to avoid 0 length hardware loop issues. This is wasteful of
++ memory.
++
++ Ideas for improvement:
++
++ 1/ Rewrite filter for dual MAC inner loop. The issue here is handling
++ history sample offsets that are 16 bit aligned - the dual MAC needs
++ 32 bit aligmnent. There are some good examples in libbfdsp.
++
++ 2/ Use the hardware circular buffer facility tohalve memory usage.
++
++ 3/ Consider using internal memory.
++
++ Using less memory might also improve speed as cache misses will be
++ reduced. A drop in MIPs and memory approaching 50% should be
++ possible.
++
++ The foreground and background filters currenlty use a total of
++ about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo
++ can.
++*/
++
++#if defined(USE_MMX) || defined(USE_SSE2)
++#include "mmx.h"
++#endif
++
++/*
++ * 16 bit integer FIR descriptor. This defines the working state for a single
++ * instance of an FIR filter using 16 bit integer coefficients.
++ */
++struct fir16_state_t {
++ int taps;
++ int curr_pos;
++ const int16_t *coeffs;
++ int16_t *history;
++};
++
++/*
++ * 32 bit integer FIR descriptor. This defines the working state for a single
++ * instance of an FIR filter using 32 bit integer coefficients, and filtering
++ * 16 bit integer data.
++ */
++struct fir32_state_t {
++ int taps;
++ int curr_pos;
++ const int32_t *coeffs;
++ int16_t *history;
++};
++
++/*
++ * Floating point FIR descriptor. This defines the working state for a single
++ * instance of an FIR filter using floating point coefficients and data.
++ */
++struct fir_float_state_t {
++ int taps;
++ int curr_pos;
++ const float *coeffs;
++ float *history;
++};
++
++static inline const int16_t *fir16_create(struct fir16_state_t *fir,
++ const int16_t *coeffs, int taps)
++{
++ fir->taps = taps;
++ fir->curr_pos = taps - 1;
++ fir->coeffs = coeffs;
++#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__)
++ fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL);
++#else
++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
++#endif
++ return fir->history;
++}
++
++static inline void fir16_flush(struct fir16_state_t *fir)
++{
++#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__)
++ memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t));
++#else
++ memset(fir->history, 0, fir->taps * sizeof(int16_t));
++#endif
++}
++
++static inline void fir16_free(struct fir16_state_t *fir)
++{
++ kfree(fir->history);
++}
++
++#ifdef __bfin__
++static inline int32_t dot_asm(short *x, short *y, int len)
++{
++ int dot;
++
++ len--;
++
++ __asm__("I0 = %1;\n\t"
++ "I1 = %2;\n\t"
++ "A0 = 0;\n\t"
++ "R0.L = W[I0++] || R1.L = W[I1++];\n\t"
++ "LOOP dot%= LC0 = %3;\n\t"
++ "LOOP_BEGIN dot%=;\n\t"
++ "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t"
++ "LOOP_END dot%=;\n\t"
++ "A0 += R0.L*R1.L (IS);\n\t"
++ "R0 = A0;\n\t"
++ "%0 = R0;\n\t"
++ : "=&d"(dot)
++ : "a"(x), "a"(y), "a"(len)
++ : "I0", "I1", "A1", "A0", "R0", "R1"
++ );
++
++ return dot;
++}
++#endif
++
++static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample)
++{
++ int32_t y;
++#if defined(USE_MMX)
++ int i;
++ union mmx_t *mmx_coeffs;
++ union mmx_t *mmx_hist;
++
++ fir->history[fir->curr_pos] = sample;
++ fir->history[fir->curr_pos + fir->taps] = sample;
++
++ mmx_coeffs = (union mmx_t *) fir->coeffs;
++ mmx_hist = (union mmx_t *) &fir->history[fir->curr_pos];
++ i = fir->taps;
++ pxor_r2r(mm4, mm4);
++ /* 8 samples per iteration, so the filter must be a multiple of
++ 8 long. */
++ while (i > 0) {
++ movq_m2r(mmx_coeffs[0], mm0);
++ movq_m2r(mmx_coeffs[1], mm2);
++ movq_m2r(mmx_hist[0], mm1);
++ movq_m2r(mmx_hist[1], mm3);
++ mmx_coeffs += 2;
++ mmx_hist += 2;
++ pmaddwd_r2r(mm1, mm0);
++ pmaddwd_r2r(mm3, mm2);
++ paddd_r2r(mm0, mm4);
++ paddd_r2r(mm2, mm4);
++ i -= 8;
++ }
++ movq_r2r(mm4, mm0);
++ psrlq_i2r(32, mm0);
++ paddd_r2r(mm0, mm4);
++ movd_r2m(mm4, y);
++ emms();
++#elif defined(USE_SSE2)
++ int i;
++ union xmm_t *xmm_coeffs;
++ union xmm_t *xmm_hist;
++
++ fir->history[fir->curr_pos] = sample;
++ fir->history[fir->curr_pos + fir->taps] = sample;
++
++ xmm_coeffs = (union xmm_t *) fir->coeffs;
++ xmm_hist = (union xmm_t *) &fir->history[fir->curr_pos];
++ i = fir->taps;
++ pxor_r2r(xmm4, xmm4);
++ /* 16 samples per iteration, so the filter must be a multiple of
++ 16 long. */
++ while (i > 0) {
++ movdqu_m2r(xmm_coeffs[0], xmm0);
++ movdqu_m2r(xmm_coeffs[1], xmm2);
++ movdqu_m2r(xmm_hist[0], xmm1);
++ movdqu_m2r(xmm_hist[1], xmm3);
++ xmm_coeffs += 2;
++ xmm_hist += 2;
++ pmaddwd_r2r(xmm1, xmm0);
++ pmaddwd_r2r(xmm3, xmm2);
++ paddd_r2r(xmm0, xmm4);
++ paddd_r2r(xmm2, xmm4);
++ i -= 16;
++ }
++ movdqa_r2r(xmm4, xmm0);
++ psrldq_i2r(8, xmm0);
++ paddd_r2r(xmm0, xmm4);
++ movdqa_r2r(xmm4, xmm0);
++ psrldq_i2r(4, xmm0);
++ paddd_r2r(xmm0, xmm4);
++ movd_r2m(xmm4, y);
++#elif defined(__bfin__)
++ fir->history[fir->curr_pos] = sample;
++ fir->history[fir->curr_pos + fir->taps] = sample;
++ y = dot_asm((int16_t *) fir->coeffs, &fir->history[fir->curr_pos],
++ fir->taps);
++#else
++ int i;
++ int offset1;
++ int offset2;
++
++ fir->history[fir->curr_pos] = sample;
++
++ offset2 = fir->curr_pos;
++ offset1 = fir->taps - offset2;
++ y = 0;
++ for (i = fir->taps - 1; i >= offset1; i--)
++ y += fir->coeffs[i] * fir->history[i - offset1];
++ for (; i >= 0; i--)
++ y += fir->coeffs[i] * fir->history[i + offset2];
++#endif
++ if (fir->curr_pos <= 0)
++ fir->curr_pos = fir->taps;
++ fir->curr_pos--;
++ return (int16_t) (y >> 15);
++}
++
++static inline const int16_t *fir32_create(struct fir32_state_t *fir,
++ const int32_t *coeffs, int taps)
++{
++ fir->taps = taps;
++ fir->curr_pos = taps - 1;
++ fir->coeffs = coeffs;
++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL);
++ return fir->history;
++}
++
++static inline void fir32_flush(struct fir32_state_t *fir)
++{
++ memset(fir->history, 0, fir->taps * sizeof(int16_t));
++}
++
++static inline void fir32_free(struct fir32_state_t *fir)
++{
++ kfree(fir->history);
++}
++
++static inline int16_t fir32(struct fir32_state_t *fir, int16_t sample)
++{
++ int i;
++ int32_t y;
++ int offset1;
++ int offset2;
++
++ fir->history[fir->curr_pos] = sample;
++ offset2 = fir->curr_pos;
++ offset1 = fir->taps - offset2;
++ y = 0;
++ for (i = fir->taps - 1; i >= offset1; i--)
++ y += fir->coeffs[i] * fir->history[i - offset1];
++ for (; i >= 0; i--)
++ y += fir->coeffs[i] * fir->history[i + offset2];
++ if (fir->curr_pos <= 0)
++ fir->curr_pos = fir->taps;
++ fir->curr_pos--;
++ return (int16_t) (y >> 15);
++}
++
++#endif
+diff -urN dahdi-svn-orig/drivers/staging/echo/Kbuild dahdi-svn-new/drivers/staging/echo/Kbuild
+--- dahdi-svn-orig/drivers/staging/echo/Kbuild 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/Kbuild 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,6 @@
++ifdef DAHDI_USE_MMX
++EXTRA_CFLAGS += -DUSE_MMX
++endif
++
++# An explicit 'obj-m' , unlike the Makefile
++obj-m += echo.o
+diff -urN dahdi-svn-orig/drivers/staging/echo/mmx.h dahdi-svn-new/drivers/staging/echo/mmx.h
+--- dahdi-svn-orig/drivers/staging/echo/mmx.h 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/mmx.h 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,288 @@
++/*
++ * mmx.h
++ * Copyright (C) 1997-2001 H. Dietz and R. Fisher
++ *
++ * This file is part of FFmpeg.
++ *
++ * FFmpeg is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * FFmpeg 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with FFmpeg; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++#ifndef AVCODEC_I386MMX_H
++#define AVCODEC_I386MMX_H
++
++/*
++ * The type of an value that fits in an MMX register (note that long
++ * long constant values MUST be suffixed by LL and unsigned long long
++ * values by ULL, lest they be truncated by the compiler)
++ */
++
++union mmx_t {
++ long long q; /* Quadword (64-bit) value */
++ unsigned long long uq; /* Unsigned Quadword */
++ int d[2]; /* 2 Doubleword (32-bit) values */
++ unsigned int ud[2]; /* 2 Unsigned Doubleword */
++ short w[4]; /* 4 Word (16-bit) values */
++ unsigned short uw[4]; /* 4 Unsigned Word */
++ char b[8]; /* 8 Byte (8-bit) values */
++ unsigned char ub[8]; /* 8 Unsigned Byte */
++ float s[2]; /* Single-precision (32-bit) value */
++}; /* On an 8-byte (64-bit) boundary */
++
++/* SSE registers */
++union xmm_t {
++ char b[16];
++};
++
++
++#define mmx_i2r(op, imm, reg) \
++ __asm__ __volatile__ (#op " %0, %%" #reg \
++ : /* nothing */ \
++ : "i" (imm))
++
++#define mmx_m2r(op, mem, reg) \
++ __asm__ __volatile__ (#op " %0, %%" #reg \
++ : /* nothing */ \
++ : "m" (mem))
++
++#define mmx_r2m(op, reg, mem) \
++ __asm__ __volatile__ (#op " %%" #reg ", %0" \
++ : "=m" (mem) \
++ : /* nothing */)
++
++#define mmx_r2r(op, regs, regd) \
++ __asm__ __volatile__ (#op " %" #regs ", %" #regd)
++
++
++#define emms() __asm__ __volatile__ ("emms")
++
++#define movd_m2r(var, reg) mmx_m2r(movd, var, reg)
++#define movd_r2m(reg, var) mmx_r2m(movd, reg, var)
++#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd)
++
++#define movq_m2r(var, reg) mmx_m2r(movq, var, reg)
++#define movq_r2m(reg, var) mmx_r2m(movq, reg, var)
++#define movq_r2r(regs, regd) mmx_r2r(movq, regs, regd)
++
++#define packssdw_m2r(var, reg) mmx_m2r(packssdw, var, reg)
++#define packssdw_r2r(regs, regd) mmx_r2r(packssdw, regs, regd)
++#define packsswb_m2r(var, reg) mmx_m2r(packsswb, var, reg)
++#define packsswb_r2r(regs, regd) mmx_r2r(packsswb, regs, regd)
++
++#define packuswb_m2r(var, reg) mmx_m2r(packuswb, var, reg)
++#define packuswb_r2r(regs, regd) mmx_r2r(packuswb, regs, regd)
++
++#define paddb_m2r(var, reg) mmx_m2r(paddb, var, reg)
++#define paddb_r2r(regs, regd) mmx_r2r(paddb, regs, regd)
++#define paddd_m2r(var, reg) mmx_m2r(paddd, var, reg)
++#define paddd_r2r(regs, regd) mmx_r2r(paddd, regs, regd)
++#define paddw_m2r(var, reg) mmx_m2r(paddw, var, reg)
++#define paddw_r2r(regs, regd) mmx_r2r(paddw, regs, regd)
++
++#define paddsb_m2r(var, reg) mmx_m2r(paddsb, var, reg)
++#define paddsb_r2r(regs, regd) mmx_r2r(paddsb, regs, regd)
++#define paddsw_m2r(var, reg) mmx_m2r(paddsw, var, reg)
++#define paddsw_r2r(regs, regd) mmx_r2r(paddsw, regs, regd)
++
++#define paddusb_m2r(var, reg) mmx_m2r(paddusb, var, reg)
++#define paddusb_r2r(regs, regd) mmx_r2r(paddusb, regs, regd)
++#define paddusw_m2r(var, reg) mmx_m2r(paddusw, var, reg)
++#define paddusw_r2r(regs, regd) mmx_r2r(paddusw, regs, regd)
++
++#define pand_m2r(var, reg) mmx_m2r(pand, var, reg)
++#define pand_r2r(regs, regd) mmx_r2r(pand, regs, regd)
++
++#define pandn_m2r(var, reg) mmx_m2r(pandn, var, reg)
++#define pandn_r2r(regs, regd) mmx_r2r(pandn, regs, regd)
++
++#define pcmpeqb_m2r(var, reg) mmx_m2r(pcmpeqb, var, reg)
++#define pcmpeqb_r2r(regs, regd) mmx_r2r(pcmpeqb, regs, regd)
++#define pcmpeqd_m2r(var, reg) mmx_m2r(pcmpeqd, var, reg)
++#define pcmpeqd_r2r(regs, regd) mmx_r2r(pcmpeqd, regs, regd)
++#define pcmpeqw_m2r(var, reg) mmx_m2r(pcmpeqw, var, reg)
++#define pcmpeqw_r2r(regs, regd) mmx_r2r(pcmpeqw, regs, regd)
++
++#define pcmpgtb_m2r(var, reg) mmx_m2r(pcmpgtb, var, reg)
++#define pcmpgtb_r2r(regs, regd) mmx_r2r(pcmpgtb, regs, regd)
++#define pcmpgtd_m2r(var, reg) mmx_m2r(pcmpgtd, var, reg)
++#define pcmpgtd_r2r(regs, regd) mmx_r2r(pcmpgtd, regs, regd)
++#define pcmpgtw_m2r(var, reg) mmx_m2r(pcmpgtw, var, reg)
++#define pcmpgtw_r2r(regs, regd) mmx_r2r(pcmpgtw, regs, regd)
++
++#define pmaddwd_m2r(var, reg) mmx_m2r(pmaddwd, var, reg)
++#define pmaddwd_r2r(regs, regd) mmx_r2r(pmaddwd, regs, regd)
++
++#define pmulhw_m2r(var, reg) mmx_m2r(pmulhw, var, reg)
++#define pmulhw_r2r(regs, regd) mmx_r2r(pmulhw, regs, regd)
++
++#define pmullw_m2r(var, reg) mmx_m2r(pmullw, var, reg)
++#define pmullw_r2r(regs, regd) mmx_r2r(pmullw, regs, regd)
++
++#define por_m2r(var, reg) mmx_m2r(por, var, reg)
++#define por_r2r(regs, regd) mmx_r2r(por, regs, regd)
++
++#define pslld_i2r(imm, reg) mmx_i2r(pslld, imm, reg)
++#define pslld_m2r(var, reg) mmx_m2r(pslld, var, reg)
++#define pslld_r2r(regs, regd) mmx_r2r(pslld, regs, regd)
++#define psllq_i2r(imm, reg) mmx_i2r(psllq, imm, reg)
++#define psllq_m2r(var, reg) mmx_m2r(psllq, var, reg)
++#define psllq_r2r(regs, regd) mmx_r2r(psllq, regs, regd)
++#define psllw_i2r(imm, reg) mmx_i2r(psllw, imm, reg)
++#define psllw_m2r(var, reg) mmx_m2r(psllw, var, reg)
++#define psllw_r2r(regs, regd) mmx_r2r(psllw, regs, regd)
++
++#define psrad_i2r(imm, reg) mmx_i2r(psrad, imm, reg)
++#define psrad_m2r(var, reg) mmx_m2r(psrad, var, reg)
++#define psrad_r2r(regs, regd) mmx_r2r(psrad, regs, regd)
++#define psraw_i2r(imm, reg) mmx_i2r(psraw, imm, reg)
++#define psraw_m2r(var, reg) mmx_m2r(psraw, var, reg)
++#define psraw_r2r(regs, regd) mmx_r2r(psraw, regs, regd)
++
++#define psrld_i2r(imm, reg) mmx_i2r(psrld, imm, reg)
++#define psrld_m2r(var, reg) mmx_m2r(psrld, var, reg)
++#define psrld_r2r(regs, regd) mmx_r2r(psrld, regs, regd)
++#define psrlq_i2r(imm, reg) mmx_i2r(psrlq, imm, reg)
++#define psrlq_m2r(var, reg) mmx_m2r(psrlq, var, reg)
++#define psrlq_r2r(regs, regd) mmx_r2r(psrlq, regs, regd)
++#define psrlw_i2r(imm, reg) mmx_i2r(psrlw, imm, reg)
++#define psrlw_m2r(var, reg) mmx_m2r(psrlw, var, reg)
++#define psrlw_r2r(regs, regd) mmx_r2r(psrlw, regs, regd)
++
++#define psubb_m2r(var, reg) mmx_m2r(psubb, var, reg)
++#define psubb_r2r(regs, regd) mmx_r2r(psubb, regs, regd)
++#define psubd_m2r(var, reg) mmx_m2r(psubd, var, reg)
++#define psubd_r2r(regs, regd) mmx_r2r(psubd, regs, regd)
++#define psubw_m2r(var, reg) mmx_m2r(psubw, var, reg)
++#define psubw_r2r(regs, regd) mmx_r2r(psubw, regs, regd)
++
++#define psubsb_m2r(var, reg) mmx_m2r(psubsb, var, reg)
++#define psubsb_r2r(regs, regd) mmx_r2r(psubsb, regs, regd)
++#define psubsw_m2r(var, reg) mmx_m2r(psubsw, var, reg)
++#define psubsw_r2r(regs, regd) mmx_r2r(psubsw, regs, regd)
++
++#define psubusb_m2r(var, reg) mmx_m2r(psubusb, var, reg)
++#define psubusb_r2r(regs, regd) mmx_r2r(psubusb, regs, regd)
++#define psubusw_m2r(var, reg) mmx_m2r(psubusw, var, reg)
++#define psubusw_r2r(regs, regd) mmx_r2r(psubusw, regs, regd)
++
++#define punpckhbw_m2r(var, reg) mmx_m2r(punpckhbw, var, reg)
++#define punpckhbw_r2r(regs, regd) mmx_r2r(punpckhbw, regs, regd)
++#define punpckhdq_m2r(var, reg) mmx_m2r(punpckhdq, var, reg)
++#define punpckhdq_r2r(regs, regd) mmx_r2r(punpckhdq, regs, regd)
++#define punpckhwd_m2r(var, reg) mmx_m2r(punpckhwd, var, reg)
++#define punpckhwd_r2r(regs, regd) mmx_r2r(punpckhwd, regs, regd)
++
++#define punpcklbw_m2r(var, reg) mmx_m2r(punpcklbw, var, reg)
++#define punpcklbw_r2r(regs, regd) mmx_r2r(punpcklbw, regs, regd)
++#define punpckldq_m2r(var, reg) mmx_m2r(punpckldq, var, reg)
++#define punpckldq_r2r(regs, regd) mmx_r2r(punpckldq, regs, regd)
++#define punpcklwd_m2r(var, reg) mmx_m2r(punpcklwd, var, reg)
++#define punpcklwd_r2r(regs, regd) mmx_r2r(punpcklwd, regs, regd)
++
++#define pxor_m2r(var, reg) mmx_m2r(pxor, var, reg)
++#define pxor_r2r(regs, regd) mmx_r2r(pxor, regs, regd)
++
++
++/* 3DNOW extensions */
++
++#define pavgusb_m2r(var, reg) mmx_m2r(pavgusb, var, reg)
++#define pavgusb_r2r(regs, regd) mmx_r2r(pavgusb, regs, regd)
++
++
++/* AMD MMX extensions - also available in intel SSE */
++
++
++#define mmx_m2ri(op, mem, reg, imm) \
++ __asm__ __volatile__ (#op " %1, %0, %%" #reg \
++ : /* nothing */ \
++ : "m" (mem), "i" (imm))
++#define mmx_r2ri(op, regs, regd, imm) \
++ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
++ : /* nothing */ \
++ : "i" (imm))
++
++#define mmx_fetch(mem, hint) \
++ __asm__ __volatile__ ("prefetch" #hint " %0" \
++ : /* nothing */ \
++ : "m" (mem))
++
++
++#define maskmovq(regs, maskreg) mmx_r2ri(maskmovq, regs, maskreg)
++
++#define movntq_r2m(mmreg, var) mmx_r2m(movntq, mmreg, var)
++
++#define pavgb_m2r(var, reg) mmx_m2r(pavgb, var, reg)
++#define pavgb_r2r(regs, regd) mmx_r2r(pavgb, regs, regd)
++#define pavgw_m2r(var, reg) mmx_m2r(pavgw, var, reg)
++#define pavgw_r2r(regs, regd) mmx_r2r(pavgw, regs, regd)
++
++#define pextrw_r2r(mmreg, reg, imm) mmx_r2ri(pextrw, mmreg, reg, imm)
++
++#define pinsrw_r2r(reg, mmreg, imm) mmx_r2ri(pinsrw, reg, mmreg, imm)
++
++#define pmaxsw_m2r(var, reg) mmx_m2r(pmaxsw, var, reg)
++#define pmaxsw_r2r(regs, regd) mmx_r2r(pmaxsw, regs, regd)
++
++#define pmaxub_m2r(var, reg) mmx_m2r(pmaxub, var, reg)
++#define pmaxub_r2r(regs, regd) mmx_r2r(pmaxub, regs, regd)
++
++#define pminsw_m2r(var, reg) mmx_m2r(pminsw, var, reg)
++#define pminsw_r2r(regs, regd) mmx_r2r(pminsw, regs, regd)
++
++#define pminub_m2r(var, reg) mmx_m2r(pminub, var, reg)
++#define pminub_r2r(regs, regd) mmx_r2r(pminub, regs, regd)
++
++#define pmovmskb(mmreg, reg) \
++ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
++
++#define pmulhuw_m2r(var, reg) mmx_m2r(pmulhuw, var, reg)
++#define pmulhuw_r2r(regs, regd) mmx_r2r(pmulhuw, regs, regd)
++
++#define prefetcht0(mem) mmx_fetch(mem, t0)
++#define prefetcht1(mem) mmx_fetch(mem, t1)
++#define prefetcht2(mem) mmx_fetch(mem, t2)
++#define prefetchnta(mem) mmx_fetch(mem, nta)
++
++#define psadbw_m2r(var, reg) mmx_m2r(psadbw, var, reg)
++#define psadbw_r2r(regs, regd) mmx_r2r(psadbw, regs, regd)
++
++#define pshufw_m2r(var, reg, imm) mmx_m2ri(pshufw, var, reg, imm)
++#define pshufw_r2r(regs, regd, imm) mmx_r2ri(pshufw, regs, regd, imm)
++
++#define sfence() __asm__ __volatile__ ("sfence\n\t")
++
++/* SSE2 */
++#define pshufhw_m2r(var, reg, imm) mmx_m2ri(pshufhw, var, reg, imm)
++#define pshufhw_r2r(regs, regd, imm) mmx_r2ri(pshufhw, regs, regd, imm)
++#define pshuflw_m2r(var, reg, imm) mmx_m2ri(pshuflw, var, reg, imm)
++#define pshuflw_r2r(regs, regd, imm) mmx_r2ri(pshuflw, regs, regd, imm)
++
++#define pshufd_r2r(regs, regd, imm) mmx_r2ri(pshufd, regs, regd, imm)
++
++#define movdqa_m2r(var, reg) mmx_m2r(movdqa, var, reg)
++#define movdqa_r2m(reg, var) mmx_r2m(movdqa, reg, var)
++#define movdqa_r2r(regs, regd) mmx_r2r(movdqa, regs, regd)
++#define movdqu_m2r(var, reg) mmx_m2r(movdqu, var, reg)
++#define movdqu_r2m(reg, var) mmx_r2m(movdqu, reg, var)
++#define movdqu_r2r(regs, regd) mmx_r2r(movdqu, regs, regd)
++
++#define pmullw_r2m(reg, var) mmx_r2m(pmullw, reg, var)
++
++#define pslldq_i2r(imm, reg) mmx_i2r(pslldq, imm, reg)
++#define psrldq_i2r(imm, reg) mmx_i2r(psrldq, imm, reg)
++
++#define punpcklqdq_r2r(regs, regd) mmx_r2r(punpcklqdq, regs, regd)
++#define punpckhqdq_r2r(regs, regd) mmx_r2r(punpckhqdq, regs, regd)
++
++
++#endif /* AVCODEC_I386MMX_H */
+diff -urN dahdi-svn-orig/drivers/staging/echo/oslec.h dahdi-svn-new/drivers/staging/echo/oslec.h
+--- dahdi-svn-orig/drivers/staging/echo/oslec.h 1970-01-01 02:00:00.000000000 +0200
++++ dahdi-svn-new/drivers/staging/echo/oslec.h 2010-01-21 13:35:37.000000000 +0200
+@@ -0,0 +1,94 @@
++/*
++ * OSLEC - A line echo canceller. This code is being developed
++ * against and partially complies with G168. Using code from SpanDSP
++ *
++ * Written by Steve Underwood <steveu at coppice.org>
++ * and David Rowe <david_at_rowetel_dot_com>
++ *
++ * Copyright (C) 2001 Steve Underwood and 2007-2008 David Rowe
++ *
++ * 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 version 2, as
++ * published by the Free Software Foundation.
++ *
++ * 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.
++ *
++ */
++
++#ifndef __OSLEC_H
++#define __OSLEC_H
++
++/* Mask bits for the adaption mode */
++#define ECHO_CAN_USE_ADAPTION 0x01
++#define ECHO_CAN_USE_NLP 0x02
++#define ECHO_CAN_USE_CNG 0x04
++#define ECHO_CAN_USE_CLIP 0x08
++#define ECHO_CAN_USE_TX_HPF 0x10
++#define ECHO_CAN_USE_RX_HPF 0x20
++#define ECHO_CAN_DISABLE 0x40
++
++/**
++ * oslec_state: G.168 echo canceller descriptor.
++ *
++ * This defines the working state for a line echo canceller.
++ */
++struct oslec_state;
++
++/**
++ * oslec_create - Create a voice echo canceller context.
++ * @len: The length of the canceller, in samples.
++ * @return: The new canceller context, or NULL if the canceller could not be
++ * created.
++ */
++struct oslec_state *oslec_create(int len, int adaption_mode);
++
++/**
++ * oslec_free - Free a voice echo canceller context.
++ * @ec: The echo canceller context.
++ */
++void oslec_free(struct oslec_state *ec);
++
++/**
++ * oslec_flush - Flush (reinitialise) a voice echo canceller context.
++ * @ec: The echo canceller context.
++ */
++void oslec_flush(struct oslec_state *ec);
++
++/**
++ * oslec_adaption_mode - set the adaption mode of a voice echo canceller context.
++ * @ec The echo canceller context.
++ * @adaption_mode: The mode.
++ */
++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode);
++
++void oslec_snapshot(struct oslec_state *ec);
++
++/**
++ * oslec_update: Process a sample through a voice echo canceller.
++ * @ec: The echo canceller context.
++ * @tx: The transmitted audio sample.
++ * @rx: The received audio sample.
++ *
++ * The return value is the clean (echo cancelled) received sample.
++ */
++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx);
++
++/**
++ * oslec_hpf_tx: Process to high pass filter the tx signal.
++ * @ec: The echo canceller context.
++ * @tx: The transmitted auio sample.
++ *
++ * The return value is the HP filtered transmit sample, send this to your D/A.
++ */
++int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx);
++
++#endif /* __OSLEC_H */
Modified: dahdi-linux/trunk/debian/patches/series
URL: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/trunk/debian/patches/series?rev=7979&op=diff
==============================================================================
--- dahdi-linux/trunk/debian/patches/series (original)
+++ dahdi-linux/trunk/debian/patches/series Thu Jan 21 11:50:17 2010
@@ -1,12 +1,5 @@
-#oslec_zaptel
-#bri_dchan
-
-zaphfc
-
-# OSLEC:
-oslec_kernelorg
-
-oslec_kbuild
+# Mega-patch of extra drivers:
+dahdi_linux_extra
no_firmware_download
mmx_auto
uk_rotary
More information about the Pkg-voip-commits
mailing list